BSD 4_3_Net_2 release
[unix-history] / usr / src / sbin / XNSrouted / tables.c
/*
* Copyright (c) 1985 The 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.9 (Berkeley) 6/1/90";
#endif /* not lint */
/*
* Routing Table Management Daemon
*/
#include "defs.h"
#include <sys/ioctl.h>
#include <errno.h>
#ifndef DEBUG
#define DEBUG 0
#endif
extern char *xns_ntoa();
#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
int install = !DEBUG; /* if 1 call kernel */
int delete = 1;
/*
* 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;
u_int hash;
FIXLEN(dst);
FIXLEN(gate);
if (af >= AF_MAX)
return;
(*afswitch[af].af_hash)(dst, &h);
flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
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_metric = metric;
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_flags |= RTF_GATEWAY;
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 && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) {
if (errno != EEXIST)
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;
struct rtentry oldroute;
FIXLEN(gate);
if (!equal(&rt->rt_router, gate))
doioctl++;
if (metric != rt->rt_metric)
metricchanged++;
if (doioctl || metricchanged) {
TRACE_ACTION(CHANGE FROM, rt);
if (doioctl) {
oldroute = rt->rt_rt;
rt->rt_router = *gate;
}
rt->rt_metric = metric;
if ((rt->rt_state & RTS_INTERFACE) && metric) {
rt->rt_state &= ~RTS_INTERFACE;
syslog(LOG_ERR,
"changing route from interface %s (timed out)",
rt->rt_ifp->int_name);
}
if (metric)
rt->rt_flags |= RTF_GATEWAY;
else
rt->rt_flags &= ~RTF_GATEWAY;
rt->rt_state |= RTS_CHANGED;
TRACE_ACTION(CHANGE TO, rt);
}
if (doioctl && install) {
#ifndef RTM_ADD
if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m",
xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
perror("SIOCDELRT");
#else
if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
perror("SIOCDELRT");
if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m",
xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
#endif
}
}
rtdelete(rt)
struct rt_entry *rt;
{
struct sockaddr *sa = &(rt->rt_rt.rt_gateway);
FIXLEN(sa);
#undef rt_dst
sa = &(rt->rt_rt.rt_dst);
FIXLEN(sa);
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 && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
perror("SIOCDELRT");
remque(rt);
free((char *)rt);
}
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;
}