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.