385 lines
10 KiB
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[] = "from: @(#)tables.c 5.17 (Berkeley) 6/1/90";*/
|
|
static char rcsid[] = "$Id: tables.c,v 1.6 1993/12/30 10:44:23 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;
|
|
}
|