table.c revision 1f465d433636c5cd386100aeff14a785d9f8f846
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1983, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgment:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "defs.h"
#include <fcntl.h>
#include <stropts.h>
/* This structure is used to store a disassembled routing socket message. */
struct rt_addrinfo {
int rti_addrs;
};
static void set_need_flash(void);
char *, int);
/* Flash update needed. _B_TRUE to suppress the 1st. */
};
static uint32_t total_routes;
#define ROUNDUP_LONG(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
/*
* It is desirable to "aggregate" routes, to combine differing routes of
* the same metric and next hop into a common route with a smaller netmask
* or to suppress redundant routes, routes that add no information to
* routes with smaller netmasks.
*
* A route is redundant if and only if any and all routes with smaller
* but matching netmasks and nets are the same. Since routes are
* kept sorted in the radix tree, redundant routes always come second.
*
* There are two kinds of aggregations. First, two routes of the same bit
* mask and differing only in the least significant bit of the network
* number can be combined into a single route with a coarser mask.
*
* Second, a route can be suppressed in favor of another route with a more
* coarse mask provided no incompatible routes with intermediate masks
* are present. The second kind of aggregation involves suppressing routes.
* A route must not be suppressed if an incompatible route exists with
* an intermediate mask, since the suppressed route would be covered
* by the intermediate.
*
* This code relies on the radix tree walk encountering routes
* sorted first by address, with the smallest address first.
*/
#ifdef DEBUG_AG
acnt++; \
acnt++; \
if (acnt != NUM_AG_SLOTS) \
abort(); \
} while (_B_FALSE)
#else
#define CHECK_AG() (void)0
#endif
/*
* Output the contents of an aggregation table slot.
* This function must always be immediately followed with the deletion
* of the target slot.
*/
static void
{
/* Forget it if this route should not be output for split-horizon. */
return;
/*
* If we output both the even and odd twins, then the immediate parent,
* if it is present, is redundant, unless the parent manages to
* aggregate into something coarser.
* On successive calls, this code detects the even and odd twins,
* and marks the parent.
*
* Note that the order in which the radix tree code emits routes
* ensures that the twins are seen before the parent is emitted.
*/
AGS_REDUN0 : AGS_REDUN1);
}
/*
* Skip it if this route is itself redundant.
*
* It is ok to change the contents of the slot here, since it is
* always deleted next.
*/
return; /* quit if fully redundant */
/* make it finer if it is half-redundant */
/* make it finer if it is half-redundant */
}
}
static void
{
CHECK_AG();
else
else
CHECK_AG();
}
/* Look for a route that can suppress the given route. */
static struct ag_info *
{
/*
* We found a route with a coarser mask that covers
* the given target. It can suppress the target
* only if it has a good enough metric and it
* either has the same (gateway, ifp), or if its state
* includes AGS_CORS_GATE or the target's state
* includes AGS_FINE_GATE.
*/
return (ag_cors);
}
}
}
return (NULL);
}
/*
* Flush routes waiting for aggregation.
* This must not suppress a route unless it is known that among all routes
* with coarser masks that match it, the one with the longest mask is
* appropriate. This is ensured by scanning the routes in lexical order,
* and with the most restrictive mask first among routes to the same
* destination.
*/
void
{
/* Get the next route now, before we delete ag. */
/* Work on only the specified routes. */
continue;
/*
* Don't try to suppress the route if its state doesn't
* include AGS_SUPPRESS.
*/
continue;
}
/*
* We didn't find a route which suppresses the
* target, so the target can go out.
*/
} else {
/*
* We found a route which suppresses the target, so
* don't output the target.
*/
if (TRACEACTIONS) {
trace_misc("aggregated away %s",
trace_misc("on coarser route %s",
}
/*
* If the suppressed target was redundant, then
* mark the suppressor as redundant.
*/
else
}
}
/* The route has either been output or suppressed */
}
CHECK_AG();
}
/* Try to aggregate a route with previous routes. */
void
{
int tmp;
/*
* Don't bother trying to aggregate routes with non-contiguous
* subnet masks.
*
* (X & -X) contains a single bit if and only if X is a power of 2.
* (X + (X & -X)) == 0 if and only if X is a power of 2.
*/
return;
}
/* Search for the right slot in the aggregation table. */
ag = ag_corsest;
break;
/*
* Suppress old routes (i.e. combine with compatible routes
* with coarser masks) as we look for the right slot in the
* aggregation table for the new route.
* A route to an address less than the current destination
* will not be affected by the current route or any route
* seen hereafter. That means it is safe to suppress it.
* This check keeps poor routes (e.g. with large hop counts)
* from preventing suppression of finer routes.
*/
/*
* If the suppressed target was redundant,
* then mark the suppressor redundant.
*/
else
}
CHECK_AG();
} else {
}
}
/*
* masks and so forth are equal, we can aggregate them.
* We can probably promote one of the pair.
*
* Since the routes are encountered in lexical order,
* the new route must be odd. However, the second or later
* times around this loop, it could be the even twin promoted
*/
/*
* Here we know the target route and the route in the current
* slot have the same netmasks and differ by at most the
* last bit. They are either for the same destination, or
*/
/*
* We have two routes to the same destination,
* with the same nexthop and interface.
* Routes are encountered in lexical order,
* so a route is never promoted until the
* parent route is already present. So we
* know that the new route is a promoted (or
* aggregated) pair and the route already in
* the slot is the explicit route.
*
* Prefer the best route if their metrics
* differ, or the aggregated one if not,
* following a sort of longest-match rule.
*/
}
/*
* Some bits are set if they are set on
* either route, except when the route is
* for an interface.
*/
(state & (AGS_AGGREGATE_EITHER |
AGS_REDUN0 | AGS_REDUN1));
return;
} else {
/*
* both out.
*/
break;
}
}
/*
* If one of the routes can be promoted and the other can
* be suppressed, it may be possible to combine them or
* worthwhile to promote one.
*
* Any route that can be promoted is always
* marked to be eligible to be suppressed.
*/
if (!((state & AGS_AGGREGATE) &&
break;
/*
* if either is redundant, or if they are via the
* same gateway and have the same metric.
*/
/*
* We have both the even and odd pairs.
* Since the routes are encountered in order,
* the route in the slot must be the even twin.
*
* Combine and promote (aggregate) the pair of routes.
*/
if (!AG_IS_REDUN(state))
state &= ~AGS_REDUN1;
state |= AGS_REDUN0;
else
state &= ~AGS_REDUN0;
tag = 0;
nhop = 0;
/*
* Get rid of the even twin that was already
* in the slot.
*/
/*
* If we cannot combine the pair, maybe the route
* with the worse metric can be promoted.
*
* Promote the old, even twin, by giving its slot
* in the table to the new, odd twin.
*/
/*
* The promoted route is even-redundant only if the
* even twin was fully redundant. It is not
* odd-redundant because the odd-twin will still be
* in the table.
*/
if (!AG_IS_REDUN(tmp))
tmp &= ~AGS_REDUN0;
tmp &= ~AGS_REDUN1;
/* take the newest sequence number */
else
} else {
if (!(state & AGS_AGGREGATE))
break; /* cannot promote either twin */
/*
* Promote the new, odd twin by shaving its
* mask and address.
* The promoted route is odd-redundant only if the
* odd twin was fully redundant. It is not
* even-redundant because the even twin is still in
* the table.
*/
if (!AG_IS_REDUN(state))
state &= ~AGS_REDUN1;
state &= ~AGS_REDUN0;
else
}
mask <<= 1;
ag = ag_corsest;
break;
}
}
/*
* When we can no longer promote and combine routes,
* flush the old route in the target slot. Also flush
* any finer routes that we know will never be aggregated by
* the new route.
*
* In case we moved toward coarser masks,
* get back where we belong
*/
}
/* Empty the target slot */
}
#ifdef DEBUG_AG
abort();
abort();
abort();
abort();
CHECK_AG();
#endif
/* Save the new route on the end of the table. */
else
ag_corsest = nag;
else
CHECK_AG();
}
static const char *
{
static const char *rtm_types[] = {
"RTM_ADD",
"RTM_DELETE",
"RTM_CHANGE",
"RTM_GET",
"RTM_LOSING",
"RTM_REDIRECT",
"RTM_MISS",
"RTM_LOCK",
"RTM_OLDADD",
"RTM_OLDDEL",
"RTM_RESOLVE",
"RTM_NEWADDR",
"RTM_DELADDR",
"RTM_IFINFO",
"RTM_NEWMADDR",
"RTM_DELMADDR"
};
#define NEW_RTM_PAT "RTM type %#x"
return (name0);
} else {
}
}
static void
{
const char *mtype;
int i, j;
struct ifa_msghdr *ifam;
case RTM_NEWADDR:
case RTM_DELADDR:
mtype = "ifam";
break;
case RTM_IFINFO:
mtype = "ifm";
break;
default:
mtype = "rtm";
break;
}
}
case RTM_NEWADDR:
case RTM_DELADDR:
trace_misc("ifam: msglen %d version %d type %d addrs %X",
ifam->ifam_addrs);
trace_misc("ifam: flags %X index %d metric %d",
break;
case RTM_IFINFO:
trace_misc("ifm: msglen %d version %d type %d addrs %X",
} else {
}
break;
default:
trace_misc("rtm: msglen %d version %d type %d index %d",
trace_misc("rtm: flags %X addrs %X pid %d seq %d",
break;
}
while (i > 0) {
buffer[0] = '\0';
for (j = 0; j < 16 && i > 0; j++, i--)
}
}
/*
* Tell the kernel to add, delete or change a route
* Pass k_state from khash in for diagnostic info.
*/
static void
int flags)
{
static int rt_sock_seqno = 0;
struct {
struct sockaddr_in w_dst;
struct sockaddr_in w_gate;
} w;
struct sockaddr_in w_mask;
struct sockaddr_dl w_ifp;
long cc;
#define PAT " %-10s %s metric=%d flags=%#x"
(void) memset(&w, 0, sizeof (w));
}
} else {
}
} else {
trace_misc("ifindex %d is too big for sdl_index",
} else {
w.w_rtm.rtm_msglen +=
ROUNDUP_LONG(sizeof (struct sockaddr_dl));
}
}
if (!no_install) {
if (TRACERTS)
if (cc < 0) {
action == RTM_DELETE)) {
if (action == RTM_CHANGE) {
goto again;
}
return;
}
return;
return;
}
}
if (TRACEKERNEL)
}
/* Hash table containing our image of the kernel forwarding table. */
static struct khash *
{
break;
}
}
return (k);
}
/*
* Find out if there is an alternate route to a given destination
* off of a given interface.
*/
static struct khash *
{
break;
}
}
return (k);
}
static struct khash *
{
if (k != NULL)
return (k);
k = rtmalloc(sizeof (*k), "kern_add");
(void) memset(k, 0, sizeof (*k));
*pk = k;
return (k);
}
/* delete all khash entries that are wired through the interface ifp */
void
{
int i;
for (i = 0; i < KHASH_SIZE; i++) {
else
khash_bins[i] = k->k_next;
free(k);
continue;
}
kprev = k;
}
}
}
/*
* rewire khash entries that currently go through oldifp to
* go through newifp.
*/
void
{
struct khash *k;
int i;
for (i = 0; i < KHASH_SIZE; i++) {
for (k = khash_bins[i]; k; k = k->k_next) {
trace_misc("kern_rewire_ifp k 0x%lx "
}
}
}
}
/*
* Check that a static route it is still in the daemon table, and not
* deleted by interfaces coming and going. This is also the routine
* responsible for adding new static routes to the daemon table.
*/
static void
{
(k->k_state & KS_PRIVATE))
/*
* because we don't currently have a good
* way to compare metrics on static routes
* with rip metrics, and therefore cannot
* mix and match the two.
*/
return;
}
} else {
}
}
/* operate on a kernel entry */
static void
kern_ioctl(struct khash *k,
int action, /* RTM_DELETE, etc */
int flags)
{
(k->k_state & KS_DEPRE_IF)) {
/*
* Prevent execution of RTM_DELETE, RTM_ADD or
* RTM_CHANGE of interface routes
*/
trace_act("Blocking execution of %s %s --> %s ",
return;
}
switch (action) {
case RTM_DELETE:
k->k_state &= ~KS_DYNAMIC;
if (k->k_state & KS_DELETED)
return;
k->k_state |= KS_DELETED;
break;
case RTM_ADD:
k->k_state &= ~KS_DELETED;
break;
case RTM_CHANGE:
if (k->k_state & KS_DELETED) {
k->k_state &= ~KS_DELETED;
}
break;
}
}
/* add a route the kernel told us */
static void
struct rt_addrinfo *info,
{
struct khash *k;
static struct msg_limit msg_no_ifp;
} else {
return;
}
/*
* Find the interface toward the gateway.
*/
"route %s --> %s nexthop is not directly connected",
naddr_ntoa(gate));
}
}
trace_act("note %s without gateway",
k->k_metric = HOPCNT_INFINITY;
trace_act("note %s with gateway AF=%d",
k->k_metric = HOPCNT_INFINITY;
} else {
if (k->k_metric < 0)
k->k_metric = 0;
}
k->k_state |= KS_DEPRE_IF;
else
}
k->k_state |= KS_GATEWAY;
k->k_state |= KS_PRIVATE;
if (INFO_AUTHOR(info) != 0 &&
else
/*
* Routers are not supposed to listen to redirects,
* so delete it if it came via an unknown interface
* or the interface does not have special permission.
*/
k->k_state &= ~KS_DYNAMIC;
trace_act("mark for deletion redirected %s --> %s"
" via %s",
naddr_ntoa(k->k_gate),
} else {
k->k_state |= KS_DYNAMIC;
trace_act("accept redirected %s --> %s via %s",
naddr_ntoa(k->k_gate),
}
return;
}
/*
* If it is not a static route, quit until the next comparison
* between the kernel and daemon tables, when it will be deleted.
*/
return;
}
/*
* Put static routes with real metrics into the daemon table so
* they can be advertised.
*/
kern_check_static(k, ifp);
}
/* deal with packet loss */
static void
{
int i, spares;
trace_act("ignore %s without gateway",
age(0);
return;
}
spares = 0;
for (i = 0; i < rt->rt_num_spares; i++) {
losing_rts = rts;
continue;
}
spares++;
}
}
trace_act("Ignore RTM_LOSING because no route found"
" for %s through %s",
return;
}
if (spares == 0) {
trace_act("Got RTM_LOSING, but no alternatives to gw %s."
" deprecating route to metric 15",
new = *losing_rts;
return;
}
if (rdisc_ok)
}
/*
* Make the gateway slot of an info structure point to something
* useful. If it is not already useful, but it specifies an interface,
* then fill in the sockaddr_in provided and point it there.
*/
static int
{
return (0);
return (1);
return (0);
return (0);
/* LINTED */
return (1);
}
/*
* Clean the kernel table by copying it to the daemon image.
* Eventually the daemon will delete any extra routes.
*/
void
sync_kern(void)
{
int i;
struct khash *k;
struct {
struct T_optmgmt_req req;
} req;
union {
struct T_optmgmt_ack ack;
unsigned char space[64];
} ack;
struct rt_addrinfo info;
struct sockaddr_in sin_dst;
struct sockaddr_in sin_gate;
struct sockaddr_in sin_mask;
struct sockaddr_in sin_author;
for (i = 0; i < KHASH_SIZE; i++) {
}
}
if (ipfd == -1) {
goto hash_clean;
}
goto hash_clean;
}
for (;;) {
flags = 0;
if (r == -1) {
goto hash_clean;
}
msglog("bad T_OPTMGMT response; len=%d prim=%d "
goto hash_clean;
}
/* LINTED */
break;
}
while (r == MOREDATA) {
}
continue;
}
break;
}
/* LINTED */
/* LINTED */
/* LINTED */
/* LINTED */
for (;;) {
/*
* Ignore IRE cache, broadcast, and local address
* entries; they're not subject to routing socket
* control.
*/
continue;
/* ignore multicast and link local addresses */
continue;
}
#ifdef DEBUG_KERNEL_ROUTE_READ
#endif
/* Fake up the needed entries */
sizeof (ifname));
/*
* First try to match up on gwkludge entries
*/
}
#ifdef DEBUG_KERNEL_ROUTE_READ
}
#endif
/*
* Note static routes and interface routes, and also
* preload the image of the kernel table so that
* we can later clean it, as well as avoid making
* unneeded changes. Keep the old kernel routes for a
* few seconds to allow a RIP or router-discovery
* response to be heard.
*/
}
if (r == 0) {
break;
}
}
if (ipfd != -1)
for (i = 0; i < KHASH_SIZE; i++) {
/*
* KS_DELETED routes have been removed from the
* kernel, but we keep them around for reasons
* stated in del_static(), so we skip the check
* for KS_DELETED routes here.
*/
if (!(k->k_state & KS_DYNAMIC)) {
"%s --> %s disappeared from kernel",
naddr_ntoa(k->k_gate));
}
k->k_ifp, 1);
}
}
}
}
/* Listen to announcements from the kernel */
void
read_rt(void)
{
long cc;
struct sockaddr_in gate_sin;
union {
struct {
} r;
} m;
struct rt_addrinfo info;
for (;;) {
if (cc <= 0) {
LOGERR("read(rt_sock)");
return;
}
if (TRACERTS)
msglog("routing message truncated (%d < %d)",
}
msglog("bogus routing message version %d",
m.r.rtm.rtm_version);
continue;
}
}
ifname);
trace_act("note %s with flags %s"
" for unknown interface index #%d",
} else {
trace_act("note %s with flags %s"
" for unknown interface %s",
}
} else {
trace_act("note %s with flags %s for %s",
}
/*
* After being informed of a change to an interface,
* check them all now if the check would otherwise
* be a long time from now, if the interface is
* not known, or if the interface has been turned
* off or on.
*/
IFF_UP) != 0)
continue;
} else {
}
sizeof (str));
/* LINTED */
continue;
}
continue;
}
continue;
}
continue;
}
} else {
gate = 0;
}
if (INFO_AUTHOR(&info) != 0)
" by authority of %s",
case RTM_ADD:
case RTM_CHANGE:
case RTM_REDIRECT:
trace_act("ignore %s with \"%s\" error",
} else {
}
break;
case RTM_DELETE:
trace_act("ignore %s with \"%s\" error",
} else {
}
break;
case RTM_LOSING:
break;
default:
break;
}
}
}
/*
* Disassemble a routing message. The result is an array of pointers
* to sockaddr_storage structures stored in the info argument.
*
* ss is a pointer to the beginning of the data following the
* rt_msghdr contained in the routing socket message, which consists
* of a string of concatenated sockaddr structure of different types.
*
* Extended attributes can be appended at the end of the list.
*/
static int
struct sockaddr_storage *ss,
char *lim,
int addrs)
{
int retv = 0;
int i;
int abit;
int complaints;
static int prev_complaints;
#define XBAD_AF 0x1
#define XBAD_SHORT 0x2
#define XBAD_LONG 0x4
complaints = 0;
i++, abit <<= 1) {
continue;
/* Horrible interface here */
case AF_UNIX:
/* LINTED */
ss = (struct sockaddr_storage *)(
break;
case AF_INET:
/* LINTED */
ss = (struct sockaddr_storage *)(
break;
case AF_LINK:
/* LINTED */
ss = (struct sockaddr_storage *)(
break;
case AF_INET6:
/* LINTED */
ss = (struct sockaddr_storage *)(
break;
default:
if (!(prev_complaints & XBAD_AF))
"unknown address family %d "
if (complaints & XBAD_AF)
goto xaddr_done;
/* LINTED */
ss = (struct sockaddr_storage *)(
complaints |= XBAD_AF;
retv = -1;
break;
}
if (!(prev_complaints & XBAD_SHORT))
msglog("sockaddr %d too short by %d "
complaints |= XBAD_SHORT;
retv = -1;
goto xaddr_done;
}
}
char *nxt;
/* LINTED: alignment */
break;
}
/* LINTED: alignment */
}
if (!(prev_complaints & XBAD_SHORT))
msglog("routing message too short by %d bytes",
complaints |= XBAD_SHORT;
} else if (!(prev_complaints & XBAD_LONG)) {
msglog("%d bytes of routing message left over",
complaints |= XBAD_LONG;
}
retv = -1;
}
return (retv);
}
/* after aggregating, note routes that belong in the kernel */
static void
{
struct khash *k;
/*
* Do not install bad routes if they are not already present.
* This includes routes that had RS_NET_SYN for interfaces that
* recently died.
*/
if (k == NULL)
return;
} else {
ifp);
}
/* will need to add new entry to the kernel table */
k->k_state |= KS_GATEWAY;
k->k_state |= KS_PASSIVE;
return;
}
return;
}
/* modify existing kernel entry if necessary */
/*
* Must delete bad interface routes etc.
* to change them.
*/
if (k->k_metric == HOPCNT_INFINITY)
k->k_state |= KS_DEL_ADD;
}
/*
* If the daemon thinks the route should exist, forget
* about any redirections.
* If the daemon thinks the route should exist, eventually
* override manual intervention by the operator.
*/
k->k_state &= ~KS_DYNAMIC;
}
k->k_state &= ~KS_GATEWAY;
k->k_state |= KS_GATEWAY;
}
/*
* Deleting-and-adding is necessary to change aspects of a route.
* Just delete instead of deleting and then adding a bad route.
* Otherwise, we want to keep the route in the kernel.
*/
else
}
/*
* Update our image of the kernel forwarding table using the given
* route from our internal routing table.
*/
/*ARGSUSED1*/
static int
{
int i;
/* Do not install synthetic routes */
return (0);
/*
* Do not install static routes here. Only
* read_rt->rtm_add->kern_add should install those
*/
return (0);
/* Do not clobber kernel if this is a route for a dead interface */
return (0);
/* This is an ordinary route, not for an interface. */
/*
* aggregate, ordinary good routes without regard to
* their metric
*/
pref = 1;
/*
* Do not install host routes directly to hosts, to avoid
* interfering with ARP entries in the kernel table.
*/
return (0);
} else {
/*
* This is an interface route.
* Do not install routes for "external" remote interfaces.
*/
return (0);
/* Interfaces should override received routes. */
pref = 0;
IS_PASSIVE) {
ags |= AGS_PASSIVE;
}
/*
* If it is not an interface, or an alias for an interface,
* it must be a "gateway."
*
* If it is a "remote" interface, it is also a "gateway" to
* the kernel if is not a alias.
*/
/*
* Do not aggregate IS_PASSIVE routes.
*/
ags |= AGS_AGGREGATE;
}
}
if (metric == HOPCNT_INFINITY) {
/* If the route is dead, try hard to aggregate. */
}
/*
* dump all routes that have the same metric as rt_spares[0]
* into the kern_table, to be added to the kernel.
*/
for (i = 0; i < RT->rt_num_spares; i++) {
/* Do not install external routes */
continue;
kern_out);
}
}
return (0);
}
/* Update the kernel table to match the daemon table. */
static void
fix_kern(void)
{
int i;
/* Walk daemon table, updating the copy of the kernel table. */
for (i = 0; i < KHASH_SIZE; i++) {
/* Do not touch local interface routes */
if ((k->k_state & KS_DEPRE_IF) ||
pk = k;
continue;
}
/* Do not touch static routes */
kern_check_static(k, 0);
pk = k;
continue;
}
/* check hold on routes deleted by the operator */
/* ensure we check when the hold is over */
pk = k;
continue;
}
!(k->k_state & KS_DYNAMIC)) {
if ((k->k_dst == RIP_DEFAULT) &&
rdisc_restore(k->k_ifp);
kern_ioctl(k, RTM_DELETE, 0);
else
khash_bins[i] = knext;
free(k);
continue;
}
if (k->k_state & KS_DEL_ADD)
kern_ioctl(k, RTM_DELETE, 0);
if ((k->k_dst == RIP_DEFAULT) &&
rdisc_suppress(k->k_ifp);
kern_ioctl(k, RTM_ADD,
((0 != (k->k_state & (KS_GATEWAY |
KS_DYNAMIC))) ? RTF_GATEWAY : 0));
/*
* Should be using RTM_CHANGE here, but
* since RTM_CHANGE is currently
* not multipath-aware, and assumes
* that RTF_GATEWAY implies the gateway
* of the route for dst has to be
* changed, we play safe, and do a del + add.
*/
kern_ioctl(k, RTM_DELETE, 0);
kern_ioctl(k, RTM_ADD,
((0 != (k->k_state & (KS_GATEWAY |
KS_DYNAMIC))) ? RTF_GATEWAY : 0));
}
/*
* Mark this route to be deleted in the next cycle.
* This deletes routes that disappear from the
* daemon table, since the normal aging code
* will clear the bit for routes that have not
* disappeared from the daemon table.
*/
pk = k;
}
}
}
/* Delete a static route in the image of the kernel table. */
void
{
struct khash *k;
/*
* Just mark it in the table to be deleted next time the kernel
* table is updated.
* If it has already been deleted, mark it as such, and set its
* keep-timer so that it will not be deleted again for a while.
* This lets the operator delete a route added by the daemon
* and add a replacement.
*/
if (gone) {
k->k_state |= KS_DELETED;
}
}
}
/*
* Delete all routes generated from ICMP Redirects that use a given gateway,
* as well as old redirected routes.
*/
void
{
int i;
struct khash *k;
for (i = 0; i < KHASH_SIZE; i++) {
if (!(k->k_state & KS_DYNAMIC) ||
continue;
!dosupply)
continue;
k->k_state &= ~KS_DYNAMIC;
trace_act("mark redirected %s --> %s for deletion",
naddr_ntoa(k->k_gate));
}
}
}
/* Start the daemon tables. */
void
rtinit(void)
{
int i;
/* Initialize the radix trees */
rn_init();
/* mark all of the slots in the table free */
ag++;
}
}
static void
set_need_flash(void)
{
if (!need_flash) {
/*
* Do not send the flash update immediately. Wait a little
* while to hear from other routers.
*/
}
}
/* Get a particular routing table entry */
struct rt_entry *
{
return (NULL);
return (rt);
}
/* Find a route to dst as the kernel would. */
struct rt_entry *
{
}
/* add a route to the table */
void
{
int i;
/* This is the only function that increments total_routes. */
if (total_routes == MAX_ROUTES) {
return;
}
"rtadd");
}
if (TRACEACTIONS)
msglog("rnh_addaddr() failed for %s mask=%s",
}
total_routes++;
}
/* notice a changed route */
void
char *label)
{
/*
* Fix the kernel immediately if it seems the route
* has gone bad, since there may be a working route that
* aggregates this route.
*/
}
}
}
/* Keep various things from deciding ageless routes are stale. */
if (TRACEACTIONS)
/*
* If the interface state of the new primary route is good,
* turn off RS_BADIF flag
*/
}
/* check for a better route among the spares */
static struct rt_spare *
{
int i;
/* find the best alternative among the spares */
}
return (rts);
}
/* switch to a backup route */
void
{
char label[10];
/* Do not change permanent routes */
RS_NET_SYN | RS_IF)))
return;
/* find the best alternative among the spares */
/* Do not bother if it is not worthwhile. */
return;
} else {
}
}
void
{
struct rt_entry *deleted_rt;
int i;
if (TRACEACTIONS)
for (i = 0; i < rt->rt_num_spares; i++) {
}
if (rt != (deleted_rt =
rhead)))) {
msglog("rnh_deladdr(%s) failed; found rt 0x%lx",
if (deleted_rt != NULL)
}
total_routes--;
/*
* we just deleted the default route. Trigger rdisc_sort
* so that we can recover from any rdisc information that
* is valid
*/
rdisc_timer.tv_sec = 0;
}
}
void
{
struct khash *k;
if (k != NULL &&
!(k->k_state & KS_DEPRE_IF) &&
}
}
/*
* Get rid of a bad route, and try to switch to a replacement.
* If the route has gone bad because of a bad interface,
* the information about the dead interface is available in badifp
* for the purpose of sanity checks, if_flags checks etc.
*/
static void
{
/* Poison the route */
}
/*
* Dont mark the rtentry bad unless the ifp for the primary
* route is the bad ifp
*/
return;
/*
* badifp has just gone bad. We want to keep this
* rt_entry around so that we tell our rip-neighbors
* about the bad route, but we can't do anything
* to the kernel itself, so mark it as RS_BADIF
*/
}
}
/*
* Junk a RS_NET_SYN or RS_LOCAL route,
* unless it is needed by another interface.
*/
void
{
state = 0;
/*
* Is this the route through loopback for the interface?
* If so, see if it is used by any other interfaces, such
* as a point-to-point interface with the same local address.
*/
/* Retain it if another interface needs it. */
break;
}
}
}
/*
* Retain RIPv1 logical network route if there is another
* interface that justifies it.
*/
state |= RS_NET_SYN;
break;
}
}
}
/* or if there is an authority route that needs it. */
break;
}
}
}
&new, 0);
} else {
}
}
/*
* Called while walking the table looking for sick interfaces
* or after a time change.
*/
int
void *argp)
{
int i, j = -1;
/* fix any spare routes through the interface */
else {
j = i;
}
}
/*
* Deal with the main route
* finished if it has been handled before or if its interface is ok
*/
return (0);
/* Bad routes for other than interfaces are easy. */
if (j > 0) {
} else {
}
return (0);
}
return (0);
}
/*
* Called while walking the table to replace a duplicate interface
* with a backup.
*/
int
{
int i;
/* fix any spare routes through the interface */
/*
* If the main route is getting a worse metric,
* then it may be time to switch to a backup.
*/
}
}
}
return (0);
}
/* Check the age of an individual route. */
static int
{
int i;
/*
* age all of the spare routes, including the primary route
* currently in use
*/
if (i == RT->rt_num_spares) {
/*
* Keep various things from deciding ageless
* routes are stale
*/
continue;
}
/* forget RIP routes after RIP has been turned off. */
if (rip_sock < 0) {
}
}
/* age failing routes */
}
/* trash the spare routes when they go bad */
((rip_sock < 0) ||
i != RT->rt_num_spares) {
}
}
/* finished if the active route is still fresh */
return (0);
/* try to switch to an alternative */
/* Delete a dead route after it has been publically mourned. */
return (0);
}
/* Start poisoning a bad route before deleting it. */
}
return (0);
}
/* Watch for dead routes and interfaces. */
void
{
int need_query = 0;
/*
* If not listening to RIP, there is no need to age the routes in
* the table.
*/
/*
* Check for dead IS_REMOTE interfaces by timing their
* transmissions.
*/
continue;
/* ignore unreachable remote interfaces */
if (!check_remote(ifp))
continue;
/* Restore remote interface that has become reachable */
"remote interface %s to %s timed out after"
" %ld:%ld",
}
/*
* If we have not heard from the other router
* recently, ask it.
*/
need_query = 1;
}
}
/* Age routes. */
/*
* delete old redirected routes to keep the kernel table small
* and prevent blackholes
*/
/* Update the kernel routing table. */
fix_kern();
/* poke reticent remote gateways */
if (need_query)
rip_query();
}
void
kern_dump(void)
{
int i;
struct khash *k;
for (i = 0; i < KHASH_SIZE; i++) {
trace_khash(k);
}
}
static struct interface *
{
continue;
return (ifp);
}
return (NULL);
}
/*
* Lookup logical interface structure given the gateway address.
* Returns null if no interfaces match the given name.
*/
static struct interface *
{
struct physical_interface *phyi;
return (NULL);
#ifdef DEBUG_KERNEL_ROUTE_READ
" %-4s %-4s %-15s-->%-15s \n",
#endif
/* Exact match found */
return (ifp);
}
/* No exact match found but return any best match found */
return (best);
}