summaryrefslogtreecommitdiff
path: root/usr.sbin/routed/tables.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/routed/tables.c')
-rw-r--r--usr.sbin/routed/tables.c410
1 files changed, 410 insertions, 0 deletions
diff --git a/usr.sbin/routed/tables.c b/usr.sbin/routed/tables.c
new file mode 100644
index 000000000000..17a4fb7b966e
--- /dev/null
+++ b/usr.sbin/routed/tables.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 1983, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tables.c 5.17 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/syslog.h>
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+#ifdef RTM_ADD
+#define FIXLEN(s) {if ((s)->sa_len == 0) (s)->sa_len = sizeof *(s);}
+#else
+#define FIXLEN(s) { }
+#endif
+
+int install = !DEBUG; /* if 1 call kernel */
+
+/*
+ * Lookup dst in the tables for an exact match.
+ */
+struct rt_entry *
+rtlookup(dst)
+ struct sockaddr *dst;
+{
+ register struct rt_entry *rt;
+ register struct rthash *rh;
+ register u_int hash;
+ struct afhash h;
+ int doinghost = 1;
+
+ if (dst->sa_family >= af_max)
+ return (0);
+ (*afswitch[dst->sa_family].af_hash)(dst, &h);
+ hash = h.afh_hosthash;
+ rh = &hosthash[hash & ROUTEHASHMASK];
+again:
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_hash != hash)
+ continue;
+ if (equal(&rt->rt_dst, dst))
+ return (rt);
+ }
+ if (doinghost) {
+ doinghost = 0;
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ goto again;
+ }
+ return (0);
+}
+
+struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
+
+/*
+ * Find a route to dst as the kernel would.
+ */
+struct rt_entry *
+rtfind(dst)
+ struct sockaddr *dst;
+{
+ register struct rt_entry *rt;
+ register struct rthash *rh;
+ register u_int hash;
+ struct afhash h;
+ int af = dst->sa_family;
+ int doinghost = 1, (*match)();
+
+ if (af >= af_max)
+ return (0);
+ (*afswitch[af].af_hash)(dst, &h);
+ hash = h.afh_hosthash;
+ rh = &hosthash[hash & ROUTEHASHMASK];
+
+again:
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_hash != hash)
+ continue;
+ if (doinghost) {
+ if (equal(&rt->rt_dst, dst))
+ return (rt);
+ } else {
+ if (rt->rt_dst.sa_family == af &&
+ (*match)(&rt->rt_dst, dst))
+ return (rt);
+ }
+ }
+ if (doinghost) {
+ doinghost = 0;
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ match = afswitch[af].af_netmatch;
+ goto again;
+ }
+#ifdef notyet
+ /*
+ * Check for wildcard gateway, by convention network 0.
+ */
+ if (dst != &wildcard) {
+ dst = &wildcard, hash = 0;
+ goto again;
+ }
+#endif
+ return (0);
+}
+
+rtadd(dst, gate, metric, state)
+ struct sockaddr *dst, *gate;
+ int metric, state;
+{
+ struct afhash h;
+ register struct rt_entry *rt;
+ struct rthash *rh;
+ int af = dst->sa_family, flags;
+ u_int hash;
+
+ if (af >= af_max)
+ return;
+ (*afswitch[af].af_hash)(dst, &h);
+ flags = (*afswitch[af].af_rtflags)(dst);
+ /*
+ * Subnet flag isn't visible to kernel, move to state. XXX
+ */
+ FIXLEN(dst);
+ FIXLEN(gate);
+ if (flags & RTF_SUBNET) {
+ state |= RTS_SUBNET;
+ flags &= ~RTF_SUBNET;
+ }
+ if (flags & RTF_HOST) {
+ hash = h.afh_hosthash;
+ rh = &hosthash[hash & ROUTEHASHMASK];
+ } else {
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ }
+ rt = (struct rt_entry *)malloc(sizeof (*rt));
+ if (rt == 0)
+ return;
+ rt->rt_hash = hash;
+ rt->rt_dst = *dst;
+ rt->rt_router = *gate;
+ rt->rt_timer = 0;
+ rt->rt_flags = RTF_UP | flags;
+ rt->rt_state = state | RTS_CHANGED;
+ rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst);
+ if (rt->rt_ifp == 0) {
+ rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+ /*
+ * seems like we can't figure out the interface for the
+ * IP address of the local side of a point to point
+ * connection, we just don't add that entry in the
+ * table. (it seems to already be there anyway)
+ */
+ if (rt->rt_ifp == 0) {
+ syslog(LOG_DEBUG,
+ "rtadd: can't get interface for %s",
+ (*afswitch[dst->sa_family].af_format)(dst));
+ return;
+ }
+ }
+ if ((state & RTS_INTERFACE) == 0)
+ rt->rt_flags |= RTF_GATEWAY;
+ rt->rt_metric = metric;
+ insque(rt, rh);
+ TRACE_ACTION("ADD", rt);
+ /*
+ * 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");
+ if (errno == ENETUNREACH) {
+ TRACE_ACTION("DELETE", rt);
+ remque(rt);
+ free((char *)rt);
+ }
+ }
+}
+
+rtchange(rt, gate, metric)
+ struct rt_entry *rt;
+ struct sockaddr *gate;
+ short metric;
+{
+ int add = 0, delete = 0, newgateway = 0;
+ struct rtentry oldroute;
+
+ FIXLEN(gate);
+ FIXLEN(&(rt->rt_router));
+ FIXLEN(&(rt->rt_dst));
+ if (!equal(&rt->rt_router, gate)) {
+ newgateway++;
+ TRACE_ACTION("CHANGE FROM ", rt);
+ } else if (metric != rt->rt_metric)
+ TRACE_NEWMETRIC(rt, 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 (newgateway) {
+ add++;
+ if (rt->rt_metric != HOPCNT_INFINITY)
+ delete++;
+ } else if (metric == HOPCNT_INFINITY)
+ delete++;
+ else if (rt->rt_metric == HOPCNT_INFINITY)
+ add++;
+ }
+ if (delete)
+ oldroute = rt->rt_rt;
+ if ((rt->rt_state & RTS_INTERFACE) && delete) {
+ rt->rt_state &= ~RTS_INTERFACE;
+ rt->rt_flags |= RTF_GATEWAY;
+ if (metric > rt->rt_metric && delete)
+ syslog(LOG_ERR, "%s route to interface %s (timed out)",
+ add? "changing" : "deleting",
+ rt->rt_ifp->int_name);
+ }
+ if (add) {
+ rt->rt_router = *gate;
+ rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
+ if (rt->rt_ifp == 0) {
+ rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+ /*
+ * seems like we can't figure out the interface for the
+ * IP address of the local side of a point to point
+ * connection, we just don't add that entry in the
+ * table. (it seems to already be there anyway)
+ */
+ if (rt->rt_ifp == 0) {
+ struct sockaddr *dst = &(rt->rt_dst);
+ syslog(LOG_DEBUG,
+ "rtchange: can't get interface for %s",
+ (*afswitch[dst->sa_family].af_format)(dst));
+ return;
+ }
+ }
+ }
+ rt->rt_metric = metric;
+ rt->rt_state |= RTS_CHANGED;
+ if (newgateway)
+ TRACE_ACTION("CHANGE TO ", rt);
+#ifndef RTM_ADD
+ 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");
+#else
+ if (delete && install)
+ if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
+ perror("SIOCDELRT");
+ if (add && install) {
+ if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
+ perror("SIOCADDRT");
+ }
+#endif
+}
+
+rtdelete(rt)
+ struct rt_entry *rt;
+{
+
+ TRACE_ACTION("DELETE", rt);
+ FIXLEN(&(rt->rt_router));
+ FIXLEN(&(rt->rt_dst));
+ if (rt->rt_metric < HOPCNT_INFINITY) {
+ 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);
+ if (install &&
+ (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
+ ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
+ perror("SIOCDELRT");
+ }
+ remque(rt);
+ free((char *)rt);
+}
+
+rtdeleteall(sig)
+ int sig;
+{
+ 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 ||
+ rt->rt_metric >= HOPCNT_INFINITY)
+ 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;
+ }
+ exit(sig);
+}
+
+/*
+ * 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, 1,
+ RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
+}
+
+rtinit()
+{
+ register struct rthash *rh;
+
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
+ rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
+ for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
+ rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
+}
+
+
+/* ffrom /sys/i386/i386/machdep.c */
+/*
+ * insert an element into a queue
+ */
+insque(element, head)
+ register struct rthash *element, *head;
+{
+ element->rt_forw = head->rt_forw;
+ head->rt_forw = (struct rt_entry *)element;
+ element->rt_back = (struct rt_entry *)head;
+ ((struct rthash *)(element->rt_forw))->rt_back=(struct rt_entry *)element;
+}
+
+/*
+ * remove an element from a queue
+ */
+remque(element)
+ register struct rthash *element;
+{
+ ((struct rthash *)(element->rt_forw))->rt_back = element->rt_back;
+ ((struct rthash *)(element->rt_back))->rt_forw = element->rt_forw;
+ element->rt_back = (struct rt_entry *)0;
+}