ftp.nice.ch/pub/next/unix/network/system/gated.2.1pl2.NI.bs.tar.gz#/gated-2.1/src/rt_table.c

This is rt_table.c in view mode; [Download] [Up]

/*
 *  $Header: /disk/d/src/devel/gated/dist/src/RCS/rt_table.c,v 2.1 92/02/24 14:12:58 jch Exp $
 */

/*%Copyright%*/
/************************************************************************
*									*
*	GateD, Release 2						*
*									*
*	Copyright (c) 1990,1991,1992 by Cornell University		*
*	    All rights reserved.					*
*									*
*	THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY		*
*	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT		*
*	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY		*
*	AND FITNESS FOR A PARTICULAR PURPOSE.				*
*									*
*	Royalty-free licenses to redistribute GateD Release		*
*	2 in whole or in part may be obtained by writing to:		*
*									*
*	    GateDaemon Project						*
*	    Information Technologies/Network Resources			*
*	    143 Caldwell Hall						*
*	    Cornell University						*
*	    Ithaca, NY 14853-2602					*
*									*
*	GateD is based on Kirton's EGP, UC Berkeley's routing		*
*	daemon	 (routed), and DCN's HELLO routing Protocol.		*
*	Development of Release 2 has been supported by the		*
*	National Science Foundation.					*
*									*
*	Please forward bug fixes, enhancements and questions to the	*
*	gated mailing list: gated-people@gated.cornell.edu.		*
*									*
*	Authors:							*
*									*
*		Jeffrey C Honig <jch@gated.cornell.edu>			*
*		Scott W Brim <swb@gated.cornell.edu>			*
*									*
*************************************************************************
*									*
*      Portions of this software may fall under the following		*
*      copyrights:							*
*									*
*	Copyright (c) 1988 Regents of the University of California.	*
*	All rights reserved.						*
*									*
*	Redistribution and use in source and binary forms are		*
*	permitted provided that the above copyright notice and		*
*	this paragraph are duplicated in all such forms and that	*
*	any documentation, advertising materials, and other		*
*	materials related to such distribution and use			*
*	acknowledge that the software was developed by the		*
*	University of California, Berkeley.  The name of the		*
*	University may not be used to endorse or promote		*
*	products derived from this software without specific		*
*	prior written permission.  THIS SOFTWARE IS PROVIDED		*
*	``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,	*
*	INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF	*
*	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.		*
*									*
************************************************************************/


#include "include.h"
#include "routed.h"

bits rt_flag_bits[] =
{
    {RTF_UP, "UP"},
    {RTF_GATEWAY, "GW"},
    {RTF_HOST, "HOST"},
#ifdef	RTF_DYNAMIC
    {RTF_DYNAMIC, "DYN"},
#endif				/* RTF_DYNAMIC */
#ifdef	RTF_MODIFIED
    {RTF_MODIFIED, "MOD"},
#endif				/* RTF_MODIFIED */
#ifdef	RTF_DONE
    {RTF_DONE, "DONE"},
#endif				/* RTF_DONE */
#ifdef	RTF_MASK
    {RTF_MASK, "MASK"},
#endif				/* RTF_MASK */
#ifdef	RTF_CLONING
    {RTF_CLONING, "CLONING"},
#endif				/* RTF_CLONING */
#ifdef	RTF_XRESOLVE
    {RTF_XRESOLVE, "XRESOLVE"},
#endif				/* RTF_XRESOLVE */
    {0}
};

bits rt_state_bits[] =
{
    {RTS_NOAGE, "NoAge"},
    {RTS_REMOTE, "Remote"},
    {RTS_CHANGED, "Changed"},
    {RTS_NOTINSTALL, "NotInstall"},
    {RTS_NOADVISE, "NoAdvise"},
    {RTS_SUBNET, "Subnet"},
    {RTS_POINTOPOINT, "P2P"},
    {RTS_HOSTROUTE, "Host"},
    {RTS_INTERIOR, "Int"},
    {RTS_EXTERIOR, "Ext"},
    {RTS_HOLDDOWN, "HoldDown"},
    {RTS_DELETE, "Delete"},
    {RTS_REFRESH, "Refresh"},
    {0}
};

bits rt_proto_bits[] =
{
    {RTPROTO_DIRECT, "Direct"},
    {RTPROTO_KERNEL, "Kernel"},
    {RTPROTO_REDIRECT, "Redirect"},
    {RTPROTO_DEFAULT, "Default"},
    {RTPROTO_IGP, "IGP"},
    {RTPROTO_OSPF, "OSPF"},
    {RTPROTO_IGRP, "IGRP"},
    {RTPROTO_HELLO, "HELLO"},
    {RTPROTO_RIP, "RIP"},
    {RTPROTO_BGP, "BGP"},
    {RTPROTO_EGP, "EGP"},
    {RTPROTO_STATIC, "Static"},
    {RTPROTO_SNMP, "SNMP"},
    {RTPROTO_KRT, "KRT"},
    {0}
};


rt_head *rt_inet_hash[ROUTEHASHSIZ + 1];/* Network route table */

task *rt_task = (task *) 0;
timer *rt_timer = (timer *) 0;
u_int rt_net_routes = 0;		/* # nets in routing table */
u_int rt_host_routes = 0;		/* # hosts in routing table */
int rt_default_active = 0;		/* Number of requests to install default */
int rt_default_needed = FALSE;		/* TRUE if we need to generate a default */
static rt_entry *rt_default_rt;		/* Pointer to internal default if installed */
u_long rt_revision = 0;			/* Revision level of routing table */
gw_entry *rt_gw_list;			/* List of gateways for static routes */

#if	defined(AGENT_SNMP)
static int rt_table_changed = TRUE;	/* Routing table has been changed */

#endif				/* defined(AGENT_SNMP) */

static int rt_changes = 0;		/* Number of changes to routing table */
static task *rt_opentask = (task *) 0;	/* Protocol that has table open */

#define	rt_check_open(rt, name)	if (!rt_opentask) { trace(TR_ALL, LOG_ERR, "%s: table not open - proto %s", \
							  name, trace_bits(rt_proto_bits, rt->rt_proto)); \
							      quit(EBADF); }

/*
 * rt_trace() traces changes to the routing tables
 */
 /* static*/ void
rt_trace(action, rt)
char *action;
rt_entry *rt;
{

    tracef("%-8s %-15A gw %-15A  %-8s  pref %3d  metric %d",
	   action,
	   &rt->rt_dest,
	   &rt->rt_router,
	   trace_bits(rt_proto_bits, rt->rt_proto),
	   rt->rt_preference,
	   rt->rt_metric);
    tracef("  <%s>", trace_bits(rt_state_bits, rt->rt_state));
    if (rt->rt_as) {
	tracef("  as %d", rt->rt_as);
    }
    /* XXX - Format protocol specific information? */
    trace(TR_RT | TR_NOSTAMP, 0, NULL);

}


/**/

/*
 *	Locate the rt_head pointer for this destination.  Create one if it does not exist.
 */
static rt_head *
rth_locate(dst, mask, state)
sockaddr_un *dst;
sockaddr_un *mask;
flag_t *state;
{
    rt_head *rth = (rt_head *) 0;
    rt_head *xrth;

    RT_HASH(dst);

    RT_BUCKET(rth) {
	if ((rth->rth_hash == hash) && (equal(&rth->rth_dest, dst))) {
	    break;
	}
    } RT_BUCKET_END(rth);

    if (!rth) {
	rth = (rt_head *) calloc(1, sizeof(*rth));
	if (!rth) {
	    trace(TR_ALL, LOG_ERR, "rth_locate: calloc: %m");
	    quit(errno);
	}
	/* Copy destination */
	sockcopy(dst, &rth->rth_dest);

	/* Set the mask */
	if (mask) {
	    sockcopy(mask, &rth->rth_dest_mask);
	} else {
	    sockcopy(dst, &rth->rth_dest_mask);

	    if (rth->rth_dest.in.sin_addr.s_addr == htonl(DEFAULTNET)) {
		rth->rth_dest_mask.in.sin_addr.s_addr = htonl(INADDR_ANY);
	    } else if (*state & RTS_HOSTROUTE) {
		rth->rth_state |= RTS_HOSTROUTE;
		rth->rth_dest_mask.in.sin_addr.s_addr = htonl(INADDR_BROADCAST);
	    } else {
		u_long subnet_mask, natural_mask;

		if (gd_inet_lnaof(rth->rth_dest.in.sin_addr)) {
		    /* Host bits must be zero for a network route */
		    rth->rth_dest.in.sin_addr =
		    gd_inet_makeaddr(gd_inet_netof(rth->rth_dest.in.sin_addr), 0, TRUE);
		}
		natural_mask = htonl(gd_inet_netmask(ntohl(rth->rth_dest.in.sin_addr.s_addr)));
		subnet_mask = htonl(if_subnetmask(rth->rth_dest.in.sin_addr));

		if (subnet_mask &&
		    ((rth->rth_dest.in.sin_addr.s_addr & natural_mask) !=rth->rth_dest.in.sin_addr.s_addr)) {
		    /* This is a subnet - set subnet mask */
		    *state |= RTS_SUBNET;
		    rth->rth_state |= RTS_SUBNET;
		    rth->rth_dest_mask.in.sin_addr.s_addr = subnet_mask;
		} else {
		    /* This is not a subnet - set the natural mask */
		    rth->rth_dest_mask.in.sin_addr.s_addr = natural_mask;
		}
	    }
	}

	rth->rth_hash = hash;
	rth->rt_forw = rth->rt_back = (rt_entry *) & rth->rt_forw;
	rth->rt_head = rth;

	/* Insert this rt_head structure at end of doubly linked list */
	if (*rtb) {
	    insque((struct qelem *) rth, (struct qelem *) (*rtb)->rth_back);
	} else {
	    *rtb = rth;
	    rth->rth_forw = rth->rth_back = rth;
	}
    }
    return (rth);
}


/**/
/*
 *	Remove an rt_entry structure from the doubly linked list
 *	pointed to by it's rt_head
 */

static void
rt_remove(rt)
rt_entry *rt;
{
    if (!--rt->rt_head->rth_entries) {
	if (rt->rt_state & RTS_HOSTROUTE) {
	    rt_host_routes--;
	} else {
	    rt_net_routes--;
	}
    }
    remque((struct qelem *) rt);
}


/*	Insert an rt_entry structure in preference order in the doubly linked	*/
/*	list pointed to by it's rt_head.  If two routes with identical		*/
/*	preference are found, the one witht he shorter as path length is used.	*/
/*	If the as path lengths are the same, the route with the lower next-hop	*/
/*	IP address is prefered. This insures that the selection of the prefered	*/
/*	route is deterministic.							*/
static void
rt_insert(rt)
rt_entry *rt;
{
    rt_entry *rt1;
    rt_head *rth = rt->rt_head;

    RT_ALLRT(rt1, rth) {
	if ((rt->rt_state & RTS_DELETE) && !(rt1->rt_state & RTS_DELETE)) {
	    /* Deleted routes go behind non-deleted routes */
	    continue;
	}
	if ((rt1->rt_state & RTS_DELETE) && !(rt->rt_state & RTS_DELETE)) {
	    /* non-deleted routes preceed deleted routes */
	    break;
	}
	if (rt->rt_preference < rt1->rt_preference) {
	    /* This preference is better */
	    break;
	} else if (rt->rt_preference == rt1->rt_preference) {
	    /* Same preference */

	    if (rt->rt_proto == rt1->rt_proto &&
		rt->rt_as == rt1->rt_as) {
		/* Same protocol and AS */

		if (rt->rt_metric < rt1->rt_metric) {
		    /* Use lower metric */
		    break;
		}
	    }

	    /* lastly try router address */
	    if (rt->rt_router.in.sin_addr.s_addr < rt1->rt_router.in.sin_addr.s_addr) {
		/* This router address is lower, use it */
		break;
	    }
	}
    } RT_ALLRT_END(rt1, rth);

    /* Insert prior to element if found, or behind the element at the end of a list. */
    /* For an empty list this ends up being behind the first element. */
    insque((struct qelem *) rt, (struct qelem *) (rt1 ? rt1->rt_back : rth->rt_back));

    if (!rth->rth_entries++) {
	if (rt->rt_state & RTS_HOSTROUTE) {
	    rt_host_routes++;
	} else {
	    rt_net_routes++;
	}
    }
}


/**/
/*
 *	rt_alloc - allocate an rt_entry
 */

static rt_entry *
rt_alloc(dst, mask, state)
sockaddr_un *dst;
sockaddr_un *mask;
flag_t *state;
{
    rt_entry *rt;

    /* No free list entries, allocate a new one */
    rt = (rt_entry *) calloc(1, sizeof(*rt));
    if (!rt) {
	trace(TR_ALL, LOG_ERR, "rt_alloc: calloc: %m");
    }
    /* Set pointer to head */
    rt->rt_head = rth_locate(dst, mask, state);
    return (rt);
}


/*
 * Delete a route from the routing table.
 */
static void
rt_release(rt)
rt_entry *rt;
{

    rt_check_open(rt, "rt_release");

    if (!(rt->rt_state & RTS_DELETE)) {
	/* If this route is active the kernel's routing table needs to be	*/
	/* updated.  If this is the only route for this destination we only	*/
	/* need to delete it from the kernel.  If there is another route	*/
	/* then we need to change to this new route.			*/
	if (rt->rt_active == rt) {
	    rt_entry *rt1;
	    rt_head *rth = rt->rt_head;

	    RT_ALLRT(rt1, rth) {
		if ((rt1 != rt) && !(rt1->rt_state & RTS_HOLDDOWN)) {
		    break;
		}
	    } RT_ALLRT_END(rt1, rth);

	    krt_change(rt, rt1);	/* krt_change does a change or a delete */
	    rt->rt_active = rt1;
	}
    }
    TRACE_ACTION((rt->rt_state & RTS_DELETE) ? "RELEASE" : "DELETE", rt);

    rt_changes++;

    if (rt->rt_data) {
	rtd_unlink(rt->rt_data);
    }
    rt_remove(rt);

    (void) free((caddr_t) rt);
#if	defined(AGENT_SNMP)
    rt_table_changed = TRUE;
#endif				/* defined(AGENT_SNMP) */
}

/**/
/*
 *	rt_open: Make table available for updating
 */
void
rt_open(tp)
task *tp;
{
    if (rt_opentask) {
	tracef("rt_open: open attempt by %s",
	       task_name(tp));
	trace(TR_ALL, LOG_ERR, " already open by %s",
	      task_name(rt_opentask));
	quit(EPERM);
    }
    rt_opentask = tp;
    rt_revision++;
    rt_changes = 0;
}


/*
 *	rt_close: Clean up after table updates
 */
int
rt_close(tp, gwp, changes)
task *tp;
gw_entry *gwp;
int changes;
{
    int rtchanges = rt_changes;

    if (!rt_opentask) {
	trace(TR_ALL, LOG_ERR, "rt_close: close attempt by %s when table not open",
	      task_name(tp));
	quit(EBADF);
    }
    if (rt_opentask != tp) {
	tracef("rt_close: close attempt by %s",
	       task_name(tp));
	trace(TR_ALL, LOG_ERR, " when opened by %s",
	      task_name(rt_opentask));
	quit(EBADF);
    }
    rt_opentask = (task *) 0;
    if (rt_changes) {
	tracef("rt_close: %d",
	       rt_changes);
	if (changes) {
	    tracef("/%d", changes);
	}
	tracef(" route%s proto %s",
	       rt_changes > 1 ? "s" : "",
	       task_name(tp));
	if (gwp) {
	    tracef(" from %A",
		   &gwp->gw_addr);
	}
	trace(TR_RT, 0, " table revision %ld",
	      rt_revision);
	trace(TR_RT, 0, NULL);
	rt_changes = 0;
    } else {
	rt_revision--;
    }

    return (rtchanges);
}

/**/
 /*  Add a route to the routing table after some checking.  The route	*/
 /*  is added in preference order.  If the active route changes, the	*/
 /*  kernel routing table is updated.					*/
rt_entry *
#ifdef	USE_PROTOTYPES
rt_add(sockaddr_un * dst,
       sockaddr_un * mask,
       sockaddr_un * gate,
       gw_entry * sourcegw,
       metric_t metric,
       flag_t state,
       proto_t proto,
       as_t as,
       time_t timer_max,
       pref_t preference)
#else				/* USE_PROTOTYPES */
rt_add(dst, mask, gate, sourcegw, metric, state, proto, as, timer_max, preference)
sockaddr_un *dst, *mask, *gate;
gw_entry *sourcegw;
metric_t metric;
flag_t state;
proto_t proto;
as_t as;
time_t timer_max;
pref_t preference;

#endif				/* USE_PROTOTYPES */
{
    rt_entry *rt = (rt_entry *) 0;

    rt_check_open(rt, "rt_add");

    rt = rt_alloc(dst, mask, &state);
    if (!rt) {
	return (rt);
    }
    if (rt->rt_head->rth_entries) {
	rt_entry *rt1;

	RT_ALLRT(rt1, rt->rt_head) {
	    if ((rt1->rt_proto & proto) && (rt1->rt_sourcegw == sourcegw) && (rt1->rt_state & RTS_DELETE)) {
		rt1 = rt1->rt_back;
		rt_release(rt1->rt_forw);
	    }
	} RT_ALLRT_END(rt1, rt->rt_head);
    }
    /* XXX - need to support multiple next-hops */
    rt->rt_router = *gate;
    rt->rt_sourcegw = sourcegw;
    rt->rt_metric = metric;
    rt->rt_timer = 0;
    rt->rt_timer_max = timer_max ? timer_max : RT_T_EXPIRE;
    rt->rt_flags = 0;
    rt->rt_preference = preference;
#ifdef	RTM_ADD
    rt->rt_mtu = 0;			/* Need code to figure out a good mtu by the time we port to BSD 4.4 */
#endif				/* RTM_ADD */
    rt->rt_state = state | RTS_CHANGED | rt->rt_head->rth_state;

    if ((rt->rt_state & rt->rt_head->rth_state) != rt->rt_head->rth_state) {
	/* XXX - this route does not match */
    }
    /* Set RTF_HOST flag if appropriate */
    if (rt->rt_state & RTS_HOSTROUTE) {
	rt->rt_flags |= RTF_HOST;
    }
    rt->rt_proto = proto;
    rt->rt_as = as;
    rt->rt_ifp = if_withdst(&rt->rt_router);
    if (rt->rt_ifp == (if_entry *) NULL) {
	trace(TR_ALL, LOG_WARNING, "rt_add: interface not found for net %-15A gateway %A",
	      &rt->rt_dest,
	      &rt->rt_router);
	(void) free((caddr_t) rt);
	return ((rt_entry *) 0);
    }
#ifdef	notdef
    if (rt->rt_ifp->int_state & IFS_LOOPBACK && rt->rt_proto != RTPROTO_DEFAULT) {
	rt->rt_state |= RTS_NOADVISE;
    }
#endif	/* notdef */
#ifdef	RTF_DYNAMIC
    if (rt->rt_proto == RTPROTO_REDIRECT) {
	rt->rt_flags |= RTF_DYNAMIC;
    }
#endif				/* RTF_DYNAMIC */

    /* If this is a martian net it doesn't get into MY tables */
    /* The exception is the loopback host address on the loopback */
    /* interface */
    if (is_martian(&rt->rt_dest)) {
	struct in_addr addr;

	addr.s_addr = htonl(INADDR_LOOPBACK);
	if (!((rt->rt_ifp->int_state & IFS_LOOPBACK) &&
	      equal_in(rt->rt_dest.in.sin_addr, addr))) {
	    tracef("rt_add: ignoring martian network %A",
		   &rt->rt_dest);
	    if (rt->rt_sourcegw) {
		tracef(" from gateway %A",
		       &rt->rt_sourcegw->gw_addr);
	    }
	    tracef(" protocol %s AS %d",
		   trace_bits(rt_proto_bits, rt->rt_proto),
		   rt->rt_as);
	    trace(TR_INT, LOG_WARNING, NULL);
	    (void) free((caddr_t) rt);
	    return ((rt_entry *) 0);
	}
    }
    if (rt->rt_state & RTS_EXTERIOR) {
	rt->rt_flags |= RTF_GATEWAY;
    } else {
	switch (rt->rt_proto) {
	    case RTPROTO_KERNEL:
		if (rt->rt_flags & RTF_HOST) {
		    if (!if_withaddr(&rt->rt_dest)) {
			rt->rt_flags |= RTF_GATEWAY;
		    }
		} else {
		    if (!if_withdst(&rt->rt_dest)) {
			rt->rt_flags |= RTF_GATEWAY;
		    }
		}
		break;
	    case RTPROTO_STATIC:
		if (!if_withaddr(&rt->rt_dest)) {
		    rt->rt_flags |= RTF_GATEWAY;
		}
		break;
	    case RTPROTO_DIRECT:
		break;
	    default:
		rt->rt_flags |= RTF_GATEWAY;
		break;
	}
    }
    rt_changes++;
    rt->rt_revision = rt_revision;
    TRACE_ACTION("ADD", rt);

    /* Insert this route into the table */
    rt_insert(rt);

    /* If the new route is the first in the chain and there is no active	*/
    /* route, or the active route is not in Holddown, this route will	*/
    /* become the active route and the kernel should be updated. */
    if ((rt == rt->rt_head->rt_forw) && !(rt->rt_active && (rt->rt_active->rt_state & RTS_HOLDDOWN))) {
	krt_change(rt->rt_active, rt);	/* krt_change does an add or a change */
	rt->rt_active = rt;		/* This route is now the active route */
    }
#if	defined(AGENT_SNMP)
    rt_table_changed = TRUE;
#endif				/* defined(AGENT_SNMP) */

    return (rt);
}


 /* rt_change() changes a route &/or notes that an update was received.	*/
 /* returns 1 if change made.  Updates the kernel's routing table if	*/
 /* the router has changed, or a preference change has made another	*/
 /* route active							*/
int
#ifdef	USE_PROTOTYPES
rt_change(rt_entry * rt,
	  sockaddr_un * gate,
	  metric_t metric,
	  time_t timer_max,
	  pref_t preference)
#else				/* USE_PROTOTYPES */
rt_change(rt, gate, metric, timer_max, preference)
rt_entry *rt;
sockaddr_un *gate;
metric_t metric;
time_t timer_max;
pref_t preference;

#endif				/* USE_PROTOTYPES */
{
    rt_entry orig_rt;
    int krt_changed = FALSE;		/* Kernel needs to be changed */
    int pref_changed = TRUE;		/* Active route may need to be updated */
    int changed = FALSE;
    if_entry *t_ifp;

    orig_rt = *rt;			/* Save a copy of the original route */

    rt_check_open(rt, "rt_change");

    if (rt->rt_ifp != (t_ifp = if_withdst(gate))) {
	if (rt->rt_ifp == (if_entry *) NULL) {
	    trace(TR_ALL, LOG_WARNING, "rt_change: interface not found for net %-15A gateway %A",
		  &rt->rt_dest,
		  &rt->rt_router);
	    return (FALSE);
	}
	rt->rt_ifp = t_ifp;
	changed = TRUE;
    }
    rt->rt_timer_max = timer_max ? timer_max : RT_T_EXPIRE;
    rt->rt_state |= RTS_CHANGED;	/* ensures route age reset */
    rt->rt_state &= ~RTS_HOLDDOWN;	/* If route changed, reset hold down */

    /* XXX - need to support multiple next hops */
    if (!equal(&rt->rt_router, gate)) {
	krt_changed = TRUE;
	pref_changed = TRUE;
	changed = TRUE;
	rt->rt_router = *gate;
    }
    if (metric != rt->rt_metric) {
	changed = TRUE;
	rt->rt_metric = metric;
    }
    if (preference != rt->rt_preference) {
	changed = TRUE;
	pref_changed = TRUE;
	rt->rt_preference = preference;
    }
    if (pref_changed) {
	/* Put this route in order in the queue by deleting it and	*/
	/* re-inserting it						*/
	rt_remove(rt);
	rt_insert(rt);
	krt_changed = TRUE;
    }
    if (krt_changed) {
	rt_entry *new_rt, *old_rt;

	new_rt = old_rt = rt->rt_active;/* Default is not to change active route */

	if (rt->rt_head->rt_forw == rt) {
	    /* This route is eligble to become the active route */
	    if (rt->rt_active == rt) {
		/* We are still the active route, but the gateway or mtu may have changed */
		old_rt = &orig_rt;
		new_rt = rt;
	    } else if (rt->rt_active) {
		/* Another route is active */
		if (!rt->rt_active->rt_state & RTS_HOLDDOWN) {
		    /* Other route is not in holddown, OK to switch */
		    new_rt = rt;
		}
	    } else {
		/* No route active, this is the new active route */
		new_rt = rt;
	    }
	} else if (rt->rt_active == rt) {
	    /* We were active, now find a new one */
	    rt_head *rth = rt->rt_head;

	    RT_ALLRT(new_rt, rth) {
		if ((new_rt != rt) && !(new_rt->rt_state & RTS_HOLDDOWN)) {
		    break;
		}
	    } RT_ALLRT_END(new_rt, rth);
	}
	(void) krt_change(old_rt, new_rt);	/* krt_change figures out what needs changing */

	rt->rt_active = new_rt;		/* Set the (maybe) new active route */
    }
    if (changed) {
	rt_changes++;
	rt->rt_revision = rt_revision;
	TRACE_ACTION("CHANGE", rt);
#if	defined(AGENT_SNMP)
	rt_table_changed = TRUE;
#endif				/* defined(AGENT_SNMP) */
    }
    return (TRUE);
}


#ifndef	rt_refresh
/*
 * rt_refresh() flags the route as being refreshed so the route timer will be reset the next time rt_time is run.
 */
rt_refresh(rt)
rt_entry *rt;
{

    rt_check_open(rt, "rt_refresh");

    rt->rt_state |= RTS_REFRESH;
}

#endif				/* rt_refresh */


/*
 *
 * 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 RT_HOLDDOWN seconds.
 */
int
rt_unreach(rt)
rt_entry *rt;
{

    rt_check_open(rt, "rt_unreach");

    if (!(rt->rt_state & RTS_HOLDDOWN)) {
	rt->rt_state |= RTS_HOLDDOWN;
	rt->rt_timer = 0;
	rt->rt_timer_max = RT_T_HOLDDOWN;
	rt_changes++;
	rt->rt_revision = rt_revision;
	TRACE_ACTION("CHANGE", rt);
#if	defined(AGENT_SNMP)
	rt_table_changed = TRUE;
#endif				/* defined(AGENT_SNMP) */
	return (1);
    } else {
	return (0);
    }
}


/*
 * rt_delete() does processing on a route that has been indicated as
 * deleted in a routing update.  The timer is set so the route will expire
 * within RT_DELETE seconds.
 */
int
rt_delete(rt)
rt_entry *rt;
{

    rt_check_open(rt, "rt_delete");

    if (!(rt->rt_state & RTS_DELETE)) {
	rt->rt_state |= RTS_DELETE;
	rt->rt_timer = 0;
	rt->rt_timer_max = RT_T_DELETE;
	rt_changes++;
	rt->rt_revision = rt_revision;
	if (rt->rt_active == rt) {
	    rt_entry *rt1;
	    rt_head *rth = rt->rt_head;

	    rt_remove(rt);
	    rt_insert(rt);

	    RT_ALLRT(rt1, rth) {
		if ((rt1 != rt) && !(rt1->rt_state & RTS_HOLDDOWN)) {
		    break;
		}
	    } RT_ALLRT_END(rt1, rth);

	    krt_change(rt, rt1);
	    rt->rt_active = rt1;
	}
	rt->rt_state |= RTS_NOTINSTALL;
	TRACE_ACTION("DELETE", rt);
#if	defined(AGENT_SNMP)
	rt_table_changed = TRUE;
#endif				/* defined(AGENT_SNMP) */
	return (1);
    } else {
	return (0);
    }
}


/**/
/*
 *	Routines to handle route specific data
 */
rt_data *
rtd_alloc(length)
int length;
{
    rt_data *rtd;

    rtd = (rt_data *) calloc(1, sizeof(*rtd) + length);
    if (!rtd) {
	trace(TR_ALL, LOG_ERR, "rtd_alloc: calloc: %m");
	quit(errno);
    }
    rtd->rtd_data = (caddr_t) rtd + sizeof(*rtd);
    rtd->rtd_length = length;

    return (rtd);
}


rt_data *
rtd_locate(data, length, head)
caddr_t data;
int length;
rt_data *head;
{
    rt_data *rtd;

    RTDATA_LIST(rtd, head) {
	if ((rtd->rtd_length == length) &&
	    !memcmp(rtd->rtd_data, data, length)) {
	    break;
	}
    } RTDATA_LIST_END(rtd, head);

    if (!rtd) {
	rtd = rtd_alloc(length);

	memcpy(rtd->rtd_data, data, length);
	insque((struct qelem *) rtd, (struct qelem *) head);
    }
    rtd->rtd_refcount++;

    return (rtd);
}


rt_data *
rtd_insert(rtd, head)
rt_data *rtd;
rt_data *head;
{
    rt_data *rtd1;

    RTDATA_LIST(rtd1, head) {
	if ((rtd->rtd_length == rtd1->rtd_length) &&
	 !memcmp(rtd->rtd_data, rtd1->rtd_data, (int) rtd->rtd_length)) {
	    break;
	}
    } RTDATA_LIST_END(rtd1, head);

    if (rtd1) {
	(void) free((caddr_t) rtd);
	rtd = rtd1;
    } else {
	insque((struct qelem *) rtd, (struct qelem *) head);
    }

    rtd->rtd_refcount++;

    return (rtd);
}


void
rtd_unlink(rtd)
rt_data *rtd;
{
    if (!--rtd->rtd_refcount) {
	remque((struct qelem *) rtd);
	(void) free((caddr_t) rtd);
    }
}


/**/

/*
 * Handle the internally generated default route.
 */

/*
 * This is only a pseudo route so use the loopback interface,
 * it should not go down and produce
 * undesirable side effects.
 */  
#define	RT_DEFAULT_ADD	rt_default_rt = rt_add((sockaddr_un *) &default_net, \
					       (sockaddr_un *) 0, \
					       &loopback, \
					       (gw_entry *) 0, \
					       0, \
					       RTS_INTERIOR | RTS_NOAGE | RTS_NOTINSTALL, \
					       RTPROTO_DEFAULT, \
					       (as_t) 0, \
					       (time_t) 0, \
					       RTPREF_DEFAULT)

#define	RT_DEFAULT_DELETE	(void) rt_delete(rt_default_rt); \
    				rt_default_rt = (rt_entry *) 0

static void
rt_default_reinit()
{
    if (rt_default_needed) {
	/* Default is enabled */

	if (rt_default_active && !rt_default_rt) {
	    /* Should be installed, but is not */
	    sockaddr_un loopback;

	    sockclear_in(&loopback.in);
	    loopback.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

	    RT_DEFAULT_ADD;
	}
    } else {
	/* Default is disabled */

	if (rt_default_rt) {
	    /* Get rid of current default */

	    RT_DEFAULT_DELETE;
	}
    }
}


int
rt_default_add()
{
    if (!rt_default_active++ && rt_default_needed) {
	/* First request to add and default is enabled, add it */
	sockaddr_un loopback;

	sockclear_in(&loopback.in);
	loopback.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

	RT_DEFAULT_ADD;

	return 1;
    }

    return 0;
}


int
rt_default_delete()
{

    if (--rt_default_active && rt_default_rt) {
	/* Last request to delete and default is installed, remove it */

	rt_open(rt_task);
	RT_DEFAULT_DELETE;
	rt_close(rt_task, (gw_entry *) 0, 0);

	return 1;
    }

    return 0;
}


static void
rt_default_cleanup()
{
    rt_default_needed = FALSE;
}


#undef	RT_DEFAULT_ADD
#undef	RT_DEFAULT_DELETE

/**/
/*
 * rt_time() increments the age of all routes in the routing table
 */

/*ARGSUSED*/
static void
rt_time(tip, interval)
timer *tip;
time_t interval;
{
    int old_routes = 0, hold_routes = 0;
    rt_entry *rt = (rt_entry *) 0;

    rt_open(rt_task);
    RT_WHOLE(rt) {
	if (rt->rt_state & RTS_REFRESH) {	/* Route was refreshed */
	    rt->rt_state &= ~RTS_REFRESH;
	    rt->rt_timer = 0;
	}
	if (rt->rt_state & RTS_CHANGED) {	/* recently updated */
	    rt->rt_state &= ~RTS_CHANGED;
	    rt->rt_timer = 0;
	} else if (!(rt->rt_state & RTS_NOAGE) || (rt->rt_state & (RTS_HOLDDOWN | RTS_DELETE))) {
	    rt->rt_timer += interval;
	}
	/*
         *  is route too old?
         */
	if (rt->rt_timer >= rt->rt_timer_max) {
	    if (rt->rt_state & (RTS_HOLDDOWN | RTS_DELETE)) {
		if (rt->rt_proto == RTPROTO_DIRECT) {
		    trace(TR_INT, LOG_ERR, "interface timeout - deleting route to %A",
			  &rt->rt_dest);
		}
		old_routes++;
		rt = rt->rt_back;
		rt_release(rt->rt_forw);
	    } else {
		hold_routes += rt_unreach(rt);
	    }
	}
    } RT_WHOLEEND(rt);
    (void) rt_close(rt_task, (gw_entry *) 0, old_routes + hold_routes);
    if (old_routes + hold_routes) {
	trace(TR_RT, 0, "rt_time: above %d routes deleted and %d routes helddown", old_routes, hold_routes);
	if (hold_routes) {
	    /* Only do flashing for helddown routes, deleted routes are already history */
	    task_flash(rt_task);
	}
    }
    return;
}


/*
 * rt_gwunreach() deletes all exterior routes from the routing table for a
 * specified gateway
 */
int
rt_gwunreach(tp, gwp)
task *tp;
gw_entry *gwp;
{
    int changes = 0;
    rt_entry *rt;

    rt_open(tp);

    RT_TABLE(rt) {
	if (rt->rt_sourcegw == gwp) {
	    changes += rt_delete(rt);
	}
    } RT_TABLEEND;

    (void) rt_close(tp, gwp, changes);

    return (changes);
}


/*
 * Looks up a destination network route with a specific protocol mask.
 * Specifying a protocol of zero will match all protocols.
 */

rt_entry *
rt_locate(state, dst, proto)
flag_t state;
sockaddr_un *dst;
proto_t proto;
{
    rt_entry *rt;
    rt_head *rth = (rt_head *) 0;

    RT_HASH(dst);

    RT_BUCKET(rth) {
	if ((rth->rth_hash == hash) && equal(&rth->rth_dest, dst)) {
	    RT_ALLRT(rt, rth) {
		if (!(rt->rt_state & RTS_DELETE) && (rt->rt_state & state) && (rt->rt_proto & proto)) {
		    return (rt);
		}
	    } RT_ALLRT_END(rt, rth);
	}
    } RT_BUCKET_END(rth);

    return ((rt_entry *) 0);
}


/* Look up a route with a destination address, protocol and source gateway */

rt_entry *
rt_locate_gw(state, dst, proto, gwp)
flag_t state;
sockaddr_un *dst;
proto_t proto;
gw_entry *gwp;
{
    rt_entry *rt;
    rt_head *rth = (rt_head *) 0;

    RT_HASH(dst);

    RT_BUCKET(rth) {
	if ((rth->rth_hash == hash) && equal(&rth->rth_dest, dst)) {
	    RT_ALLRT(rt, rth) {
		if (!(rt->rt_state & RTS_DELETE) && (rt->rt_state & state) && (rt->rt_proto & proto) && (rt->rt_sourcegw == gwp)) {
		    return (rt);
		}
	    } RT_ALLRT_END(rt, rth);
	}
    } RT_BUCKET_END(rth);

    return ((rt_entry *) 0);
}


/*
 * rt_redirect() changes the routing tables in response to a redirect
 * message or indication from the kernel
 */

int ignore_redirects = FALSE;
int redirect_n_trusted = 0;		/* Number of trusted ICMP gateways */
pref_t redirect_preference = RTPREF_REDIRECT;	/* Preference for ICMP redirects */
gw_entry *redirect_gw_list;		/* Active ICMP gateways */
adv_entry *redirect_accept_list = NULL;	/* List of nets to accept from ICMP */
adv_entry **redirect_int_accept = NULL;	/* List of accept lists per interface */

void
rt_redirect(tp, dst, gateway, src, host_redirect)
task *tp;
sockaddr_un *dst, *gateway, *src;
int host_redirect;
{
    int saveinstall = install;
    rt_entry *rt;
    int interior = 0;
    pref_t preference = redirect_preference;
    register if_entry *ifp;
    gw_entry *gwp = 0;
    const char *redirect_type;
    flag_t table;

    /* XXX - How about installing all ICMP routes, then delete the ones we don't want */
    /* XXX - This will remove need for special interface to kernel delete routines */
    /* XXX - Maybe a flag to indicate that a delete failure is OK */

    rt_open(tp);

    if (host_redirect) {
	redirect_type = "host";
	table = RTS_HOSTROUTE;
    } else {
	redirect_type = "net";
	table = RTS_NETROUTE;
    }

    /* check gateway directly reachable */
    if (!if_withdst((sockaddr_un *) gateway)) {
	goto do_log;
    }
    /* Ignore if we are the source of this packet */
    if (src && if_withaddr((sockaddr_un *) src)) {
	goto do_log;
    }
    if (if_withaddr((sockaddr_un *) gateway) || if_withaddr((sockaddr_un *) dst)) {	/* a routing loop? */
	/*	XXX - What do to here?	*/
#ifdef	notdef
	tracef("rt_redirect: Routing loop, %A via %A",
	       dst,
	       &gateway);
	if (src) {
	    tracef(" from %A",
		   &src);
	}
	trace(TR_ALL, LOG_ERR, NULL);
#endif				/* notdef */
	goto do_log;
    }
    install = FALSE;			/* route already in kernel */

    tracef("REDIRECT: %s redirect", redirect_type);
    if (src != NULL) {
	tracef(" from %A",
	       src);
    }
    tracef(": %A via %A: ",
	   dst,
	   gateway);

    if (ignore_redirects) {
	trace(TR_RT, 0, "redirects not allowed");
	goto Delete;
    }
    if (ifp = if_withaddr((sockaddr_un *) gateway)) {
	trace(TR_RT, 0, "cannot redirect to myself");
	goto Delete;
    }
    if (!host_redirect) {
	IF_LIST(ifp) {
	    if (gd_inet_wholenetof(dst->in.sin_addr) == gd_inet_wholenetof(ifp->int_addr.in.sin_addr)) {
		interior++;
		break;
	    }
	} IF_LISTEND(ifp) ;
    }
    rt = rt_locate(table, (sockaddr_un *) dst, (flag_t) RTPROTO_ANY);
    if (src && rt && !equal(src, &rt->rt_router)) {
	trace(TR_RT, 0, "not from router in use");
	if (!equal(gateway, &rt->rt_router)) {
	    goto Delete;
	} else {
	    goto Invalid;
	}
    }

    gwp = gw_timestamp(&redirect_gw_list, RTPROTO_REDIRECT, src);
    /* If we have a list of trusted gateways, verify that this gateway is trusted */
    if (redirect_n_trusted && !(gwp->gw_flags & GWF_TRUSTED)) {
	trace(TR_ALL, LOG_ERR, "not from a trusted gateway");
	goto Delete;
    }

    ifp = if_withdst((sockaddr_un *) gateway);
    if (!ifp) {
	trace(TR_ALL, LOG_ERR, "can not find interface for gateway");
	goto Delete;
    }
    if (!is_valid_in((sockaddr_un *) dst,
		     redirect_accept_list,
		     INT_CONTROL(redirect_int_accept, ifp),
		     gwp->gw_accept,
		     &preference)) {
	trace(TR_RT, 0, "not valid");
	goto Delete;
    }

    trace(TR_RT, 0, NULL);

    rt = rt_locate(table, (sockaddr_un *) dst, RTPROTO_REDIRECT);
    /* XXX - what if a route with a lower preference exists?  Need to fix the kernel */
    if (rt) {
	if (rt_change(rt,
		      (sockaddr_un *) gateway,
		      rt->rt_metric,
		      (time_t) 0,
		      preference) == 0) {
	    trace(TR_RT, 0, "rt_redirect: error from rt_change");
	    goto Invalid;
	}
    } else {
	table = (host_redirect) ? RTS_HOSTROUTE : interior ? RTS_INTERIOR : RTS_EXTERIOR;
	if (!(rt = rt_add((sockaddr_un *) dst,
			  (sockaddr_un *) 0,
			  (sockaddr_un *) gateway,
			  gwp,
			  0,
			  table,
			  RTPROTO_REDIRECT,
			  0,
			  (time_t) 0,
			  preference))) {
	    trace(TR_RT, 0, "rt_redirect: error from rt_add");
	    goto Delete;
	}
    }
    goto do_log;

  Delete:
    /*
     *  Delete the entry from the kernel
     */

    install = saveinstall;
    (void) krt_delete_dst(tp,
			  dst,
			  (sockaddr_un *) 0,
			  gateway,
	  (flag_t) ((host_redirect ? RTF_HOST : 0) | RTF_UP | RTF_GATEWAY
#ifdef	RTF_DYNAMIC
		    | RTF_DYNAMIC
#endif				/* RTF_DYNAMIC */
			  )
	);

    /* If we have a route for this net installed, we had better reinstall it */
    rt = rt_locate(table, (sockaddr_un *) dst, (flag_t) RTPROTO_ANY);
    if (rt) {
	(void) krt_add(rt);
    }
    if (!(trace_flags & TR_KRT)) {
	goto Invalid;
    }
  do_log:

  Invalid:
    install = saveinstall;
    (void) rt_close(tp, gwp, 0);
    return;
}


#if	defined(AGENT_SNMP)
/*
 *	Routine to compare to routine table entries, used by rt_next
 */
int
rt_next_compare(rt1, rt2)
rt_entry **rt1, **rt2;
{
    u_long dst1 = ntohl((*rt1)->rt_dest.in.sin_addr.s_addr);
    u_long dst2 = ntohl((*rt2)->rt_dest.in.sin_addr.s_addr);
    int compare;

    if (dst1 < dst2) {
	compare = -1;
    } else if (dst1 > dst2) {
	compare = 1;
    } else {
	compare = 0;
    }
    return (compare);
}


/*
 *	Lookup next routing table entry, used by SNMP
 */
rt_entry *
rt_next(dst)
sockaddr_un *dst;
{
    int i;
    static int numb_routes = 0;
    static u_int n_routes = 0;
    rt_entry *rt;
    static rt_entry **rt_table_sort;
    rt_entry **rtp = rt_table_sort;

    if (rt_table_changed) {
	rt_table_changed = FALSE;
	n_routes = rt_net_routes + rt_host_routes;
	if ((n_routes > numb_routes)) {
	    if (rt_table_sort) {
		free((char *) rt_table_sort);
	    }
	    trace(TR_INT, 0, "rt_next: allocating routing table for %d routes", n_routes);
	    /* Allocate for one extra so the list is null terminated */
	    rt_table_sort = (rt_entry **) calloc(n_routes + 1, sizeof(rt_entry *));
	    if (rt_table_sort == NULL) {
		trace(TR_ALL, LOG_ERR, "rt_next: malloc: %m");
		return (NULL);
	    }
	    rtp = rt_table_sort;
	    numb_routes = n_routes;
	}
	trace(TR_INT, 0, "rt_next: copying and sorting table for %d routes", n_routes);
	i = 0;
	RT_TABLE(rt) {
	    rt_table_sort[i++] = rt;
	    if (i > n_routes) {
		trace(TR_ALL, LOG_ERR, "rt_next: n_routes = %d is too small", n_routes);
		return (NULL);
	    }
	} RT_TABLEEND;

	if (i != n_routes) {
	    trace(TR_ALL, LOG_ERR, "rt_next n_routes = %d, i = %d",
		  n_routes,
		  i);
	    return (NULL);
	}
	n_routes = i;

	qsort((char *) rt_table_sort, (int) n_routes, sizeof(rt_entry *), rt_next_compare);

    }
    if (dst) {
	u_long dest = ntohl(dst->in.sin_addr.s_addr);

	/* Really need some sort of binary search */
	do {
	    if (dest < (u_long) ntohl((*rtp)->rt_dest.in.sin_addr.s_addr)) {
		break;
	    }
	} while (*(++rtp));
    }
    return (*rtp);
}

#endif				/* defined(AGENT_SNMP) */


/*
 *	Dump routing table to dump file
 */
static void
rt_dump(fd)
FILE *fd;
{
    rt_entry *rt;
    rt_head *rth;

    /* Dump the control info */
    control_dump(fd);

    /*
     * Dump the static gateways
     */
    if (rt_gw_list) {
	(void) fprintf(fd,
		       "Gateways referenced by static routes:\n");
	gw_dump(fd,
		"\t\t",
		rt_gw_list);
    }
    (void) fprintf(fd, "Redirects: %s\n",
		   ignore_redirects ? "off" : "on");
    (void) fprintf(fd, "\tPreference: %d\n",
		   redirect_preference);
    if (redirect_gw_list) {
	(void) fprintf(fd, "\tActive gateways:\n");
	gw_dump(fd, "\t\t", redirect_gw_list);
    }
    control_accept_dump(fd, 1, redirect_accept_list, redirect_int_accept, redirect_gw_list);
    (void) fprintf(fd, "\n\n");

    /* Print our AS */
    if (my_system) {
	(void) fprintf(fd, "Autonomous system:\t%u\n",
		       my_system);
    }
    /*
     * Dump all the routing information
     */
    (void) fprintf(fd,
	    "\n\nRouting Tables:\n\tInstall: %s\tGenerate Default: %s\n",
		   install ? "yes" : "no",
		   rt_default_needed ? "yes" : "no");
    (void) fprintf(fd,
		   "\tRevision: %lu\n",
		   rt_revision);
    (void) fprintf(fd,
		   "\tHashsize: %d\t\tHashmask: %04x\n",
		   ROUTEHASHSIZ,
		   ROUTEHASHMASK);
    (void) fprintf(fd,
		   "\tEntries:\t%u nets\t%u hosts\n\n",
		   rt_net_routes,
		   rt_host_routes);

    RT_SCTBL RT_BUCKET(rth) {
	(void) fprintf(fd,
		  "\t%-15A\tmask %-15A\thash %u\tentries %d\tstate %s\n",
		       &rth->rth_dest,
		       &rth->rth_dest_mask,
		       rth->rth_hash,
		       rth->rth_entries,
		       trace_bits(rt_state_bits, rth->rth_state));
	RT_ALLRT(rt, rth) {
	    (void) fprintf(fd,
			   "\t\t%c%s\tPreference: %3d\n",
			   (rth->rt_active == rt) ? '*' : ' ',
			   trace_bits(rt_proto_bits, rt->rt_proto),
			   rt->rt_preference);

	    (void) fprintf(fd,
			   "\t\t\tGateway: %-15A\tInterface: %s\n",
			   &rt->rt_router,
			   rt->rt_ifp->int_name);

	    (void) fprintf(fd,
			   "\t\t\tFlags: <%s>",
			   trace_bits(rt_flag_bits, rt->rt_flags));
	    (void) fprintf(fd,
			   "\tState: <%s>\n",
			   trace_bits(rt_state_bits, rt->rt_state));

	    if (rt->rt_as) {
		(void) fprintf(fd,
			       "\t\t\tAS: %5u\n",
			       rt->rt_as);
	    }
	    if (!(rt->rt_state & RTS_NOAGE)) {
		(void) fprintf(fd,
			       "\t\t\tAge: %#T\tMax Age: %#T\n",
			       rt->rt_timer,
			       rt->rt_timer_max);
	    }
	    (void) fprintf(fd,
			   "\t\t\tMetric: %d",
			   rt->rt_metric);
	    (void) fprintf(fd,
			   "\tRevision: %lu\n",
			   rt->rt_revision);
	    /* Format protocol specific data */
	    if (rt->rt_data && rt->rt_data->rtd_dump) {
		rt->rt_data->rtd_dump(fd, rt);
	    }
	    (void) fprintf(fd, "\n");
	} RT_ALLRT_END(rt, rth);
	(void) fprintf(fd, "\n");
    } RT_BUCKET_END(rth) RT_SCTBL_END;
}


/*
 *	In preparation for a re-parse, reset the NOAGE flags on static routes
 *	so they will be deleted if they are not refreshed.
 */
/*ARGSUSED*/
static void
rt_cleanup(tp)
task *tp;
{
    int changes = 0;
    rt_entry *rt = (rt_entry *) 0;

    rt_open(rt_task);

    RT_WHOLE(rt) {
	if (rt->rt_proto & RTPROTO_STATIC) {
	    rt->rt_state &= ~RTS_NOAGE;
	    changes++;
	}
    } RT_WHOLEEND(rt);

    rt_default_cleanup();

    /* Reset defaults */
    ignore_redirects = FALSE;
    
    (void) rt_close(rt_task, (gw_entry *) 0, changes);

    adv_cleanup(&redirect_n_trusted, (int *) 0, redirect_gw_list,
		&redirect_accept_list, (adv_entry **) 0,
		&redirect_int_accept, (adv_entry ***) 0);
}


/*
 *	Delete any static routes that do not have the NOAGE flag.
 */
/*ARGSUSED*/
static void
rt_reinit(tp)
task *tp;
{
    int changes = 0;
    rt_entry *rt = (rt_entry *) 0;

    rt_open(rt_task);

    RT_WHOLE(rt) {
	if ((rt->rt_proto & RTPROTO_STATIC) && !(rt->rt_state & RTS_NOAGE)) {
	    (void) rt_unreach(rt);
	}
    } RT_WHOLEEND(rt);

    rt_default_reinit();
    
    (void) rt_close(rt_task, (gw_entry *) 0, changes);
}


 /*  Initialize the routing table.  The hash buckets are initilized	*/
 /*  with empty doubly linked list.  The last+1 entry is initilized to	*/
 /*  zero so the end of the list can be detected easily.		*/
 /*									*/
 /*  Also creates a timer and task for the job of aging the routing	*/
 /*  table 								*/
void
rt_init()
{
#ifdef	__HIGHC__
    rt_head *temp = (rt_head *) rt_inet_hash;

    rt_inet_hash[ROUTEHASHSIZ] = temp;
#else	/* __HIGHC__ */
    rt_inet_hash[ROUTEHASHSIZ] = (rt_head *) rt_inet_hash;
#endif	/* __HIGHC__ */

    rt_task = task_alloc("RT");
    rt_task->task_cleanup = rt_cleanup;
    rt_task->task_reinit = rt_reinit;
    rt_task->task_dump = rt_dump;
    if (!task_create(rt_task, 0)) {
	quit(EINVAL);
    }
    rt_timer = timer_create(rt_task, 0, "Age", 0, (time_t) RT_T_AGE, rt_time);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.