ba5706e086c2e694aa45ae56ac06eeea8e466b8f
[unix-history] / usr / src / sbin / routed / tables.c
/*
* 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
static char sccsid[] = "@(#)tables.c 5.9 (Berkeley) %G%";
#endif not lint
/*
* Routing Table Management Daemon
*/
#include "defs.h"
#include <sys/ioctl.h>
#include <errno.h>
#include <syslog.h>
#ifndef DEBUG
#define DEBUG 0
#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);
}
/*
* 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;
}
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, ifmetric;
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
*/
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_router);
if (rt->rt_ifp == 0)
rt->rt_ifp = if_ifwithnet(&rt->rt_router);
if (rt->rt_ifp)
ifmetric = rt->rt_ifp->int_metric;
else
ifmetric = 0;
if ((state & RTS_INTERFACE) == 0)
rt->rt_flags |= RTF_GATEWAY;
/*
* Set rt_ifmetric to the amount by which we
* increment the route when sending it to others.
*/
if (state & RTS_INTERFACE) {
rt->rt_metric = 0;
rt->rt_ifmetric = metric + 1;
} else {
rt->rt_metric = metric;
rt->rt_ifmetric = ifmetric + 1;
}
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 &&
dst->sa_family < af_max && 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 doioctl = 0, metricchanged = 0, delete = 0;
struct rtentry oldroute;
if (!equal(&rt->rt_router, gate) && (rt->rt_state & RTS_INTERNAL) == 0)
doioctl++;
if (metric != rt->rt_metric) {
metricchanged++;
if (metric == HOPCNT_INFINITY)
delete++;
}
if (doioctl || metricchanged) {
TRACE_ACTION(CHANGE FROM, rt);
if ((rt->rt_state & RTS_INTERFACE) &&
metric > rt->rt_ifp->int_metric) {
rt->rt_state &= ~RTS_INTERFACE;
rt->rt_flags |= RTF_GATEWAY;
syslog(LOG_ERR,
"changing route from interface %s (timed out)",
rt->rt_ifp->int_name);
}
if (doioctl || delete)
oldroute = rt->rt_rt;
if (doioctl) {
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);
}
rt->rt_metric = metric;
rt->rt_state |= RTS_CHANGED;
TRACE_ACTION(CHANGE TO, rt);
}
if (doioctl && install)
if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
perror("SIOCADDRT");
if ((doioctl || delete) && install)
if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
perror("SIOCDELRT");
}
rtdelete(rt)
struct rt_entry *rt;
{
if (rt->rt_state & RTS_INTERFACE)
syslog(LOG_ERR, "deleting route to interface %s (timed out)",
rt->rt_ifp->int_name);
TRACE_ACTION(DELETE, 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);
}
/*
* 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;
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;
}