1
0
Files
irix-657m-src/eoe/cmd/bsd/gated/rt_table.c
2022-09-29 17:59:04 +03:00

1124 lines
30 KiB
C

/*
* CENTER FOR THEORY AND SIMULATION IN SCIENCE AND ENGINEERING
* CORNELL UNIVERSITY
*
* Portions of this software may fall under the following
* copyrights:
*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* GATED - based on Kirton's EGP, UC Berkeley's routing daemon (routed),
* and DCN's HELLO routing Protocol.
*/
#ifndef lint
static char *rcsid = "$Header: /proj/irix6.5.7m/isms/eoe/cmd/bsd/gated/RCS/rt_table.c,v 1.6 1997/02/27 10:52:16 bitbug Exp $";
#endif not lint
/*
* Some modified from Routing Table Management Daemon routed/tables.c.
*
* Functions: rt_lookup, rt_add, rt_change, rt_delete,
* rt_default, rt_time, rt_gwunreach, rt_redirect,
* rt_check_default, rt_find, addrouteforif,
* init_hwin, add_win
*/
#include "include.h"
#include <sys/capability.h>
#ifdef sgi
/*
* Return the netmask pertaining to an internet address.
*/
u_long
inet_maskof(u_long inaddr)
{
register u_long i = ntohl(inaddr);
register u_long mask;
register struct interface *ifp;
if (i == 0) {
mask = 0;
} else if (IN_CLASSA(i)) {
mask = IN_CLASSA_NET;
} else if (IN_CLASSB(i)) {
mask = IN_CLASSB_NET;
} else
mask = IN_CLASSC_NET;
/*
* Check whether network is a subnet;
* if so, use the modified interpretation of `host'.
*/
for (ifp = ifnet; ifp; ifp = ifp->int_next)
if ((ifp->int_netmask & i) == ifp->int_net)
mask = ifp->int_subnetmask;
return (htonl(mask));
}
#define ADD 1
#define DELETE 2
#define CHANGE 3
int
rtioctl(int action,
struct rt_entry *rt)
{
static int seqno;
static int r = -1;
struct {
struct rt_msghdr w_rtm;
struct sockaddr_in w_dst;
struct sockaddr w_gate;
#ifdef _HAVE_SA_LEN
struct sockaddr_in w_netmask;
#else
struct sockaddr_in_new w_netmask;
#endif
} w;
cap_t ocap;
cap_value_t cap_priv_port = CAP_PRIV_PORT;
if (r < 0) {
ocap = cap_acquire(1, &cap_priv_port);
r = socket(AF_ROUTE, SOCK_RAW, 0);
cap_surrender(ocap);
if (r < 0) {
int serrno = errno;
syslog(LOG_ERR, "routing socket: %m");
errno = serrno;
return -1;
}
shutdown(r, 0); /* for now, don't want reponses */
}
bzero(&w, sizeof(w));
w.w_rtm.rtm_msglen = sizeof(w);
w.w_rtm.rtm_version = RTM_VERSION;
w.w_rtm.rtm_type = (action == ADD ? RTM_ADD :
(action == DELETE ? RTM_DELETE : RTM_CHANGE));
w.w_rtm.rtm_flags = rt->rt_flags & IFF_MASK;
w.w_rtm.rtm_seq = ++seqno;
w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
bcopy(&rt->rt_dst, &w.w_dst, sizeof(w.w_dst));
bcopy(&rt->rt_router, &w.w_gate, sizeof(w.w_gate));
w.w_dst.sin_family = AF_INET;
#ifdef _HAVE_SA_LEN
w.w_dst.sin_len = sizeof(w.w_dst);
#endif
w.w_gate.sa_family = AF_INET;
#ifdef _HAVE_SA_LEN
w.w_gate.sa_len = sizeof(w.w_gate);
#endif
if (w.w_rtm.rtm_flags & RTF_HOST) {
w.w_rtm.rtm_msglen -= sizeof(w.w_netmask);
} else {
#ifdef _HAVE_SA_LEN
register char *cp;
int len;
#endif
w.w_rtm.rtm_addrs |= RTA_NETMASK;
w.w_netmask.sin_addr.s_addr =
inet_maskof(w.w_dst.sin_addr.s_addr);
#ifdef _HAVE_SA_LEN
for (cp = (char *)(1 + &w.w_netmask.sin_addr);
--cp > (char *) &w.w_netmask; )
if (*cp)
break;
len = cp - (char *)&w.w_netmask;
if (len) {
len++;
w.w_netmask.sin_len = len;
len = 1 + ((len - 1) | (sizeof(long) - 1));
} else {
len = sizeof(long);
}
w.w_rtm.rtm_msglen -= (sizeof(w.w_netmask) - len);
#endif
}
errno = 0;
return (write(r, &w, w.w_rtm.rtm_msglen));
}
#endif /* sgi */
/*
* rt_lookup() looks up a destination network for an exact match.
*/
struct rt_entry *
rt_lookup(table, dst)
int table;
struct sockaddr_in *dst;
{
register struct rt_entry *rt;
register struct rthash *rh;
register unsigned hash;
struct afhash h;
if (dst->sin_family != AF_INET)
return(0);
(*afswitch[dst->sin_family].af_hash)(dst, &h);
if (table == HOSTTABLE) {
hash = h.afh_hosthash;
rh = &hosthash[hash & ROUTEHASHMASK];
} else {
hash = h.afh_nethash;
rh = &nethash[hash & ROUTEHASHMASK];
}
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) && (rt->rt_state & table))
return (rt);
}
return (0);
}
/*
* rt_add() adds a route to either the interior or exterior routing tables.
*/
struct rt_entry*
rt_add(table, dst, gate, metric, state, proto, fromproto, as, metric_exterior)
int table;
struct sockaddr *dst, *gate;
int metric, state, proto, fromproto;
u_short as;
u_int metric_exterior;
{
register struct rt_entry *rt;
struct rthash *rh;
unsigned hash;
int af = AF_INET;
struct afhash h;
/*
* take care of routes pointing back to the localhost interface.
* We don't care about them and will ignore them in the kernel forever.
*/
if ((unsigned)gd_inet_netof(sock_inaddr(gate)) == (unsigned)LOOPBACKNET) {
return((struct rt_entry *) 1); /* Need to fix this! */
}
(*afswitch[af].af_hash)((struct sockaddr_in *)dst, &h);
if (table == HOSTTABLE) {
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) {
syslog(LOG_ERR, "rt_add: malloc: out of memory\n");
return(rt);
}
rt->rt_hash = hash;
rt->rt_dst = *dst;
/*
* set local part of dest addr zero for nets
*/
if ((gd_inet_lnaof(in_addr_ofs(&rt->rt_dst)) != 0) && (table != HOSTTABLE)) {
in_addr_ofs(&rt->rt_dst) = gd_inet_makeaddr(gd_inet_netof(in_addr_ofs(&rt->rt_dst)),0,TRUE);
}
rt->rt_router = *gate;
rt->rt_metric = metric;
rt->rt_metric_exterior = metric_exterior;
rt->rt_timer = 0;
rt->rt_flags = RTF_UP;
if (table == HOSTTABLE) {
rt->rt_flags |= RTF_HOST;
}
rt->rt_state = state | RTS_CHANGED | table;
if ((htonl(gd_inet_wholenetof(sock_inaddr(&rt->rt_dst))) !=
sock_inaddr(&rt->rt_dst).s_addr) && ((rt->rt_flags & RTF_HOST) == 0)) {
rt->rt_state |= RTS_SUBNET;
}
rt->rt_proto = proto;
rt->rt_fromproto = fromproto;
if (as) {
rt->rt_as = as;
} else {
rt->rt_as = mysystem;
}
rt->rt_announcelist = (struct restrictlist *)NULL;
rt->rt_noannouncelist = (struct restrictlist *)NULL;
rt->rt_listenlist = (struct restrictlist *)NULL;
rt->rt_srclisten = (struct restrictlist *)NULL;
rt->rt_ifp = if_withnet((struct sockaddr_in *)&rt->rt_router);
#ifndef NSS
if (rt->rt_ifp == (struct interface *)NULL) {
#else NSS
if ((rt->rt_ifp == (struct interface *)NULL) && (rt->rt_proto != RTPROTO_IGP)) {
#endif NSS
char badgate[16];
(void) strcpy(badgate, inet_ntoa(((struct sockaddr_in *)&rt->rt_router)->sin_addr));
TRACE_TRC("rt_add: interface not found for net %-15s gateway %s\n", inet_ntoa(sock_inaddr(&rt->rt_dst)), badgate);
free((char *)rt);
return((struct rt_entry *) 0);
}
#ifndef NSS
if (rt->rt_proto == RTPROTO_HELLO) {
/* initialize hello history window. */
init_hwindow(&rt->rt_hwindow, metric);
}
if (donotlisten != 0) {
if ((rt->rt_listenlist = control_lookup(RT_NOLISTEN, (struct sockaddr_in *)dst)) <= (struct restrictlist *)0) {
rt->rt_listenlist = (struct restrictlist *)NULL;
} else {
/* see if there is a restriction before adding */
if ( (rt->rt_proto != RTPROTO_EGP) && !(rt->rt_state & (RTS_PASSIVE|RTS_INTERFACE|RTS_STATIC)) ) {
/* no restrictions in EGP */
if (is_valid_in(rt, rt->rt_proto, rt->rt_ifp, (struct sockaddr_in *)gate) == 0) {
free((char *) rt);
return((struct rt_entry *) 0);
}
}
}
}
if (islisten != 0) {
if ((rt->rt_srclisten = control_lookup(RT_SRCLISTEN, (struct sockaddr_in *)dst)) <= (struct restrictlist *)0) {
rt->rt_srclisten = (struct restrictlist *)NULL;
} else {
/* see if there is a restriction before adding */
/*
* No restrictions in EGP. Also, don't prevent
* rt_default(ADD) from adding default route.
*/
if ( (rt->rt_proto != RTPROTO_EGP) && (rt->rt_proto != RTPROTO_DEFAULT)
&& !(rt->rt_state & (RTS_PASSIVE|RTS_INTERFACE|RTS_STATIC)) ) {
if (is_valid_in(rt, rt->rt_proto, rt->rt_ifp, (struct sockaddr_in *)gate) == 0) {
free((char *) rt);
return((struct rt_entry *) 0);
}
}
}
}
#endif NSS
if ((glob_announcethesenets != 0) || (announcethesenets != 0)) {
if ((rt->rt_announcelist = control_lookup(RT_ANNOUNCE, (struct sockaddr_in *)dst)) <= (struct restrictlist *)0) {
rt->rt_announcelist = (struct restrictlist *)NULL;
#ifdef notdef
if ((table == INTERIOR) || (table == HOSTTABLE)) {
if (glob_announcethesenets != 0) {
syslog(LOG_NOTICE,"invalid %s route %s not announced",
(table == INTERIOR) ? "net" : "host", inet_ntoa(sock_inaddr(&rt->rt_dst)));
} else {
syslog(LOG_NOTICE, "no announcelist for %s route %s, may be restricted ",
(table == INTERIOR) ? "net" : "host", inet_ntoa(sock_inaddr(&rt->rt_dst)));
}
}
#endif notdef
}
}
if ((glob_donotannounce != 0) || (donotannounce != 0)) {
if ((rt->rt_noannouncelist = control_lookup(RT_NOANNOUNCE, (struct sockaddr_in *)dst)) <= (struct restrictlist *)0) {
rt->rt_noannouncelist = (struct restrictlist *)NULL;
} else {
#ifdef notdef
if ((table == INTERIOR) || (table == HOSTTABLE)) {
if (glob_announcethesenets != 0) {
syslog(LOG_NOTICE,"invalid %s route %s not announced",
(table == INTERIOR) ? "net" : "host", inet_ntoa(sock_inaddr(&rt->rt_dst)));
} else {
syslog(LOG_NOTICE,"noannouncelist for %s route %s",
(table == INTERIOR) ? "net" : "host", inet_ntoa(sock_inaddr(&rt->rt_dst)));
}
}
#endif notdef
}
}
if (table == EXTERIOR) {
rt->rt_flags |= RTF_GATEWAY;
TRACE_ACTION(ADD, rt)
} else {
switch (rt->rt_proto) {
case RTPROTO_DIRECT:
break;
#ifndef NSS
case RTPROTO_RIP:
if (metric) {
rt->rt_flags |= RTF_GATEWAY;
}
break;
#endif NSS
default:
rt->rt_flags |= RTF_GATEWAY;
break;
}
TRACE_ACTION(ADD, rt);
}
insque(rt, rh);
#ifndef NSS
/*
* 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/gated.conf.
*/
if (install && !(rt->rt_state & RTS_NOTINSTALL)) {
#ifdef sgi
if (rtioctl(ADD, rt) < 0) {
#else /* sgi */
if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) {
#endif /* sgi */
char badgate[16];
(void) strcpy(badgate, inet_ntoa(((struct sockaddr_in *)&rt->rt_router)->sin_addr));
(void) sprintf(err_message, "rt_add: SIOCADDRT dst %s gw %s flags %x",
inet_ntoa(((struct sockaddr_in *)&rt->rt_dst)->sin_addr),
badgate, rt->rt_flags);
p_error(err_message);
if (errno == ENETUNREACH) {
TRACE_ACTION(DELETE, rt);
remque(rt);
free((char *)rt);
}
}
}
#endif NSS
n_routes++;
#ifdef NSS
rttable_changed++;
#endif NSS
return(rt);
}
/*
* rt_change() changes a route &/or notes that an update was received.
* returns 1 if change made
*/
rt_change(rt, gate, metric, proto, fromproto, as, metric_exterior)
struct rt_entry *rt;
struct sockaddr *gate;
short metric;
int proto, fromproto;
u_short as;
u_int metric_exterior;
{
int doioctl = 0, metricchanged = 0, protochanged = 0;
int ifchanged = 0, fromprotochanged = 0, aschanged = 0;
struct interface *t_ifp;
if (rt->rt_ifp != (t_ifp = if_withnet((struct sockaddr_in *)gate))) {
#ifndef NSS
if (rt->rt_ifp == (struct interface *)NULL) {
#else NSS
if ((rt->rt_ifp == (struct interface *)NULL) && (rt->rt_proto != RTPROTO_IGP)) {
#endif NSS
char badgate[16];
(void) strcpy(badgate, inet_ntoa(((struct sockaddr_in *)&rt->rt_router)->sin_addr));
#ifdef sgi
TRACE_TRC("rt_change: interface not found for net %-15s gateway %s\n", inet_ntoa(sock_inaddr(&rt->rt_dst)), badgate);
#else
TRACE_TRC("rt_add: interface not found for net %-15s gateway %s\n", inet_ntoa(sock_inaddr(&rt->rt_dst)), badgate);
#endif
return(0);
}
rt->rt_ifp = t_ifp;
ifchanged++;
}
rt->rt_state |= RTS_CHANGED; /* ensures route age reset */
if (!equal(&rt->rt_router, gate)) {
doioctl++;
}
if (metric != rt->rt_metric) {
metricchanged++;
rt->rt_metric = metric;
}
if (metric_exterior != rt->rt_metric_exterior) {
metricchanged++;
rt->rt_metric_exterior = metric_exterior;
}
if (fromproto != rt->rt_fromproto) {
fromprotochanged++;
rt->rt_fromproto = fromproto;
}
if (as == 0) {
as = mysystem;
}
if (as != rt->rt_as) {
aschanged++;
rt->rt_as = as;
}
if (proto != rt->rt_proto) {
protochanged++;
rt->rt_proto = proto;
#ifndef NSS
if (proto & RTPROTO_HELLO) {
init_hwindow(&rt->rt_hwindow, rt->rt_metric);
}
#endif NSS
}
if (doioctl) {
#ifdef sgi
rt->rt_router = *gate;
if (install && !(rt->rt_state & RTS_NOTINSTALL)) {
if (rtioctl(CHANGE, rt) < 0
&& (errno != ESRCH || rtioctl(ADD, rt) < 0)) {
char badgate[16];
(void)strcpy(badgate, inet_ntoa(((struct sockaddr_in *
)&rt->rt_router)->sin_addr));
(void)sprintf(err_message, "rt_change: dst %s gw %s flags %x",
inet_ntoa(((struct sockaddr_in*)&rt->rt_dst)->sin_addr),
badgate, rt->rt_flags);
p_error(err_message);
}
}
#else /* sgi */
struct rt_entry oldroute;
oldroute = *rt;
rt->rt_router = *gate;
#ifndef NSS
if (install && !(rt->rt_state & RTS_NOTINSTALL)) {
if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) {
char badgate[16];
(void) strcpy(badgate, inet_ntoa(((struct sockaddr_in *)&rt->rt_router)->sin_addr));
(void) sprintf(err_message, "rt_change: SIOCADDRT dst %s gw %s flags %x",
inet_ntoa(((struct sockaddr_in *)&rt->rt_dst)->sin_addr),
badgate, rt->rt_flags);
p_error(err_message);
}
if (ioctl(s, SIOCDELRT, (char *)&oldroute.rt_rt) < 0) {
char badgate[16];
(void) strcpy(badgate, inet_ntoa(((struct sockaddr_in *)&oldroute.rt_router)->sin_addr));
(void) sprintf(err_message, "rt_change: SIOCDELRT dst %s gw %s flags %x",
inet_ntoa(((struct sockaddr_in *)&oldroute.rt_dst)->sin_addr),
badgate, oldroute.rt_flags);
p_error(err_message);
}
}
#endif NSS
#endif /* sgi */
}
if (doioctl || metricchanged || protochanged || ifchanged || fromprotochanged || aschanged) {
TRACE_ACTION(CHANGE, rt);
return(1);
#ifdef NSS
rttable_changed++;
#endif NSS
}
if (doioctl || metricchanged || protochanged || ifchanged || fromprotochanged || aschanged) {
TRACE_ACTION(CHANGE, rt);
return(1);
#ifdef NSS
rttable_changed++;
#endif NSS
}
return(0);
}
/*
* rt_delete deletes a route from the routing table.
*/
rt_delete(rt, just_kernel)
struct rt_entry *rt;
int just_kernel;
{
#ifdef NSS
struct rt_entry *rt_igp;
#endif NSS
TRACE_ACTION(DELETE, rt);
#ifndef NSS
if (install && !(rt->rt_state & RTS_NOTINSTALL)) {
#ifdef sgi
if (rtioctl(DELETE, rt) < 0) {
#else /* sgi */
if (ioctl(s, SIOCDELRT, (char *)&rt->rt_rt) < 0) {
#endif /* sgi */
char badgate[16];
(void) strcpy(badgate, inet_ntoa(((struct sockaddr_in *)&rt->rt_router)->sin_addr));
(void) sprintf(err_message,"rt_delete: SIOCDELRT dst %s gw %s flags %x",
inet_ntoa(((struct sockaddr_in *)&rt->rt_dst)->sin_addr),
badgate, rt->rt_flags);
p_error(err_message);
}
}
#else NSS
if (rt->rt_proto == RTPROTO_EGP) {
es_rtdel((struct sockaddr_in *)&rt->rt_dst);
psp_egp_rtdel((struct sockaddr_in *)&rt->rt_dst, (struct sockaddr_in *)&rt->rt_router);
rt_igp = rt_locate((int) EXTERIOR, (struct sockaddr_in *) &rt->rt_dst, RTPROTO_IGP);
if (rt_igp != NULL) {
es_rtadd((struct sockaddr_in *) &rt_igp->rt_dst, rt_igp->rt_as);
}
}
#endif NSS
if (!(just_kernel)) {
remque(rt);
free((char *)rt);
n_routes--;
#ifdef NSS
rttable_changed++;
#endif NSS
}
}
/*
* rt_default() adds or deletes default route in kernel.
* also adds a default in it's own tables only for the RIP/HELLO
* gateway.
*/
rt_default(cmd)
char *cmd;
{
struct rt_entry *rt;
struct sockaddr_in defaultdst;
int keepinstall, changed = FALSE;
if (!rip_gateway && !hello_gateway) {
return(changed);
}
bzero((char *)&defaultdst, sizeof(defaultdst));
defaultdst.sin_family = AF_INET;
defaultdst.sin_addr.s_addr = DEFAULTNET;
if (strcasecmp( cmd, "ADD") == 0) {
if (rt_default_active == FALSE) {
keepinstall = install;
install = TRUE;
rt = rt_lookup((int)EXTERIOR, &defaultdst);
if (rt) {
rt_delete(rt, KERNEL_INTR); /* delete old default route */
}
rt = rt_lookup((int)INTERIOR, &defaultdst);
if (rt) {
rt_delete(rt, KERNEL_INTR); /* delete old default route */
}
/*
* this is only a false route, so just pick the first interface address
* in the ifnet list to satisfy the gateway.
*/
install = FALSE;
(void) rt_add((int)INTERIOR, (struct sockaddr *)&defaultdst,
&ifnet->int_addr, 0, RTS_PASSIVE|RTS_NOTINSTALL|RTS_STATIC, RTPROTO_DEFAULT, RTPROTO_DEFAULT, 0, 0);
install = keepinstall;
rt_default_active = TRUE;
changed = TRUE;
}
} else {
if (rt_default_active == TRUE) {
rt = rt_lookup((int)INTERIOR, &defaultdst);
if (rt != NULL) {
rt_delete(rt, KERNEL_INTR);
rt_default_active = FALSE;
} else {
syslog(LOG_NOTICE, "rt_default: no active default route\n");
}
if (default_gateway) {
(void) rt_add(default_gateway->rt_state & RTS_INTERIOR ? (int)INTERIOR : (int)EXTERIOR,
&default_gateway->rt_dst, &default_gateway->rt_router,
default_gateway->rt_metric, default_gateway->rt_state,
default_gateway->rt_proto, default_gateway->rt_fromproto,
default_gateway->rt_as, default_gateway->rt_metric_exterior);
}
changed = TRUE;
}
}
return (changed);
}
/*
* rt_time() increments the age of all routes in the routing table
* If any EGP routes are older than rt_maxage (set in egpstime()) the routes are
* deleted. If RIP/HELLO routes are greater that GARBAGE_TIME, delete them.
*/
rt_time()
{
struct rthash *rh;
struct rthash *base;
struct rt_entry *rt;
int old_routes = 0, hold_routes = 0;
int doinghost;
#ifndef NSS
int timetobroadcast, hellotimetobroad;
int do_flash_update = FALSE;
int do_helloflash_update = FALSE;
char *s;
struct bits *p;
timetobroadcast = rip_supplier && ((gatedtime % RIP_INTERVAL) == 0);
hellotimetobroad = hello_supplier && ((gatedtime % HELLO_TIMERRATE) == 0);
#else NSS
int igp_insane_rt = 0;
#endif NSS
for (doinghost = 1, base = hosthash; doinghost >= 0; doinghost--, base=nethash) {
for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
if (rt->rt_state & RTS_CHANGED) { /* recently updated */
rt->rt_state &= ~RTS_CHANGED;
rt->rt_timer = 0;
if (((rt->rt_state & RTS_PASSIVE) == 0) &&
(rt->rt_state & RTS_INTERIOR)) {
#ifndef NSS
if (doing_rip) {
if (rip_supplier && !timetobroadcast)
do_flash_update = TRUE;
}
if (doing_hello) {
if (hello_supplier && !hellotimetobroad)
do_helloflash_update = TRUE;
}
#endif NSS
}
} else {
if (!(rt->rt_state & RTS_PASSIVE)) {
rt->rt_timer += gatedtime - last_time;
}
}
/*
* is route too old?
*/
if (rt->rt_state & RTS_EXTERIOR) {
/*
* Exterior Routes
*/
switch (rt->rt_proto) {
#ifdef NSS
case RTPROTO_IGP:
igp_insane_rt += igp_sanity_chk(rt);
break;
#endif NSS
case RTPROTO_EGP:
case RTPROTO_KERNEL:
case RTPROTO_DEFAULT:
case RTPROTO_REDIRECT:
switch (rt_time_exterior(rt)) {
case 1:
old_routes++;
break;
case 2:
hold_routes++;
break;
}
break;
default:
if (sock_inaddr(&rt->rt_dst).s_addr != DEFAULTNET) {
for (p = protobits; p->t_bits; p++) {
if ((rt->rt_proto & p->t_bits) == 0)
continue;
s = p->t_name;
}
TRACE_TRC("rt_time: Invalid exterior route to net %s via %s at %s",
inet_ntoa(sock_inaddr(&rt->rt_dst)), s, strtime);
}
break;
}
} else {
/*
* Interior Routes
*/
switch (rt->rt_proto) {
#ifndef NSS
case RTPROTO_RIP:
case RTPROTO_HELLO:
#endif NSS
case RTPROTO_DIRECT:
case RTPROTO_KERNEL:
case RTPROTO_REDIRECT:
case RTPROTO_DEFAULT:
switch (rt_time_interior(rt)) {
case 1:
old_routes++;
break;
case 2:
hold_routes++;
break;
}
break;
default:
if (sock_inaddr(&rt->rt_dst).s_addr != DEFAULTNET) {
for (p = protobits; p->t_bits; p++) {
if ((rt->rt_proto & p->t_bits) == 0)
continue;
s = p->t_name;
}
TRACE_TRC("rt_time: Invalid interior route to net %s via protocol %s at %s",
inet_ntoa(sock_inaddr(&rt->rt_dst)), s, strtime);
}
break;
}
}
}
}
}
#ifndef NSS
if (do_flash_update) {
/* there was one or more changes, update RIP */
toall(supply);
}
if (do_helloflash_update) {
/* there was one or more changes, update HELLO */
hellojob();
}
#else NSS
if (old_routes) {
egp2esrec();
}
if (igp_insane_rt > 0) {
TRACE_RT("rt_time: above %d IGP routes are insane %s\n", igp_insane_rt, strtime);
}
#endif NSS
if (old_routes+hold_routes) {
(void) rt_check_default();
TRACE_RT("rt_time: above %d routes deleted and %d routes helddown %s\n", old_routes, hold_routes, strtime);
}
last_time = gatedtime;
return;
}
/*
* rt_time_exterior() checks the age, expires and deletes exterior
* routes.
*/
rt_time_exterior(rt)
struct rt_entry *rt;
{
struct rt_entry *old_rt;
int old_routes = 0;
if ( (rt->rt_timer >= rt_maxage - HOLD_DOWN) && (rt->rt_metric < TDHOPCNT_INFINITY) ) {
old_routes = rt_unreach(rt)*2;
}
if (rt->rt_timer >= rt_maxage) {
old_rt = rt;
rt = rt->rt_back;
rt_delete(old_rt, KERNEL_INTR);
old_routes = 1;
}
return(old_routes);
}
/*
* rt_time_interior() checks the age, expires and deletes interior
* routes.
*/
rt_time_interior(rt)
struct rt_entry *rt;
{
struct rt_entry *old_rt;
u_long old_sin = sock_inaddr(&rt->rt_dst).s_addr;
int old_routes = 0;
if ( (rt->rt_timer >= EXPIRE_TIME) && (rt->rt_metric < DELAY_INFINITY) ) {
old_routes = rt_unreach(rt)*2;
#ifndef NSS
if (rt->rt_proto == RTPROTO_HELLO) {
add_win(&rt->rt_hwindow, rt->rt_metric);
}
#endif NSS
}
if (rt->rt_timer >= FINAL_DELETE) {
if (rt->rt_state & (RTS_INTERFACE|RTS_REMOTE)) {
TRACE_INT("interface timeout - deleting route to %s\n",
inet_ntoa(sock_inaddr(&rt->rt_dst)));
syslog(LOG_ERR, "interface timeout - deleting route to %s\n",
inet_ntoa(sock_inaddr(&rt->rt_dst)));
}
old_rt = rt;
rt = rt->rt_back;
rt_delete(old_rt, KERNEL_INTR);
if ((old_sin == DEFAULTNET) && (default_gateway)) {
(void) rt_add(default_gateway->rt_state & RTS_INTERIOR ? (int)INTERIOR : (int)EXTERIOR,
&default_gateway->rt_dst, &default_gateway->rt_router,
default_gateway->rt_metric, default_gateway->rt_state,
default_gateway->rt_proto, default_gateway->rt_fromproto,
default_gateway->rt_as, default_gateway->rt_metric_exterior);
}
old_routes = 1;
}
return(old_routes);
}
/*
*
* rt_unreach() does processing on a route that has been
* indicated as unreachable in a routing update. The metric
* is set to infinity and the timer is set so the route will expire
* within HOLD_DOWN seconds. A fudge factor is subtracted from the
* time to make sure that the hold down lasts at least HOLD_DOWN.
*/
int rt_unreach(rt)
struct rt_entry *rt;
{
#ifdef NO_HOLD_DOWN
rt->rt_timer = rt_maxage;
return(0);
#else NO_HOLD_DOWN
TRACE_ACTION(HOLDDOWN, rt);
if ( rt->rt_state & (RTS_INTERIOR|RTS_HOSTROUTE) ) {
rt->rt_timer = EXPIRE_TIME;
rt->rt_metric = DELAY_INFINITY;
} else {
rt->rt_metric = TDHOPCNT_INFINITY;
rt->rt_timer = rt_maxage - HOLD_DOWN;
}
rt->rt_state &= ~RTS_CHANGED;
rt->rt_timer -= gatedtime - last_time;
return(1);
#endif NO_HOLD_DOWN
}
/*
* rt_check_default() checks whether there are currently any EGP neighbors
* that are both acquired and reachable and if not, stops the announcement
* of default, if the gateway is a rip_gateway or hello_gateway.
*/
rt_check_default()
{
int neigh_up = FALSE;
struct egpngh *ngp;
static int save_neigh_up = -1;
if (rt_default_active == FALSE) {
return(0);
}
if ( !doing_egp || (!(rip_gateway) && !(hello_gateway)) ) {
return(0);
}
for (ngp = egpngh; ngp != NULL; ngp = ngp->ng_next) {
if ((ngp->ng_state == NGS_UP) && !(ngp->ng_reach & NG_DOWN) && !(ngp->ng_flags & NG_NOGENDEFAULT)) {
neigh_up = TRUE;
break;
}
}
if (neigh_up != save_neigh_up) {
if (neigh_up == FALSE) {
(void) rt_default("DELETE");
}
save_neigh_up = neigh_up;
return(1);
} else {
return(0);
}
}
/*
* rt_gwunreach() deletes all exterior routes from the routing table for a
* specified gateway
*/
rt_gwunreach(gateway)
struct in_addr gateway; /* internet address of gateway */
{
int changes = 0;
struct rthash *rh;
struct rt_entry *rt, *unreach_rt;
for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) {
for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
if ((sock_inaddr(&rt->rt_router).s_addr == gateway.s_addr) &&
!(rt->rt_state & RTS_PASSIVE) && (rt->rt_state & RTS_EXTERIOR)) {
unreach_rt = rt;
rt = rt->rt_back;
TRACE_RT("GW UNREACH: ");
rt_delete(unreach_rt, KERNEL_INTR);
changes++;
}
}
}
return(changes);
}
/*
* rt_find() looks up a destination as the kernel would
*/
struct rt_entry *
rt_find(dst)
struct sockaddr_in *dst;
{
register struct rt_entry *rt;
register struct rthash *rh;
register unsigned hash;
struct afhash h;
int af = AF_INET;
int doinghost = 1;
int (*match)();
(*afswitch[af].af_hash)((struct sockaddr_in *)dst, &h);
hash = h.afh_hosthash;
rh = &hosthash[hash & ROUTEHASHMASK];
match = afswitch[af].af_netmatch;
nexttable:
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 ((*match)(&rt->rt_dst, dst))
return(rt);
}
}
if (doinghost) {
doinghost = 0;
hash = h.afh_nethash;
rh = &nethash[hash & ROUTEHASHMASK];
goto nexttable;
}
return (0);
}
/*
* Looks up a destination network route with a specific protocol mask.
* Specifying a protocol of zero will match all protocols.
*/
struct rt_entry *rt_locate(table, dst, proto)
int table;
struct sockaddr_in *dst;
int proto;
{
struct rt_entry *rt;
struct rthash *rh;
unsigned hash;
struct afhash h;
if (dst->sin_family != AF_INET) {
return(NULL);
}
(*afswitch[dst->sin_family].af_hash)(dst, &h);
if (table == HOSTTABLE) {
hash = h.afh_hosthash;
rh = &hosthash[hash & ROUTEHASHMASK];
} else {
hash = h.afh_nethash;
rh = &nethash[hash & ROUTEHASHMASK];
}
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) && (rt->rt_state & table) && (!proto || (rt->rt_proto & proto)) ) {
return (rt);
}
}
return (NULL);
}
#ifndef NSS
/*
* initialize the sliding HELLO history window.
*/
init_hwindow(hwp, tdelay)
struct hello_win *hwp;
int tdelay;
{
int msf = 0;
while (msf < HWINSIZE)
hwp->h_win[msf++] = DELAY_INFINITY;
hwp->h_index = 0;
hwp->h_min = tdelay;
hwp->h_min_ttl = 0;
hwp->h_win[0] = tdelay;
}
/*
* add a HELLO derived time delay to the route entries HELLO window.
*/
add_win(hwp, tdelay)
struct hello_win *hwp;
int tdelay;
{
int msf, t_index = 0;
hwp->h_index++;
if (hwp->h_index >= HWINSIZE)
hwp->h_index = 0;
hwp->h_win[hwp->h_index] = tdelay;
if (tdelay > hwp->h_min)
hwp->h_min_ttl++;
else {
hwp->h_min = tdelay;
hwp->h_min_ttl = 0;
}
if (hwp->h_min_ttl >= HWINSIZE) {
hwp->h_min = DELAY_INFINITY;
for (msf = 0; msf < HWINSIZE; msf++)
if (hwp->h_win[msf] <= hwp->h_min) {
hwp->h_min = hwp->h_win[msf];
t_index = msf;
}
hwp->h_min_ttl = 0;
if (t_index < hwp->h_index)
hwp->h_min_ttl = hwp->h_index - t_index;
else
if (t_index > hwp->h_index)
hwp->h_min_ttl = HWINSIZE - (t_index - hwp->h_index);
}
}
/*
* map HELLO time delay to RIP metric OR
* RIP metric to HELLO time delay (in miliseconds)
*/
mapmetric(proto, cost)
int proto;
u_short cost;
{
static unsigned short metric_delay[RIPHOPCNT_INFINITY+1] = HOP_TO_DELAY;
u_short metric;
switch (proto) {
case HELLO_TO_RIP:
for (metric = 0; metric < RIPHOPCNT_INFINITY; metric++) {
if (cost <= metric_delay[metric]) {
break;
}
}
return(metric);
case RIP_TO_HELLO:
if (cost > RIPHOPCNT_INFINITY) {
cost = RIPHOPCNT_INFINITY;
}
return(metric_delay[cost]);
case EGP_TO_HELLO:
if (cost > HOPCNT_INFINITY) {
cost = HOPCNT_INFINITY;
}
return(cost * ETOTD_CONV);
case HELLO_TO_EGP:
if (cost > TDHOPCNT_INFINITY) {
cost = TDHOPCNT_INFINITY;
}
return(cost / ETOTD_CONV);
default:
return(DELAY_INFINITY); /* just in case some screwup, better to return INFINITY */
}
}
#endif NSS