NetBSD/sbin/routed/tables.c

385 lines
10 KiB
C

/*
* 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";
static char rcsid[] = "$Id: tables.c,v 1.2 1993/03/22 08:04:00 cgd Exp $";
#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);
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);
}
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;
}