17c478bd9Sstevel@tonic-gate /*
2d6b3210dSsowmini  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  *
57c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1988, 1993
67c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
97c478bd9Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
107c478bd9Sstevel@tonic-gate  * are met:
117c478bd9Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
127c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
137c478bd9Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
147c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
157c478bd9Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
167c478bd9Sstevel@tonic-gate  * 3. All advertising materials mentioning features or use of this software
177c478bd9Sstevel@tonic-gate  *    must display the following acknowledgment:
187c478bd9Sstevel@tonic-gate  *	This product includes software developed by the University of
197c478bd9Sstevel@tonic-gate  *	California, Berkeley and its contributors.
207c478bd9Sstevel@tonic-gate  * 4. Neither the name of the University nor the names of its contributors
217c478bd9Sstevel@tonic-gate  *    may be used to endorse or promote products derived from this software
227c478bd9Sstevel@tonic-gate  *    without specific prior written permission.
237c478bd9Sstevel@tonic-gate  *
247c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
257c478bd9Sstevel@tonic-gate  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
267c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
277c478bd9Sstevel@tonic-gate  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
287c478bd9Sstevel@tonic-gate  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
297c478bd9Sstevel@tonic-gate  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
307c478bd9Sstevel@tonic-gate  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
317c478bd9Sstevel@tonic-gate  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
327c478bd9Sstevel@tonic-gate  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
337c478bd9Sstevel@tonic-gate  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
347c478bd9Sstevel@tonic-gate  * SUCH DAMAGE.
357c478bd9Sstevel@tonic-gate  *
367c478bd9Sstevel@tonic-gate  * $FreeBSD: src/sbin/routed/table.c,v 1.15 2000/08/11 08:24:38 sheldonh Exp $
377c478bd9Sstevel@tonic-gate  */
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #include "defs.h"
427c478bd9Sstevel@tonic-gate #include <fcntl.h>
437c478bd9Sstevel@tonic-gate #include <stropts.h>
447c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
457c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
467c478bd9Sstevel@tonic-gate #include <inet/ip.h>
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate /* This structure is used to store a disassembled routing socket message. */
497c478bd9Sstevel@tonic-gate struct rt_addrinfo {
507c478bd9Sstevel@tonic-gate 	int	rti_addrs;
517c478bd9Sstevel@tonic-gate 	struct sockaddr_storage *rti_info[RTAX_MAX];
527c478bd9Sstevel@tonic-gate };
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate static struct rt_spare *rts_better(struct rt_entry *);
557c478bd9Sstevel@tonic-gate static struct rt_spare rts_empty = EMPTY_RT_SPARE;
567c478bd9Sstevel@tonic-gate static void set_need_flash(void);
577c478bd9Sstevel@tonic-gate static void rtbad(struct rt_entry *, struct interface *);
587c478bd9Sstevel@tonic-gate static int rt_xaddrs(struct rt_addrinfo *, struct sockaddr_storage *,
597c478bd9Sstevel@tonic-gate     char *, int);
607c478bd9Sstevel@tonic-gate static struct interface *gwkludge_iflookup(in_addr_t, in_addr_t, in_addr_t);
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate struct radix_node_head *rhead;		/* root of the radix tree */
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /* Flash update needed.  _B_TRUE to suppress the 1st. */
657c478bd9Sstevel@tonic-gate boolean_t need_flash = _B_TRUE;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate struct timeval age_timer;		/* next check of old routes */
687c478bd9Sstevel@tonic-gate struct timeval need_kern = {		/* need to update kernel table */
697c478bd9Sstevel@tonic-gate 	EPOCH+MIN_WAITTIME-1, 0
707c478bd9Sstevel@tonic-gate };
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate static uint32_t	total_routes;
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate #define	ROUNDUP_LONG(a) \
757c478bd9Sstevel@tonic-gate 	((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate /*
787c478bd9Sstevel@tonic-gate  * It is desirable to "aggregate" routes, to combine differing routes of
797c478bd9Sstevel@tonic-gate  * the same metric and next hop into a common route with a smaller netmask
807c478bd9Sstevel@tonic-gate  * or to suppress redundant routes, routes that add no information to
817c478bd9Sstevel@tonic-gate  * routes with smaller netmasks.
827c478bd9Sstevel@tonic-gate  *
837c478bd9Sstevel@tonic-gate  * A route is redundant if and only if any and all routes with smaller
847c478bd9Sstevel@tonic-gate  * but matching netmasks and nets are the same.  Since routes are
857c478bd9Sstevel@tonic-gate  * kept sorted in the radix tree, redundant routes always come second.
867c478bd9Sstevel@tonic-gate  *
877c478bd9Sstevel@tonic-gate  * There are two kinds of aggregations.  First, two routes of the same bit
887c478bd9Sstevel@tonic-gate  * mask and differing only in the least significant bit of the network
897c478bd9Sstevel@tonic-gate  * number can be combined into a single route with a coarser mask.
907c478bd9Sstevel@tonic-gate  *
917c478bd9Sstevel@tonic-gate  * Second, a route can be suppressed in favor of another route with a more
927c478bd9Sstevel@tonic-gate  * coarse mask provided no incompatible routes with intermediate masks
937c478bd9Sstevel@tonic-gate  * are present.  The second kind of aggregation involves suppressing routes.
947c478bd9Sstevel@tonic-gate  * A route must not be suppressed if an incompatible route exists with
957c478bd9Sstevel@tonic-gate  * an intermediate mask, since the suppressed route would be covered
967c478bd9Sstevel@tonic-gate  * by the intermediate.
977c478bd9Sstevel@tonic-gate  *
987c478bd9Sstevel@tonic-gate  * This code relies on the radix tree walk encountering routes
997c478bd9Sstevel@tonic-gate  * sorted first by address, with the smallest address first.
1007c478bd9Sstevel@tonic-gate  */
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate static struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest,
1037c478bd9Sstevel@tonic-gate 	*ag_finest;
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate #ifdef DEBUG_AG
1067c478bd9Sstevel@tonic-gate #define	CHECK_AG() do { int acnt = 0; struct ag_info *cag;	\
1077c478bd9Sstevel@tonic-gate 	for (cag = ag_avail; cag != NULL; cag = cag->ag_fine)	\
1087c478bd9Sstevel@tonic-gate 		acnt++;						\
1097c478bd9Sstevel@tonic-gate 	for (cag = ag_corsest; cag != NULL; cag = cag->ag_fine)	\
1107c478bd9Sstevel@tonic-gate 		acnt++;						\
1117c478bd9Sstevel@tonic-gate 	if (acnt != NUM_AG_SLOTS)				\
1127c478bd9Sstevel@tonic-gate 		abort();					\
1137c478bd9Sstevel@tonic-gate } while (_B_FALSE)
1147c478bd9Sstevel@tonic-gate #else
1157c478bd9Sstevel@tonic-gate #define	CHECK_AG()	(void)0
1167c478bd9Sstevel@tonic-gate #endif
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate /*
1207c478bd9Sstevel@tonic-gate  * Output the contents of an aggregation table slot.
1217c478bd9Sstevel@tonic-gate  *	This function must always be immediately followed with the deletion
1227c478bd9Sstevel@tonic-gate  *	of the target slot.
1237c478bd9Sstevel@tonic-gate  */
1247c478bd9Sstevel@tonic-gate static void
1257c478bd9Sstevel@tonic-gate ag_out(struct ag_info *ag, void (*out)(struct ag_info *))
1267c478bd9Sstevel@tonic-gate {
1277c478bd9Sstevel@tonic-gate 	struct ag_info *ag_cors;
1287c478bd9Sstevel@tonic-gate 	uint32_t bit;
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	/* Forget it if this route should not be output for split-horizon. */
1327c478bd9Sstevel@tonic-gate 	if (ag->ag_state & AGS_SPLIT_HZ)
1337c478bd9Sstevel@tonic-gate 		return;
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	/*
1367c478bd9Sstevel@tonic-gate 	 * If we output both the even and odd twins, then the immediate parent,
1377c478bd9Sstevel@tonic-gate 	 * if it is present, is redundant, unless the parent manages to
1387c478bd9Sstevel@tonic-gate 	 * aggregate into something coarser.
1397c478bd9Sstevel@tonic-gate 	 * On successive calls, this code detects the even and odd twins,
1407c478bd9Sstevel@tonic-gate 	 * and marks the parent.
1417c478bd9Sstevel@tonic-gate 	 *
1427c478bd9Sstevel@tonic-gate 	 * Note that the order in which the radix tree code emits routes
1437c478bd9Sstevel@tonic-gate 	 * ensures that the twins are seen before the parent is emitted.
1447c478bd9Sstevel@tonic-gate 	 */
1457c478bd9Sstevel@tonic-gate 	ag_cors = ag->ag_cors;
1467c478bd9Sstevel@tonic-gate 	if (ag_cors != NULL &&
1477c478bd9Sstevel@tonic-gate 	    ag_cors->ag_mask == (ag->ag_mask << 1) &&
1487c478bd9Sstevel@tonic-gate 	    ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) {
1497c478bd9Sstevel@tonic-gate 		ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h) ?
1507c478bd9Sstevel@tonic-gate 		    AGS_REDUN0 : AGS_REDUN1);
1517c478bd9Sstevel@tonic-gate 	}
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	/*
1547c478bd9Sstevel@tonic-gate 	 * Skip it if this route is itself redundant.
1557c478bd9Sstevel@tonic-gate 	 *
1567c478bd9Sstevel@tonic-gate 	 * It is ok to change the contents of the slot here, since it is
1577c478bd9Sstevel@tonic-gate 	 * always deleted next.
1587c478bd9Sstevel@tonic-gate 	 */
1597c478bd9Sstevel@tonic-gate 	if (ag->ag_state & AGS_REDUN0) {
1607c478bd9Sstevel@tonic-gate 		if (ag->ag_state & AGS_REDUN1)
1617c478bd9Sstevel@tonic-gate 			return;		/* quit if fully redundant */
1627c478bd9Sstevel@tonic-gate 		/* make it finer if it is half-redundant */
1637c478bd9Sstevel@tonic-gate 		bit = (-ag->ag_mask) >> 1;
1647c478bd9Sstevel@tonic-gate 		ag->ag_dst_h |= bit;
1657c478bd9Sstevel@tonic-gate 		ag->ag_mask |= bit;
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	} else if (ag->ag_state & AGS_REDUN1) {
1687c478bd9Sstevel@tonic-gate 		/* make it finer if it is half-redundant */
1697c478bd9Sstevel@tonic-gate 		bit = (-ag->ag_mask) >> 1;
1707c478bd9Sstevel@tonic-gate 		ag->ag_mask |= bit;
1717c478bd9Sstevel@tonic-gate 	}
1727c478bd9Sstevel@tonic-gate 	out(ag);
1737c478bd9Sstevel@tonic-gate }
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate static void
1777c478bd9Sstevel@tonic-gate ag_del(struct ag_info *ag)
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate 	CHECK_AG();
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	if (ag->ag_cors == NULL)
1827c478bd9Sstevel@tonic-gate 		ag_corsest = ag->ag_fine;
1837c478bd9Sstevel@tonic-gate 	else
1847c478bd9Sstevel@tonic-gate 		ag->ag_cors->ag_fine = ag->ag_fine;
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	if (ag->ag_fine == NULL)
1877c478bd9Sstevel@tonic-gate 		ag_finest = ag->ag_cors;
1887c478bd9Sstevel@tonic-gate 	else
1897c478bd9Sstevel@tonic-gate 		ag->ag_fine->ag_cors = ag->ag_cors;
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	ag->ag_fine = ag_avail;
1927c478bd9Sstevel@tonic-gate 	ag_avail = ag;
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	CHECK_AG();
1957c478bd9Sstevel@tonic-gate }
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate /* Look for a route that can suppress the given route. */
1997c478bd9Sstevel@tonic-gate static struct ag_info *
2007c478bd9Sstevel@tonic-gate ag_find_suppressor(struct ag_info *ag)
2017c478bd9Sstevel@tonic-gate {
2027c478bd9Sstevel@tonic-gate 	struct ag_info *ag_cors;
2037c478bd9Sstevel@tonic-gate 	in_addr_t dst_h = ag->ag_dst_h;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	for (ag_cors = ag->ag_cors; ag_cors != NULL;
2067c478bd9Sstevel@tonic-gate 	    ag_cors = ag_cors->ag_cors) {
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 		if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) {
2097c478bd9Sstevel@tonic-gate 			/*
2107c478bd9Sstevel@tonic-gate 			 * We found a route with a coarser mask that covers
2117c478bd9Sstevel@tonic-gate 			 * the given target.  It can suppress the target
2127c478bd9Sstevel@tonic-gate 			 * only if it has a good enough metric and it
2137c478bd9Sstevel@tonic-gate 			 * either has the same (gateway, ifp), or if its state
2147c478bd9Sstevel@tonic-gate 			 * includes AGS_CORS_GATE or the target's state
2157c478bd9Sstevel@tonic-gate 			 * includes AGS_FINE_GATE.
2167c478bd9Sstevel@tonic-gate 			 */
2177c478bd9Sstevel@tonic-gate 			if (ag_cors->ag_pref <= ag->ag_pref &&
2187c478bd9Sstevel@tonic-gate 			    (((ag->ag_nhop == ag_cors->ag_nhop) &&
2197c478bd9Sstevel@tonic-gate 			    (ag->ag_ifp == ag_cors->ag_ifp)) ||
2207c478bd9Sstevel@tonic-gate 			    ag_cors->ag_state & AGS_CORS_GATE ||
2217c478bd9Sstevel@tonic-gate 			    ag->ag_state & AGS_FINE_GATE)) {
2227c478bd9Sstevel@tonic-gate 				return (ag_cors);
2237c478bd9Sstevel@tonic-gate 			}
2247c478bd9Sstevel@tonic-gate 		}
2257c478bd9Sstevel@tonic-gate 	}
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	return (NULL);
2287c478bd9Sstevel@tonic-gate }
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate /*
2327c478bd9Sstevel@tonic-gate  * Flush routes waiting for aggregation.
2337c478bd9Sstevel@tonic-gate  * This must not suppress a route unless it is known that among all routes
2347c478bd9Sstevel@tonic-gate  * with coarser masks that match it, the one with the longest mask is
2357c478bd9Sstevel@tonic-gate  * appropriate.  This is ensured by scanning the routes in lexical order,
2367c478bd9Sstevel@tonic-gate  * and with the most restrictive mask first among routes to the same
2377c478bd9Sstevel@tonic-gate  * destination.
2387c478bd9Sstevel@tonic-gate  */
2397c478bd9Sstevel@tonic-gate void
2407c478bd9Sstevel@tonic-gate ag_flush(in_addr_t lim_dst_h,	/* flush routes to here */
2417c478bd9Sstevel@tonic-gate     in_addr_t lim_mask,		/* matching this mask */
2427c478bd9Sstevel@tonic-gate     void (*out)(struct ag_info *))
2437c478bd9Sstevel@tonic-gate {
2447c478bd9Sstevel@tonic-gate 	struct ag_info *ag, *ag_cors, *ag_supr;
2457c478bd9Sstevel@tonic-gate 	in_addr_t dst_h;
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	for (ag = ag_finest; ag != NULL && ag->ag_mask >= lim_mask;
2497c478bd9Sstevel@tonic-gate 	    ag = ag_cors) {
2507c478bd9Sstevel@tonic-gate 		/* Get the next route now, before we delete ag. */
2517c478bd9Sstevel@tonic-gate 		ag_cors = ag->ag_cors;
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 		/* Work on only the specified routes. */
2547c478bd9Sstevel@tonic-gate 		dst_h = ag->ag_dst_h;
2557c478bd9Sstevel@tonic-gate 		if ((dst_h & lim_mask) != lim_dst_h)
2567c478bd9Sstevel@tonic-gate 			continue;
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 		/*
2597c478bd9Sstevel@tonic-gate 		 * Don't try to suppress the route if its state doesn't
2607c478bd9Sstevel@tonic-gate 		 * include AGS_SUPPRESS.
2617c478bd9Sstevel@tonic-gate 		 */
2627c478bd9Sstevel@tonic-gate 		if (!(ag->ag_state & AGS_SUPPRESS)) {
2637c478bd9Sstevel@tonic-gate 			ag_out(ag, out);
2647c478bd9Sstevel@tonic-gate 			ag_del(ag);
2657c478bd9Sstevel@tonic-gate 			continue;
2667c478bd9Sstevel@tonic-gate 		}
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 		ag_supr = ag_find_suppressor(ag);
2697c478bd9Sstevel@tonic-gate 		if (ag_supr == NULL) {
2707c478bd9Sstevel@tonic-gate 			/*
2717c478bd9Sstevel@tonic-gate 			 * We didn't find a route which suppresses the
2727c478bd9Sstevel@tonic-gate 			 * target, so the target can go out.
2737c478bd9Sstevel@tonic-gate 			 */
2747c478bd9Sstevel@tonic-gate 			ag_out(ag, out);
2757c478bd9Sstevel@tonic-gate 		} else {
2767c478bd9Sstevel@tonic-gate 			/*
2777c478bd9Sstevel@tonic-gate 			 * We found a route which suppresses the target, so
2787c478bd9Sstevel@tonic-gate 			 * don't output the target.
2797c478bd9Sstevel@tonic-gate 			 */
2807c478bd9Sstevel@tonic-gate 			if (TRACEACTIONS) {
2817c478bd9Sstevel@tonic-gate 				trace_misc("aggregated away %s",
2827c478bd9Sstevel@tonic-gate 				    rtname(htonl(ag->ag_dst_h), ag->ag_mask,
2837c478bd9Sstevel@tonic-gate 				    ag->ag_nhop));
2847c478bd9Sstevel@tonic-gate 				trace_misc("on coarser route %s",
2857c478bd9Sstevel@tonic-gate 				    rtname(htonl(ag_supr->ag_dst_h),
2867c478bd9Sstevel@tonic-gate 				    ag_supr->ag_mask, ag_supr->ag_nhop));
2877c478bd9Sstevel@tonic-gate 			}
2887c478bd9Sstevel@tonic-gate 			/*
2897c478bd9Sstevel@tonic-gate 			 * If the suppressed target was redundant, then
2907c478bd9Sstevel@tonic-gate 			 * mark the suppressor as redundant.
2917c478bd9Sstevel@tonic-gate 			 */
2927c478bd9Sstevel@tonic-gate 			if (AG_IS_REDUN(ag->ag_state) &&
2937c478bd9Sstevel@tonic-gate 			    ag_supr->ag_mask == (ag->ag_mask<<1)) {
2947c478bd9Sstevel@tonic-gate 				if (ag_supr->ag_dst_h == dst_h)
2957c478bd9Sstevel@tonic-gate 					ag_supr->ag_state |= AGS_REDUN0;
2967c478bd9Sstevel@tonic-gate 				else
2977c478bd9Sstevel@tonic-gate 					ag_supr->ag_state |= AGS_REDUN1;
2987c478bd9Sstevel@tonic-gate 			}
2997c478bd9Sstevel@tonic-gate 			if (ag->ag_tag != ag_supr->ag_tag)
3007c478bd9Sstevel@tonic-gate 				ag_supr->ag_tag = 0;
3017c478bd9Sstevel@tonic-gate 			if (ag->ag_nhop != ag_supr->ag_nhop)
3027c478bd9Sstevel@tonic-gate 				ag_supr->ag_nhop = 0;
3037c478bd9Sstevel@tonic-gate 		}
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 		/* The route has either been output or suppressed */
3067c478bd9Sstevel@tonic-gate 		ag_del(ag);
3077c478bd9Sstevel@tonic-gate 	}
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	CHECK_AG();
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate /* Try to aggregate a route with previous routes. */
3147c478bd9Sstevel@tonic-gate void
3157c478bd9Sstevel@tonic-gate ag_check(in_addr_t dst,
3167c478bd9Sstevel@tonic-gate     in_addr_t	mask,
3177c478bd9Sstevel@tonic-gate     in_addr_t	gate,
3187c478bd9Sstevel@tonic-gate     struct interface *ifp,
3197c478bd9Sstevel@tonic-gate     in_addr_t	nhop,
3207c478bd9Sstevel@tonic-gate     uint8_t	metric,
3217c478bd9Sstevel@tonic-gate     uint8_t	pref,
3227c478bd9Sstevel@tonic-gate     uint32_t	seqno,
3237c478bd9Sstevel@tonic-gate     uint16_t	tag,
3247c478bd9Sstevel@tonic-gate     uint16_t	state,
3257c478bd9Sstevel@tonic-gate     void (*out)(struct ag_info *))	/* output using this */
3267c478bd9Sstevel@tonic-gate {
3277c478bd9Sstevel@tonic-gate 	struct ag_info *ag, *nag, *ag_cors;
3287c478bd9Sstevel@tonic-gate 	in_addr_t xaddr;
3297c478bd9Sstevel@tonic-gate 	int tmp;
3307c478bd9Sstevel@tonic-gate 	struct interface *xifp;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	dst = ntohl(dst);
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	/*
3357c478bd9Sstevel@tonic-gate 	 * Don't bother trying to aggregate routes with non-contiguous
3367c478bd9Sstevel@tonic-gate 	 * subnet masks.
3377c478bd9Sstevel@tonic-gate 	 *
3387c478bd9Sstevel@tonic-gate 	 * (X & -X) contains a single bit if and only if X is a power of 2.
3397c478bd9Sstevel@tonic-gate 	 * (X + (X & -X)) == 0 if and only if X is a power of 2.
3407c478bd9Sstevel@tonic-gate 	 */
3417c478bd9Sstevel@tonic-gate 	if ((mask & -mask) + mask != 0) {
3427c478bd9Sstevel@tonic-gate 		struct ag_info nc_ag;
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 		nc_ag.ag_dst_h = dst;
3457c478bd9Sstevel@tonic-gate 		nc_ag.ag_mask = mask;
3467c478bd9Sstevel@tonic-gate 		nc_ag.ag_gate = gate;
3477c478bd9Sstevel@tonic-gate 		nc_ag.ag_ifp = ifp;
3487c478bd9Sstevel@tonic-gate 		nc_ag.ag_nhop = nhop;
3497c478bd9Sstevel@tonic-gate 		nc_ag.ag_metric = metric;
3507c478bd9Sstevel@tonic-gate 		nc_ag.ag_pref = pref;
3517c478bd9Sstevel@tonic-gate 		nc_ag.ag_tag = tag;
3527c478bd9Sstevel@tonic-gate 		nc_ag.ag_state = state;
3537c478bd9Sstevel@tonic-gate 		nc_ag.ag_seqno = seqno;
3547c478bd9Sstevel@tonic-gate 		out(&nc_ag);
3557c478bd9Sstevel@tonic-gate 		return;
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	/* Search for the right slot in the aggregation table. */
3597c478bd9Sstevel@tonic-gate 	ag_cors = NULL;
3607c478bd9Sstevel@tonic-gate 	ag = ag_corsest;
3617c478bd9Sstevel@tonic-gate 	while (ag != NULL) {
3627c478bd9Sstevel@tonic-gate 		if (ag->ag_mask >= mask)
3637c478bd9Sstevel@tonic-gate 			break;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 		/*
3667c478bd9Sstevel@tonic-gate 		 * Suppress old routes (i.e. combine with compatible routes
3677c478bd9Sstevel@tonic-gate 		 * with coarser masks) as we look for the right slot in the
3687c478bd9Sstevel@tonic-gate 		 * aggregation table for the new route.
3697c478bd9Sstevel@tonic-gate 		 * A route to an address less than the current destination
3707c478bd9Sstevel@tonic-gate 		 * will not be affected by the current route or any route
3717c478bd9Sstevel@tonic-gate 		 * seen hereafter.  That means it is safe to suppress it.
3727c478bd9Sstevel@tonic-gate 		 * This check keeps poor routes (e.g. with large hop counts)
3737c478bd9Sstevel@tonic-gate 		 * from preventing suppression of finer routes.
3747c478bd9Sstevel@tonic-gate 		 */
3757c478bd9Sstevel@tonic-gate 		if (ag_cors != NULL && ag->ag_dst_h < dst &&
3767c478bd9Sstevel@tonic-gate 		    (ag->ag_state & AGS_SUPPRESS) &&
3777c478bd9Sstevel@tonic-gate 		    ag_cors->ag_pref <= ag->ag_pref &&
3787c478bd9Sstevel@tonic-gate 		    (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h &&
3797c478bd9Sstevel@tonic-gate 		    ((ag_cors->ag_nhop == ag->ag_nhop &&
3807c478bd9Sstevel@tonic-gate 		    (ag_cors->ag_ifp == ag->ag_ifp))||
381*f9aa3e1eSkcpoon 		    (ag->ag_state & AGS_FINE_GATE) ||
382*f9aa3e1eSkcpoon 		    (ag_cors->ag_state & AGS_CORS_GATE))) {
3837c478bd9Sstevel@tonic-gate 			/*
3847c478bd9Sstevel@tonic-gate 			 * If the suppressed target was redundant,
3857c478bd9Sstevel@tonic-gate 			 * then mark the suppressor redundant.
3867c478bd9Sstevel@tonic-gate 			 */
3877c478bd9Sstevel@tonic-gate 			if (AG_IS_REDUN(ag->ag_state) &&
3887c478bd9Sstevel@tonic-gate 			    ag_cors->ag_mask == (ag->ag_mask << 1)) {
3897c478bd9Sstevel@tonic-gate 				if (ag_cors->ag_dst_h == dst)
3907c478bd9Sstevel@tonic-gate 					ag_cors->ag_state |= AGS_REDUN0;
3917c478bd9Sstevel@tonic-gate 				else
3927c478bd9Sstevel@tonic-gate 					ag_cors->ag_state |= AGS_REDUN1;
3937c478bd9Sstevel@tonic-gate 			}
3947c478bd9Sstevel@tonic-gate 			if (ag->ag_tag != ag_cors->ag_tag)
3957c478bd9Sstevel@tonic-gate 				ag_cors->ag_tag = 0;
3967c478bd9Sstevel@tonic-gate 			if (ag->ag_nhop != ag_cors->ag_nhop)
3977c478bd9Sstevel@tonic-gate 				ag_cors->ag_nhop = 0;
3987c478bd9Sstevel@tonic-gate 			ag_del(ag);
3997c478bd9Sstevel@tonic-gate 			CHECK_AG();
4007c478bd9Sstevel@tonic-gate 		} else {
4017c478bd9Sstevel@tonic-gate 			ag_cors = ag;
4027c478bd9Sstevel@tonic-gate 		}
4037c478bd9Sstevel@tonic-gate 		ag = ag_cors->ag_fine;
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	/*
4077c478bd9Sstevel@tonic-gate 	 * If we find the even/odd twin of the new route, and if the
4087c478bd9Sstevel@tonic-gate 	 * masks and so forth are equal, we can aggregate them.
4097c478bd9Sstevel@tonic-gate 	 * We can probably promote one of the pair.
4107c478bd9Sstevel@tonic-gate 	 *
4117c478bd9Sstevel@tonic-gate 	 * Since the routes are encountered in lexical order,
4127c478bd9Sstevel@tonic-gate 	 * the new route must be odd.  However, the second or later
4137c478bd9Sstevel@tonic-gate 	 * times around this loop, it could be the even twin promoted
4147c478bd9Sstevel@tonic-gate 	 * from the even/odd pair of twins of the finer route.
4157c478bd9Sstevel@tonic-gate 	 */
4167c478bd9Sstevel@tonic-gate 	while (ag != NULL && ag->ag_mask == mask &&
4177c478bd9Sstevel@tonic-gate 	    ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) {
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 		/*
4207c478bd9Sstevel@tonic-gate 		 * Here we know the target route and the route in the current
4217c478bd9Sstevel@tonic-gate 		 * slot have the same netmasks and differ by at most the
4227c478bd9Sstevel@tonic-gate 		 * last bit.  They are either for the same destination, or
4237c478bd9Sstevel@tonic-gate 		 * for an even/odd pair of destinations.
4247c478bd9Sstevel@tonic-gate 		 */
4257c478bd9Sstevel@tonic-gate 		if (ag->ag_dst_h == dst) {
4267c478bd9Sstevel@tonic-gate 			if (ag->ag_nhop == nhop && ag->ag_ifp == ifp) {
4277c478bd9Sstevel@tonic-gate 				/*
4287c478bd9Sstevel@tonic-gate 				 * We have two routes to the same destination,
4297c478bd9Sstevel@tonic-gate 				 * with the same nexthop and interface.
4307c478bd9Sstevel@tonic-gate 				 * Routes are encountered in lexical order,
4317c478bd9Sstevel@tonic-gate 				 * so a route is never promoted until the
4327c478bd9Sstevel@tonic-gate 				 * parent route is already present.  So we
4337c478bd9Sstevel@tonic-gate 				 * know that the new route is a promoted (or
4347c478bd9Sstevel@tonic-gate 				 * aggregated) pair and the route already in
4357c478bd9Sstevel@tonic-gate 				 * the slot is the explicit route.
4367c478bd9Sstevel@tonic-gate 				 *
4377c478bd9Sstevel@tonic-gate 				 * Prefer the best route if their metrics
4387c478bd9Sstevel@tonic-gate 				 * differ, or the aggregated one if not,
4397c478bd9Sstevel@tonic-gate 				 * following a sort of longest-match rule.
4407c478bd9Sstevel@tonic-gate 				 */
4417c478bd9Sstevel@tonic-gate 				if (pref <= ag->ag_pref) {
4427c478bd9Sstevel@tonic-gate 					ag->ag_gate = gate;
4437c478bd9Sstevel@tonic-gate 					ag->ag_ifp = ifp;
4447c478bd9Sstevel@tonic-gate 					ag->ag_nhop = nhop;
4457c478bd9Sstevel@tonic-gate 					ag->ag_tag = tag;
4467c478bd9Sstevel@tonic-gate 					ag->ag_metric = metric;
4477c478bd9Sstevel@tonic-gate 					ag->ag_pref = pref;
4487c478bd9Sstevel@tonic-gate 					if (seqno > ag->ag_seqno)
4497c478bd9Sstevel@tonic-gate 						ag->ag_seqno = seqno;
4507c478bd9Sstevel@tonic-gate 					tmp = ag->ag_state;
4517c478bd9Sstevel@tonic-gate 					ag->ag_state = state;
4527c478bd9Sstevel@tonic-gate 					state = tmp;
4537c478bd9Sstevel@tonic-gate 				}
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 				/*
4567c478bd9Sstevel@tonic-gate 				 * Some bits are set if they are set on
4577c478bd9Sstevel@tonic-gate 				 * either route, except when the route is
4587c478bd9Sstevel@tonic-gate 				 * for an interface.
4597c478bd9Sstevel@tonic-gate 				 */
4607c478bd9Sstevel@tonic-gate 				if (!(ag->ag_state & AGS_IF))
4617c478bd9Sstevel@tonic-gate 					ag->ag_state |=
4627c478bd9Sstevel@tonic-gate 					    (state & (AGS_AGGREGATE_EITHER |
4637c478bd9Sstevel@tonic-gate 					    AGS_REDUN0 | AGS_REDUN1));
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 				return;
4667c478bd9Sstevel@tonic-gate 			} else {
4677c478bd9Sstevel@tonic-gate 				/*
4687c478bd9Sstevel@tonic-gate 				 * multiple routes to same dest/mask with
4697c478bd9Sstevel@tonic-gate 				 * differing gate nexthop/or ifp. Flush
4707c478bd9Sstevel@tonic-gate 				 * both out.
4717c478bd9Sstevel@tonic-gate 				 */
4727c478bd9Sstevel@tonic-gate 				break;
4737c478bd9Sstevel@tonic-gate 			}
4747c478bd9Sstevel@tonic-gate 		}
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 		/*
4777c478bd9Sstevel@tonic-gate 		 * If one of the routes can be promoted and the other can
4787c478bd9Sstevel@tonic-gate 		 * be suppressed, it may be possible to combine them or
4797c478bd9Sstevel@tonic-gate 		 * worthwhile to promote one.
4807c478bd9Sstevel@tonic-gate 		 *
4817c478bd9Sstevel@tonic-gate 		 * Any route that can be promoted is always
4827c478bd9Sstevel@tonic-gate 		 * marked to be eligible to be suppressed.
4837c478bd9Sstevel@tonic-gate 		 */
4847c478bd9Sstevel@tonic-gate 		if (!((state & AGS_AGGREGATE) &&
4857c478bd9Sstevel@tonic-gate 		    (ag->ag_state & AGS_SUPPRESS)) &&
4867c478bd9Sstevel@tonic-gate 		    !((ag->ag_state & AGS_AGGREGATE) && (state & AGS_SUPPRESS)))
4877c478bd9Sstevel@tonic-gate 			break;
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 		/*
4907c478bd9Sstevel@tonic-gate 		 * A pair of even/odd twin routes can be combined
4917c478bd9Sstevel@tonic-gate 		 * if either is redundant, or if they are via the
4927c478bd9Sstevel@tonic-gate 		 * same gateway and have the same metric.
4937c478bd9Sstevel@tonic-gate 		 */
4947c478bd9Sstevel@tonic-gate 		if (AG_IS_REDUN(ag->ag_state) || AG_IS_REDUN(state) ||
4957c478bd9Sstevel@tonic-gate 		    (ag->ag_nhop == nhop && ag->ag_ifp == ifp &&
4967c478bd9Sstevel@tonic-gate 		    ag->ag_pref == pref &&
4977c478bd9Sstevel@tonic-gate 		    (state & ag->ag_state & AGS_AGGREGATE) != 0)) {
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 			/*
5007c478bd9Sstevel@tonic-gate 			 * We have both the even and odd pairs.
5017c478bd9Sstevel@tonic-gate 			 * Since the routes are encountered in order,
5027c478bd9Sstevel@tonic-gate 			 * the route in the slot must be the even twin.
5037c478bd9Sstevel@tonic-gate 			 *
5047c478bd9Sstevel@tonic-gate 			 * Combine and promote (aggregate) the pair of routes.
5057c478bd9Sstevel@tonic-gate 			 */
5067c478bd9Sstevel@tonic-gate 			if (seqno < ag->ag_seqno)
5077c478bd9Sstevel@tonic-gate 				seqno = ag->ag_seqno;
5087c478bd9Sstevel@tonic-gate 			if (!AG_IS_REDUN(state))
5097c478bd9Sstevel@tonic-gate 				state &= ~AGS_REDUN1;
5107c478bd9Sstevel@tonic-gate 			if (AG_IS_REDUN(ag->ag_state))
5117c478bd9Sstevel@tonic-gate 				state |= AGS_REDUN0;
5127c478bd9Sstevel@tonic-gate 			else
5137c478bd9Sstevel@tonic-gate 				state &= ~AGS_REDUN0;
5147c478bd9Sstevel@tonic-gate 			state |= (ag->ag_state & AGS_AGGREGATE_EITHER);
5157c478bd9Sstevel@tonic-gate 			if (ag->ag_tag != tag)
5167c478bd9Sstevel@tonic-gate 				tag = 0;
5177c478bd9Sstevel@tonic-gate 			if (ag->ag_nhop != nhop)
5187c478bd9Sstevel@tonic-gate 				nhop = 0;
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 			/*
5217c478bd9Sstevel@tonic-gate 			 * Get rid of the even twin that was already
5227c478bd9Sstevel@tonic-gate 			 * in the slot.
5237c478bd9Sstevel@tonic-gate 			 */
5247c478bd9Sstevel@tonic-gate 			ag_del(ag);
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 		} else if (ag->ag_pref >= pref &&
5277c478bd9Sstevel@tonic-gate 		    (ag->ag_state & AGS_AGGREGATE)) {
5287c478bd9Sstevel@tonic-gate 			/*
5297c478bd9Sstevel@tonic-gate 			 * If we cannot combine the pair, maybe the route
5307c478bd9Sstevel@tonic-gate 			 * with the worse metric can be promoted.
5317c478bd9Sstevel@tonic-gate 			 *
5327c478bd9Sstevel@tonic-gate 			 * Promote the old, even twin, by giving its slot
5337c478bd9Sstevel@tonic-gate 			 * in the table to the new, odd twin.
5347c478bd9Sstevel@tonic-gate 			 */
5357c478bd9Sstevel@tonic-gate 			ag->ag_dst_h = dst;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 			xaddr = ag->ag_gate;
5387c478bd9Sstevel@tonic-gate 			ag->ag_gate = gate;
5397c478bd9Sstevel@tonic-gate 			gate = xaddr;
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 			xifp = ag->ag_ifp;
5427c478bd9Sstevel@tonic-gate 			ag->ag_ifp = ifp;
5437c478bd9Sstevel@tonic-gate 			ifp = xifp;
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 			xaddr = ag->ag_nhop;
5467c478bd9Sstevel@tonic-gate 			ag->ag_nhop = nhop;
5477c478bd9Sstevel@tonic-gate 			nhop = xaddr;
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 			tmp = ag->ag_tag;
5507c478bd9Sstevel@tonic-gate 			ag->ag_tag = tag;
5517c478bd9Sstevel@tonic-gate 			tag = tmp;
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 			/*
5547c478bd9Sstevel@tonic-gate 			 * The promoted route is even-redundant only if the
5557c478bd9Sstevel@tonic-gate 			 * even twin was fully redundant.  It is not
5567c478bd9Sstevel@tonic-gate 			 * odd-redundant because the odd-twin will still be
5577c478bd9Sstevel@tonic-gate 			 * in the table.
5587c478bd9Sstevel@tonic-gate 			 */
5597c478bd9Sstevel@tonic-gate 			tmp = ag->ag_state;
5607c478bd9Sstevel@tonic-gate 			if (!AG_IS_REDUN(tmp))
5617c478bd9Sstevel@tonic-gate 				tmp &= ~AGS_REDUN0;
5627c478bd9Sstevel@tonic-gate 			tmp &= ~AGS_REDUN1;
5637c478bd9Sstevel@tonic-gate 			ag->ag_state = state;
5647c478bd9Sstevel@tonic-gate 			state = tmp;
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 			tmp = ag->ag_metric;
5677c478bd9Sstevel@tonic-gate 			ag->ag_metric = metric;
5687c478bd9Sstevel@tonic-gate 			metric = tmp;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 			tmp = ag->ag_pref;
5717c478bd9Sstevel@tonic-gate 			ag->ag_pref = pref;
5727c478bd9Sstevel@tonic-gate 			pref = tmp;
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 			/* take the newest sequence number */
5757c478bd9Sstevel@tonic-gate 			if (seqno <= ag->ag_seqno)
5767c478bd9Sstevel@tonic-gate 				seqno = ag->ag_seqno;
5777c478bd9Sstevel@tonic-gate 			else
5787c478bd9Sstevel@tonic-gate 				ag->ag_seqno = seqno;
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 		} else {
5817c478bd9Sstevel@tonic-gate 			if (!(state & AGS_AGGREGATE))
5827c478bd9Sstevel@tonic-gate 				break;	/* cannot promote either twin */
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 			/*
5857c478bd9Sstevel@tonic-gate 			 * Promote the new, odd twin by shaving its
5867c478bd9Sstevel@tonic-gate 			 * mask and address.
5877c478bd9Sstevel@tonic-gate 			 * The promoted route is odd-redundant only if the
5887c478bd9Sstevel@tonic-gate 			 * odd twin was fully redundant.  It is not
5897c478bd9Sstevel@tonic-gate 			 * even-redundant because the even twin is still in
5907c478bd9Sstevel@tonic-gate 			 * the table.
5917c478bd9Sstevel@tonic-gate 			 */
5927c478bd9Sstevel@tonic-gate 			if (!AG_IS_REDUN(state))
5937c478bd9Sstevel@tonic-gate 				state &= ~AGS_REDUN1;
5947c478bd9Sstevel@tonic-gate 			state &= ~AGS_REDUN0;
5957c478bd9Sstevel@tonic-gate 			if (seqno < ag->ag_seqno)
5967c478bd9Sstevel@tonic-gate 				seqno = ag->ag_seqno;
5977c478bd9Sstevel@tonic-gate 			else
5987c478bd9Sstevel@tonic-gate 				ag->ag_seqno = seqno;
5997c478bd9Sstevel@tonic-gate 		}
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 		mask <<= 1;
6027c478bd9Sstevel@tonic-gate 		dst &= mask;
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 		if (ag_cors == NULL) {
6057c478bd9Sstevel@tonic-gate 			ag = ag_corsest;
6067c478bd9Sstevel@tonic-gate 			break;
6077c478bd9Sstevel@tonic-gate 		}
6087c478bd9Sstevel@tonic-gate 		ag = ag_cors;
6097c478bd9Sstevel@tonic-gate 		ag_cors = ag->ag_cors;
6107c478bd9Sstevel@tonic-gate 	}
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	/*
6137c478bd9Sstevel@tonic-gate 	 * When we can no longer promote and combine routes,
6147c478bd9Sstevel@tonic-gate 	 * flush the old route in the target slot.  Also flush
6157c478bd9Sstevel@tonic-gate 	 * any finer routes that we know will never be aggregated by
6167c478bd9Sstevel@tonic-gate 	 * the new route.
6177c478bd9Sstevel@tonic-gate 	 *
6187c478bd9Sstevel@tonic-gate 	 * In case we moved toward coarser masks,
6197c478bd9Sstevel@tonic-gate 	 * get back where we belong
6207c478bd9Sstevel@tonic-gate 	 */
6217c478bd9Sstevel@tonic-gate 	if (ag != NULL && ag->ag_mask < mask) {
6227c478bd9Sstevel@tonic-gate 		ag_cors = ag;
6237c478bd9Sstevel@tonic-gate 		ag = ag->ag_fine;
6247c478bd9Sstevel@tonic-gate 	}
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	/* Empty the target slot */
6277c478bd9Sstevel@tonic-gate 	if (ag != NULL && ag->ag_mask == mask) {
6287c478bd9Sstevel@tonic-gate 		ag_flush(ag->ag_dst_h, ag->ag_mask, out);
6297c478bd9Sstevel@tonic-gate 		ag = (ag_cors == NULL) ? ag_corsest : ag_cors->ag_fine;
6307c478bd9Sstevel@tonic-gate 	}
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate #ifdef DEBUG_AG
6337c478bd9Sstevel@tonic-gate 	if (ag == NULL && ag_cors != ag_finest)
6347c478bd9Sstevel@tonic-gate 		abort();
6357c478bd9Sstevel@tonic-gate 	if (ag_cors == NULL && ag != ag_corsest)
6367c478bd9Sstevel@tonic-gate 		abort();
6377c478bd9Sstevel@tonic-gate 	if (ag != NULL && ag->ag_cors != ag_cors)
6387c478bd9Sstevel@tonic-gate 		abort();
6397c478bd9Sstevel@tonic-gate 	if (ag_cors != NULL && ag_cors->ag_fine != ag)
6407c478bd9Sstevel@tonic-gate 		abort();
6417c478bd9Sstevel@tonic-gate 	CHECK_AG();
6427c478bd9Sstevel@tonic-gate #endif
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	/* Save the new route on the end of the table. */
6457c478bd9Sstevel@tonic-gate 	nag = ag_avail;
6467c478bd9Sstevel@tonic-gate 	ag_avail = nag->ag_fine;
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	nag->ag_dst_h = dst;
6497c478bd9Sstevel@tonic-gate 	nag->ag_mask = mask;
6507c478bd9Sstevel@tonic-gate 	nag->ag_ifp = ifp;
6517c478bd9Sstevel@tonic-gate 	nag->ag_gate = gate;
6527c478bd9Sstevel@tonic-gate 	nag->ag_nhop = nhop;
6537c478bd9Sstevel@tonic-gate 	nag->ag_metric = metric;
6547c478bd9Sstevel@tonic-gate 	nag->ag_pref = pref;
6557c478bd9Sstevel@tonic-gate 	nag->ag_tag = tag;
6567c478bd9Sstevel@tonic-gate 	nag->ag_state = state;
6577c478bd9Sstevel@tonic-gate 	nag->ag_seqno = seqno;
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	nag->ag_fine = ag;
6607c478bd9Sstevel@tonic-gate 	if (ag != NULL)
6617c478bd9Sstevel@tonic-gate 		ag->ag_cors = nag;
6627c478bd9Sstevel@tonic-gate 	else
6637c478bd9Sstevel@tonic-gate 		ag_finest = nag;
6647c478bd9Sstevel@tonic-gate 	nag->ag_cors = ag_cors;
6657c478bd9Sstevel@tonic-gate 	if (ag_cors == NULL)
6667c478bd9Sstevel@tonic-gate 		ag_corsest = nag;
6677c478bd9Sstevel@tonic-gate 	else
6687c478bd9Sstevel@tonic-gate 		ag_cors->ag_fine = nag;
6697c478bd9Sstevel@tonic-gate 	CHECK_AG();
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate static const char *
6747c478bd9Sstevel@tonic-gate rtm_type_name(uchar_t type)
6757c478bd9Sstevel@tonic-gate {
6767c478bd9Sstevel@tonic-gate 	static const char *rtm_types[] = {
6777c478bd9Sstevel@tonic-gate 		"RTM_ADD",
6787c478bd9Sstevel@tonic-gate 		"RTM_DELETE",
6797c478bd9Sstevel@tonic-gate 		"RTM_CHANGE",
6807c478bd9Sstevel@tonic-gate 		"RTM_GET",
6817c478bd9Sstevel@tonic-gate 		"RTM_LOSING",
6827c478bd9Sstevel@tonic-gate 		"RTM_REDIRECT",
6837c478bd9Sstevel@tonic-gate 		"RTM_MISS",
6847c478bd9Sstevel@tonic-gate 		"RTM_LOCK",
6857c478bd9Sstevel@tonic-gate 		"RTM_OLDADD",
6867c478bd9Sstevel@tonic-gate 		"RTM_OLDDEL",
6877c478bd9Sstevel@tonic-gate 		"RTM_RESOLVE",
6887c478bd9Sstevel@tonic-gate 		"RTM_NEWADDR",
6897c478bd9Sstevel@tonic-gate 		"RTM_DELADDR",
6907c478bd9Sstevel@tonic-gate 		"RTM_IFINFO",
6917c478bd9Sstevel@tonic-gate 		"RTM_NEWMADDR",
6927c478bd9Sstevel@tonic-gate 		"RTM_DELMADDR"
6937c478bd9Sstevel@tonic-gate 	};
6947c478bd9Sstevel@tonic-gate #define	NEW_RTM_PAT	"RTM type %#x"
6957c478bd9Sstevel@tonic-gate 	static char name0[sizeof (NEW_RTM_PAT) + 2];
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	if (type > sizeof (rtm_types) / sizeof (rtm_types[0]) || type == 0) {
6987c478bd9Sstevel@tonic-gate 		(void) snprintf(name0, sizeof (name0), NEW_RTM_PAT, type);
6997c478bd9Sstevel@tonic-gate 		return (name0);
7007c478bd9Sstevel@tonic-gate 	} else {
7017c478bd9Sstevel@tonic-gate 		return (rtm_types[type-1]);
7027c478bd9Sstevel@tonic-gate 	}
7037c478bd9Sstevel@tonic-gate #undef	NEW_RTM_PAT
7047c478bd9Sstevel@tonic-gate }
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate static void
7087c478bd9Sstevel@tonic-gate dump_rt_msg(const char *act, struct rt_msghdr *rtm, int mlen)
7097c478bd9Sstevel@tonic-gate {
7107c478bd9Sstevel@tonic-gate 	const char *mtype;
7117c478bd9Sstevel@tonic-gate 	uchar_t *cp;
7127c478bd9Sstevel@tonic-gate 	int i, j;
7137c478bd9Sstevel@tonic-gate 	char buffer[16*3 + 1], *ibs;
7147c478bd9Sstevel@tonic-gate 	struct ifa_msghdr *ifam;
7157c478bd9Sstevel@tonic-gate 	struct if_msghdr *ifm;
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 	switch (rtm->rtm_type) {
7187c478bd9Sstevel@tonic-gate 	case RTM_NEWADDR:
7197c478bd9Sstevel@tonic-gate 	case RTM_DELADDR:
7207c478bd9Sstevel@tonic-gate 		mtype = "ifam";
7217c478bd9Sstevel@tonic-gate 		break;
7227c478bd9Sstevel@tonic-gate 	case RTM_IFINFO:
7237c478bd9Sstevel@tonic-gate 		mtype = "ifm";
7247c478bd9Sstevel@tonic-gate 		break;
7257c478bd9Sstevel@tonic-gate 	default:
7267c478bd9Sstevel@tonic-gate 		mtype = "rtm";
7277c478bd9Sstevel@tonic-gate 		break;
7287c478bd9Sstevel@tonic-gate 	}
7297c478bd9Sstevel@tonic-gate 	trace_misc("%s %s %d bytes", act, mtype, mlen);
7307c478bd9Sstevel@tonic-gate 	if (mlen > rtm->rtm_msglen) {
7317c478bd9Sstevel@tonic-gate 		trace_misc("%s: extra %d bytes ignored", mtype,
7327c478bd9Sstevel@tonic-gate 		    mlen - rtm->rtm_msglen);
7337c478bd9Sstevel@tonic-gate 		mlen = rtm->rtm_msglen;
7347c478bd9Sstevel@tonic-gate 	} else if (mlen < rtm->rtm_msglen) {
7357c478bd9Sstevel@tonic-gate 		trace_misc("%s: truncated by %d bytes", mtype,
7367c478bd9Sstevel@tonic-gate 		    rtm->rtm_msglen - mlen);
7377c478bd9Sstevel@tonic-gate 	}
7387c478bd9Sstevel@tonic-gate 	switch (rtm->rtm_type) {
7397c478bd9Sstevel@tonic-gate 	case RTM_NEWADDR:
7407c478bd9Sstevel@tonic-gate 	case RTM_DELADDR:
7417c478bd9Sstevel@tonic-gate 		ifam = (struct ifa_msghdr *)rtm;
7427c478bd9Sstevel@tonic-gate 		trace_misc("ifam: msglen %d version %d type %d addrs %X",
7437c478bd9Sstevel@tonic-gate 		    ifam->ifam_msglen, ifam->ifam_version, ifam->ifam_type,
7447c478bd9Sstevel@tonic-gate 		    ifam->ifam_addrs);
7457c478bd9Sstevel@tonic-gate 		trace_misc("ifam: flags %X index %d metric %d",
7467c478bd9Sstevel@tonic-gate 		    ifam->ifam_flags, ifam->ifam_index, ifam->ifam_metric);
7477c478bd9Sstevel@tonic-gate 		cp = (uchar_t *)(ifam + 1);
7487c478bd9Sstevel@tonic-gate 		break;
7497c478bd9Sstevel@tonic-gate 	case RTM_IFINFO:
7507c478bd9Sstevel@tonic-gate 		ifm = (struct if_msghdr *)rtm;
7517c478bd9Sstevel@tonic-gate 		trace_misc("ifm: msglen %d version %d type %d addrs %X",
7527c478bd9Sstevel@tonic-gate 		    ifm->ifm_msglen, ifm->ifm_version, ifm->ifm_type,
7537c478bd9Sstevel@tonic-gate 		    ifm->ifm_addrs);
7547c478bd9Sstevel@tonic-gate 		ibs = if_bit_string(ifm->ifm_flags, _B_TRUE);
7557c478bd9Sstevel@tonic-gate 		if (ibs == NULL) {
7567c478bd9Sstevel@tonic-gate 			trace_misc("ifm: flags %#x index %d", ifm->ifm_flags,
7577c478bd9Sstevel@tonic-gate 			    ifm->ifm_index);
7587c478bd9Sstevel@tonic-gate 		} else {
7597c478bd9Sstevel@tonic-gate 			trace_misc("ifm: flags %s index %d", ibs,
7607c478bd9Sstevel@tonic-gate 			    ifm->ifm_index);
7617c478bd9Sstevel@tonic-gate 			free(ibs);
7627c478bd9Sstevel@tonic-gate 		}
7637c478bd9Sstevel@tonic-gate 		cp = (uchar_t *)(ifm + 1);
7647c478bd9Sstevel@tonic-gate 		break;
7657c478bd9Sstevel@tonic-gate 	default:
7667c478bd9Sstevel@tonic-gate 		trace_misc("rtm: msglen %d version %d type %d index %d",
7677c478bd9Sstevel@tonic-gate 		    rtm->rtm_msglen, rtm->rtm_version, rtm->rtm_type,
7687c478bd9Sstevel@tonic-gate 		    rtm->rtm_index);
7697c478bd9Sstevel@tonic-gate 		trace_misc("rtm: flags %X addrs %X pid %d seq %d",
7707c478bd9Sstevel@tonic-gate 		    rtm->rtm_flags, rtm->rtm_addrs, rtm->rtm_pid, rtm->rtm_seq);
7717c478bd9Sstevel@tonic-gate 		trace_misc("rtm: errno %d use %d inits %X", rtm->rtm_errno,
7727c478bd9Sstevel@tonic-gate 		    rtm->rtm_use, rtm->rtm_inits);
7737c478bd9Sstevel@tonic-gate 		cp = (uchar_t *)(rtm + 1);
7747c478bd9Sstevel@tonic-gate 		break;
7757c478bd9Sstevel@tonic-gate 	}
7767c478bd9Sstevel@tonic-gate 	i = mlen - (cp - (uint8_t *)rtm);
7777c478bd9Sstevel@tonic-gate 	while (i > 0) {
7787c478bd9Sstevel@tonic-gate 		buffer[0] = '\0';
7797c478bd9Sstevel@tonic-gate 		ibs = buffer;
7807c478bd9Sstevel@tonic-gate 		for (j = 0; j < 16 && i > 0; j++, i--)
7817c478bd9Sstevel@tonic-gate 			ibs += sprintf(ibs, " %02X", *cp++);
7827c478bd9Sstevel@tonic-gate 		trace_misc("addr%s", buffer);
7837c478bd9Sstevel@tonic-gate 	}
7847c478bd9Sstevel@tonic-gate }
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate /*
7877c478bd9Sstevel@tonic-gate  * Tell the kernel to add, delete or change a route
7887c478bd9Sstevel@tonic-gate  * Pass k_state from khash in for diagnostic info.
7897c478bd9Sstevel@tonic-gate  */
7907c478bd9Sstevel@tonic-gate static void
7917c478bd9Sstevel@tonic-gate rtioctl(int action,			/* RTM_DELETE, etc */
7927c478bd9Sstevel@tonic-gate     in_addr_t dst,
7937c478bd9Sstevel@tonic-gate     in_addr_t gate,
7947c478bd9Sstevel@tonic-gate     in_addr_t mask,
7957c478bd9Sstevel@tonic-gate     struct interface *ifp,
7967c478bd9Sstevel@tonic-gate     uint8_t metric,
7977c478bd9Sstevel@tonic-gate     int flags)
7987c478bd9Sstevel@tonic-gate {
7997c478bd9Sstevel@tonic-gate 	static int rt_sock_seqno = 0;
8007c478bd9Sstevel@tonic-gate 	struct {
8017c478bd9Sstevel@tonic-gate 		struct rt_msghdr w_rtm;
8027c478bd9Sstevel@tonic-gate 		struct sockaddr_in w_dst;
8037c478bd9Sstevel@tonic-gate 		struct sockaddr_in w_gate;
8047c478bd9Sstevel@tonic-gate 		uint8_t w_space[512];
8057c478bd9Sstevel@tonic-gate 	} w;
8067c478bd9Sstevel@tonic-gate 	struct sockaddr_in w_mask;
8077c478bd9Sstevel@tonic-gate 	struct sockaddr_dl w_ifp;
8087c478bd9Sstevel@tonic-gate 	uint8_t *cp;
8097c478bd9Sstevel@tonic-gate 	long cc;
8107c478bd9Sstevel@tonic-gate #define	PAT " %-10s %s metric=%d flags=%#x"
8117c478bd9Sstevel@tonic-gate #define	ARGS rtm_type_name(action), rtname(dst, mask, gate), metric, flags
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate again:
8147c478bd9Sstevel@tonic-gate 	(void) memset(&w, 0, sizeof (w));
8157c478bd9Sstevel@tonic-gate 	(void) memset(&w_mask, 0, sizeof (w_mask));
8167c478bd9Sstevel@tonic-gate 	(void) memset(&w_ifp, 0, sizeof (w_ifp));
8177c478bd9Sstevel@tonic-gate 	cp = w.w_space;
8187c478bd9Sstevel@tonic-gate 	w.w_rtm.rtm_msglen = sizeof (struct rt_msghdr) +
8197c478bd9Sstevel@tonic-gate 	    2 * ROUNDUP_LONG(sizeof (struct sockaddr_in));
8207c478bd9Sstevel@tonic-gate 	w.w_rtm.rtm_version = RTM_VERSION;
8217c478bd9Sstevel@tonic-gate 	w.w_rtm.rtm_type = action;
8227c478bd9Sstevel@tonic-gate 	w.w_rtm.rtm_flags = flags;
8237c478bd9Sstevel@tonic-gate 	w.w_rtm.rtm_seq = ++rt_sock_seqno;
8247c478bd9Sstevel@tonic-gate 	w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
8257c478bd9Sstevel@tonic-gate 	if (metric != 0 || action == RTM_CHANGE) {
8267c478bd9Sstevel@tonic-gate 		w.w_rtm.rtm_rmx.rmx_hopcount = metric;
8277c478bd9Sstevel@tonic-gate 		w.w_rtm.rtm_inits |= RTV_HOPCOUNT;
8287c478bd9Sstevel@tonic-gate 	}
8297c478bd9Sstevel@tonic-gate 	w.w_dst.sin_family = AF_INET;
8307c478bd9Sstevel@tonic-gate 	w.w_dst.sin_addr.s_addr = dst;
8317c478bd9Sstevel@tonic-gate 	w.w_gate.sin_family = AF_INET;
8327c478bd9Sstevel@tonic-gate 	w.w_gate.sin_addr.s_addr = gate;
8337c478bd9Sstevel@tonic-gate 	if (mask == HOST_MASK) {
8347c478bd9Sstevel@tonic-gate 		w.w_rtm.rtm_flags |= RTF_HOST;
8357c478bd9Sstevel@tonic-gate 	} else {
8367c478bd9Sstevel@tonic-gate 		w.w_rtm.rtm_addrs |= RTA_NETMASK;
8377c478bd9Sstevel@tonic-gate 		w_mask.sin_family = AF_INET;
8387c478bd9Sstevel@tonic-gate 		w_mask.sin_addr.s_addr = htonl(mask);
8397c478bd9Sstevel@tonic-gate 		(void) memmove(cp, &w_mask, sizeof (w_mask));
8407c478bd9Sstevel@tonic-gate 		cp += ROUNDUP_LONG(sizeof (struct sockaddr_in));
8417c478bd9Sstevel@tonic-gate 		w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_in));
8427c478bd9Sstevel@tonic-gate 	}
8437c478bd9Sstevel@tonic-gate 	if (ifp == NULL)
8447c478bd9Sstevel@tonic-gate 		ifp = iflookup(gate);
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	if ((ifp == NULL) || (ifp->int_phys == NULL)) {
8477c478bd9Sstevel@tonic-gate 		trace_misc("no ifp for" PAT, ARGS);
8487c478bd9Sstevel@tonic-gate 	} else {
8497c478bd9Sstevel@tonic-gate 		if (ifp->int_phys->phyi_index > UINT16_MAX) {
8507c478bd9Sstevel@tonic-gate 			trace_misc("ifindex %d is too big for sdl_index",
8517c478bd9Sstevel@tonic-gate 			    ifp->int_phys->phyi_index);
8527c478bd9Sstevel@tonic-gate 		} else {
8537c478bd9Sstevel@tonic-gate 			w_ifp.sdl_family = AF_LINK;
8547c478bd9Sstevel@tonic-gate 			w.w_rtm.rtm_addrs |= RTA_IFP;
8557c478bd9Sstevel@tonic-gate 			w_ifp.sdl_index = ifp->int_phys->phyi_index;
8567c478bd9Sstevel@tonic-gate 			(void) memmove(cp, &w_ifp, sizeof (w_ifp));
8577c478bd9Sstevel@tonic-gate 			w.w_rtm.rtm_msglen +=
8587c478bd9Sstevel@tonic-gate 			    ROUNDUP_LONG(sizeof (struct sockaddr_dl));
8597c478bd9Sstevel@tonic-gate 		}
8607c478bd9Sstevel@tonic-gate 	}
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 	if (!no_install) {
8647c478bd9Sstevel@tonic-gate 		if (TRACERTS)
8657c478bd9Sstevel@tonic-gate 			dump_rt_msg("write", &w.w_rtm, w.w_rtm.rtm_msglen);
8667c478bd9Sstevel@tonic-gate 		cc = write(rt_sock, &w, w.w_rtm.rtm_msglen);
8677c478bd9Sstevel@tonic-gate 		if (cc < 0) {
8687c478bd9Sstevel@tonic-gate 			if (errno == ESRCH && (action == RTM_CHANGE ||
8697c478bd9Sstevel@tonic-gate 			    action == RTM_DELETE)) {
8707c478bd9Sstevel@tonic-gate 				trace_act("route disappeared before" PAT, ARGS);
8717c478bd9Sstevel@tonic-gate 				if (action == RTM_CHANGE) {
8727c478bd9Sstevel@tonic-gate 					action = RTM_ADD;
8737c478bd9Sstevel@tonic-gate 					goto again;
8747c478bd9Sstevel@tonic-gate 				}
8757c478bd9Sstevel@tonic-gate 				return;
8767c478bd9Sstevel@tonic-gate 			}
8777c478bd9Sstevel@tonic-gate 			writelog(LOG_WARNING, "write(rt_sock)" PAT ": %s ",
8787c478bd9Sstevel@tonic-gate 			    ARGS, rip_strerror(errno));
8797c478bd9Sstevel@tonic-gate 			return;
8807c478bd9Sstevel@tonic-gate 		} else if (cc != w.w_rtm.rtm_msglen) {
8817c478bd9Sstevel@tonic-gate 			msglog("write(rt_sock) wrote %ld instead of %d for" PAT,
8827c478bd9Sstevel@tonic-gate 			    cc, w.w_rtm.rtm_msglen, ARGS);
8837c478bd9Sstevel@tonic-gate 			return;
8847c478bd9Sstevel@tonic-gate 		}
8857c478bd9Sstevel@tonic-gate 	}
8867c478bd9Sstevel@tonic-gate 	if (TRACEKERNEL)
8877c478bd9Sstevel@tonic-gate 		trace_misc("write kernel" PAT, ARGS);
8887c478bd9Sstevel@tonic-gate #undef PAT
8897c478bd9Sstevel@tonic-gate #undef ARGS
8907c478bd9Sstevel@tonic-gate }
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate /* Hash table containing our image of the kernel forwarding table. */
8947c478bd9Sstevel@tonic-gate #define	KHASH_SIZE 71			/* should be prime */
8957c478bd9Sstevel@tonic-gate #define	KHASH(a, m) khash_bins[((a) ^ (m)) % KHASH_SIZE]
8967c478bd9Sstevel@tonic-gate static struct khash *khash_bins[KHASH_SIZE];
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate #define	K_KEEP_LIM	30	/* k_keep */
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate static struct khash *
9017c478bd9Sstevel@tonic-gate kern_find(in_addr_t dst, in_addr_t mask, in_addr_t gate,
9027c478bd9Sstevel@tonic-gate     struct interface *ifp, struct khash ***ppk)
9037c478bd9Sstevel@tonic-gate {
9047c478bd9Sstevel@tonic-gate 	struct khash *k, **pk;
9057c478bd9Sstevel@tonic-gate 
906e1fa7f4dSsowmini 	if (ifp != NULL && ifp->int_phys != NULL) {
907e1fa7f4dSsowmini 		ifp = ifwithname(ifp->int_phys->phyi_name);
908e1fa7f4dSsowmini 	}
909e1fa7f4dSsowmini 
9107c478bd9Sstevel@tonic-gate 	for (pk = &KHASH(dst, mask); (k = *pk) != NULL; pk = &k->k_next) {
9117c478bd9Sstevel@tonic-gate 		if (k->k_dst == dst && k->k_mask == mask &&
9127c478bd9Sstevel@tonic-gate 		    (gate == 0 || k->k_gate == gate) &&
9137c478bd9Sstevel@tonic-gate 		    (ifp == NULL || k->k_ifp == ifp)) {
9147c478bd9Sstevel@tonic-gate 			break;
9157c478bd9Sstevel@tonic-gate 		}
9167c478bd9Sstevel@tonic-gate 	}
9177c478bd9Sstevel@tonic-gate 	if (ppk != NULL)
9187c478bd9Sstevel@tonic-gate 		*ppk = pk;
9197c478bd9Sstevel@tonic-gate 	return (k);
9207c478bd9Sstevel@tonic-gate }
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate /*
9247c478bd9Sstevel@tonic-gate  * Find out if there is an alternate route to a given destination
9257c478bd9Sstevel@tonic-gate  * off of a given interface.
9267c478bd9Sstevel@tonic-gate  */
9277c478bd9Sstevel@tonic-gate static struct khash *
9287c478bd9Sstevel@tonic-gate kern_alternate(in_addr_t dst, in_addr_t mask, in_addr_t gate,
9297c478bd9Sstevel@tonic-gate     struct interface *ifp, struct khash ***ppk)
9307c478bd9Sstevel@tonic-gate {
9317c478bd9Sstevel@tonic-gate 	struct khash *k, **pk;
9327c478bd9Sstevel@tonic-gate 
933e1fa7f4dSsowmini 	if (ifp != NULL && ifp->int_phys != NULL) {
934e1fa7f4dSsowmini 		ifp = ifwithname(ifp->int_phys->phyi_name);
935e1fa7f4dSsowmini 	}
9367c478bd9Sstevel@tonic-gate 	for (pk = &KHASH(dst, mask); (k = *pk) != NULL; pk = &k->k_next) {
9377c478bd9Sstevel@tonic-gate 		if (k->k_dst == dst && k->k_mask == mask &&
9387c478bd9Sstevel@tonic-gate 		    (k->k_gate != gate) &&
9397c478bd9Sstevel@tonic-gate 		    (k->k_ifp == ifp)) {
9407c478bd9Sstevel@tonic-gate 			break;
9417c478bd9Sstevel@tonic-gate 		}
9427c478bd9Sstevel@tonic-gate 	}
9437c478bd9Sstevel@tonic-gate 	if (ppk != NULL)
9447c478bd9Sstevel@tonic-gate 		*ppk = pk;
9457c478bd9Sstevel@tonic-gate 	return (k);
9467c478bd9Sstevel@tonic-gate }
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate static struct khash *
9497c478bd9Sstevel@tonic-gate kern_add(in_addr_t dst, uint32_t mask, in_addr_t gate, struct interface *ifp)
9507c478bd9Sstevel@tonic-gate {
9517c478bd9Sstevel@tonic-gate 	struct khash *k, **pk;
9527c478bd9Sstevel@tonic-gate 
953e1fa7f4dSsowmini 	if (ifp != NULL && ifp->int_phys != NULL) {
954e1fa7f4dSsowmini 		ifp = ifwithname(ifp->int_phys->phyi_name);
955e1fa7f4dSsowmini 	}
9567c478bd9Sstevel@tonic-gate 	k = kern_find(dst, mask, gate, ifp, &pk);
9577c478bd9Sstevel@tonic-gate 	if (k != NULL)
9587c478bd9Sstevel@tonic-gate 		return (k);
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 	k = rtmalloc(sizeof (*k), "kern_add");
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	(void) memset(k, 0, sizeof (*k));
9637c478bd9Sstevel@tonic-gate 	k->k_dst = dst;
9647c478bd9Sstevel@tonic-gate 	k->k_mask = mask;
9657c478bd9Sstevel@tonic-gate 	k->k_state = KS_NEW;
9667c478bd9Sstevel@tonic-gate 	k->k_keep = now.tv_sec;
9677c478bd9Sstevel@tonic-gate 	k->k_gate = gate;
9687c478bd9Sstevel@tonic-gate 	k->k_ifp = ifp;
9697c478bd9Sstevel@tonic-gate 	*pk = k;
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 	return (k);
9727c478bd9Sstevel@tonic-gate }
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate /* delete all khash entries that are wired through the interface ifp */
9757c478bd9Sstevel@tonic-gate void
9767c478bd9Sstevel@tonic-gate kern_flush_ifp(struct interface *ifp)
9777c478bd9Sstevel@tonic-gate {
9787c478bd9Sstevel@tonic-gate 	struct khash *k, *kprev, *knext;
9797c478bd9Sstevel@tonic-gate 	int i;
9807c478bd9Sstevel@tonic-gate 
981e1fa7f4dSsowmini 	if (ifp != NULL && ifp->int_phys != NULL) {
982e1fa7f4dSsowmini 		ifp = ifwithname(ifp->int_phys->phyi_name);
983e1fa7f4dSsowmini 	}
9847c478bd9Sstevel@tonic-gate 	for (i = 0; i < KHASH_SIZE; i++) {
9857c478bd9Sstevel@tonic-gate 		kprev = NULL;
9867c478bd9Sstevel@tonic-gate 		for (k = khash_bins[i]; k != NULL; k = knext) {
9877c478bd9Sstevel@tonic-gate 			knext = k->k_next;
9887c478bd9Sstevel@tonic-gate 			if (k->k_ifp == ifp) {
9897c478bd9Sstevel@tonic-gate 				if (kprev != NULL)
9907c478bd9Sstevel@tonic-gate 					kprev->k_next = k->k_next;
9917c478bd9Sstevel@tonic-gate 				else
9927c478bd9Sstevel@tonic-gate 					khash_bins[i] = k->k_next;
9937c478bd9Sstevel@tonic-gate 				free(k);
9947c478bd9Sstevel@tonic-gate 				continue;
9957c478bd9Sstevel@tonic-gate 			}
9967c478bd9Sstevel@tonic-gate 			kprev = k;
9977c478bd9Sstevel@tonic-gate 		}
9987c478bd9Sstevel@tonic-gate 	}
9997c478bd9Sstevel@tonic-gate }
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate /*
10027c478bd9Sstevel@tonic-gate  * rewire khash entries that currently go through oldifp to
10037c478bd9Sstevel@tonic-gate  * go through newifp.
10047c478bd9Sstevel@tonic-gate  */
10057c478bd9Sstevel@tonic-gate void
10067c478bd9Sstevel@tonic-gate kern_rewire_ifp(struct interface *oldifp, struct interface *newifp)
10077c478bd9Sstevel@tonic-gate {
10087c478bd9Sstevel@tonic-gate 	struct khash *k;
10097c478bd9Sstevel@tonic-gate 	int i;
10107c478bd9Sstevel@tonic-gate 
1011e1fa7f4dSsowmini 	if (oldifp != NULL && oldifp->int_phys != NULL) {
1012e1fa7f4dSsowmini 		oldifp = ifwithname(oldifp->int_phys->phyi_name);
1013e1fa7f4dSsowmini 	}
1014e1fa7f4dSsowmini 	if (newifp != NULL && newifp->int_phys != NULL) {
1015e1fa7f4dSsowmini 		newifp = ifwithname(newifp->int_phys->phyi_name);
1016e1fa7f4dSsowmini 	}
10177c478bd9Sstevel@tonic-gate 	for (i = 0; i < KHASH_SIZE; i++) {
10187c478bd9Sstevel@tonic-gate 		for (k = khash_bins[i]; k; k = k->k_next) {
10197c478bd9Sstevel@tonic-gate 			if (k->k_ifp == oldifp) {
10207c478bd9Sstevel@tonic-gate 				k->k_ifp = newifp;
10217c478bd9Sstevel@tonic-gate 				trace_misc("kern_rewire_ifp k 0x%lx "
10227c478bd9Sstevel@tonic-gate 				    "from %s to %s", k, oldifp->int_name,
10237c478bd9Sstevel@tonic-gate 				    newifp->int_name);
10247c478bd9Sstevel@tonic-gate 			}
10257c478bd9Sstevel@tonic-gate 		}
10267c478bd9Sstevel@tonic-gate 	}
10277c478bd9Sstevel@tonic-gate }
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate /*
10317c478bd9Sstevel@tonic-gate  * Check that a static route it is still in the daemon table, and not
10327c478bd9Sstevel@tonic-gate  * deleted by interfaces coming and going.  This is also the routine
10337c478bd9Sstevel@tonic-gate  * responsible for adding new static routes to the daemon table.
10347c478bd9Sstevel@tonic-gate  */
10357c478bd9Sstevel@tonic-gate static void
10367c478bd9Sstevel@tonic-gate kern_check_static(struct khash *k, struct interface *ifp)
10377c478bd9Sstevel@tonic-gate {
10387c478bd9Sstevel@tonic-gate 	struct rt_entry *rt;
10397c478bd9Sstevel@tonic-gate 	struct rt_spare new;
10407c478bd9Sstevel@tonic-gate 	uint16_t rt_state = RS_STATIC;
10417c478bd9Sstevel@tonic-gate 
1042e1fa7f4dSsowmini 	if (ifp != NULL && ifp->int_phys != NULL) {
1043e1fa7f4dSsowmini 		ifp = ifwithname(ifp->int_phys->phyi_name);
1044e1fa7f4dSsowmini 	}
10457c478bd9Sstevel@tonic-gate 	(void) memset(&new, 0, sizeof (new));
10467c478bd9Sstevel@tonic-gate 	new.rts_ifp = ifp;
10477c478bd9Sstevel@tonic-gate 	new.rts_gate = k->k_gate;
10487c478bd9Sstevel@tonic-gate 	new.rts_router = (ifp != NULL) ? ifp->int_addr : loopaddr;
10497c478bd9Sstevel@tonic-gate 	new.rts_metric = k->k_metric;
10507c478bd9Sstevel@tonic-gate 	new.rts_time = now.tv_sec;
10517c478bd9Sstevel@tonic-gate 	new.rts_origin = RO_STATIC;
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	rt = rtget(k->k_dst, k->k_mask);
10547c478bd9Sstevel@tonic-gate 	if ((ifp != NULL && !IS_IFF_ROUTING(ifp->int_if_flags)) ||
10557c478bd9Sstevel@tonic-gate 	    (k->k_state & KS_PRIVATE))
10567c478bd9Sstevel@tonic-gate 		rt_state |= RS_NOPROPAGATE;
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	if (rt != NULL) {
10597c478bd9Sstevel@tonic-gate 		if ((rt->rt_state & RS_STATIC) == 0) {
10607c478bd9Sstevel@tonic-gate 			/*
10617c478bd9Sstevel@tonic-gate 			 * We are already tracking this dest/mask
10627c478bd9Sstevel@tonic-gate 			 * via RIP/RDISC. Ignore the static route,
10637c478bd9Sstevel@tonic-gate 			 * because we don't currently have a good
10647c478bd9Sstevel@tonic-gate 			 * way to compare metrics on static routes
10657c478bd9Sstevel@tonic-gate 			 * with rip metrics, and therefore cannot
10667c478bd9Sstevel@tonic-gate 			 * mix and match the two.
10677c478bd9Sstevel@tonic-gate 			 */
10687c478bd9Sstevel@tonic-gate 			return;
10697c478bd9Sstevel@tonic-gate 		}
10707c478bd9Sstevel@tonic-gate 		rt_state |= rt->rt_state;
10717c478bd9Sstevel@tonic-gate 		if (rt->rt_state != rt_state)
10727c478bd9Sstevel@tonic-gate 			rtchange(rt, rt_state, &new, 0);
10737c478bd9Sstevel@tonic-gate 	} else {
10747c478bd9Sstevel@tonic-gate 		rtadd(k->k_dst, k->k_mask, rt_state, &new);
10757c478bd9Sstevel@tonic-gate 	}
10767c478bd9Sstevel@tonic-gate }
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate /* operate on a kernel entry */
10807c478bd9Sstevel@tonic-gate static void
10817c478bd9Sstevel@tonic-gate kern_ioctl(struct khash *k,
10827c478bd9Sstevel@tonic-gate     int action,			/* RTM_DELETE, etc */
10837c478bd9Sstevel@tonic-gate     int flags)
10847c478bd9Sstevel@tonic-gate {
10857c478bd9Sstevel@tonic-gate 	if (((k->k_state & (KS_IF|KS_PASSIVE)) == KS_IF) ||
10867c478bd9Sstevel@tonic-gate 	    (k->k_state & KS_DEPRE_IF)) {
10877c478bd9Sstevel@tonic-gate 		/*
10887c478bd9Sstevel@tonic-gate 		 * Prevent execution of RTM_DELETE, RTM_ADD or
10897c478bd9Sstevel@tonic-gate 		 * RTM_CHANGE of interface routes
10907c478bd9Sstevel@tonic-gate 		 */
10917c478bd9Sstevel@tonic-gate 		trace_act("Blocking execution of %s  %s --> %s ",
10927c478bd9Sstevel@tonic-gate 		    rtm_type_name(action),
10937c478bd9Sstevel@tonic-gate 		    addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate));
10947c478bd9Sstevel@tonic-gate 		return;
10957c478bd9Sstevel@tonic-gate 	}
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	switch (action) {
10987c478bd9Sstevel@tonic-gate 	case RTM_DELETE:
10997c478bd9Sstevel@tonic-gate 		k->k_state &= ~KS_DYNAMIC;
11007c478bd9Sstevel@tonic-gate 		if (k->k_state & KS_DELETED)
11017c478bd9Sstevel@tonic-gate 			return;
11027c478bd9Sstevel@tonic-gate 		k->k_state |= KS_DELETED;
11037c478bd9Sstevel@tonic-gate 		break;
11047c478bd9Sstevel@tonic-gate 	case RTM_ADD:
11057c478bd9Sstevel@tonic-gate 		k->k_state &= ~KS_DELETED;
11067c478bd9Sstevel@tonic-gate 		break;
11077c478bd9Sstevel@tonic-gate 	case RTM_CHANGE:
11087c478bd9Sstevel@tonic-gate 		if (k->k_state & KS_DELETED) {
11097c478bd9Sstevel@tonic-gate 			action = RTM_ADD;
11107c478bd9Sstevel@tonic-gate 			k->k_state &= ~KS_DELETED;
11117c478bd9Sstevel@tonic-gate 		}
11127c478bd9Sstevel@tonic-gate 		break;
11137c478bd9Sstevel@tonic-gate 	}
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 	rtioctl(action, k->k_dst, k->k_gate, k->k_mask, k->k_ifp,
11167c478bd9Sstevel@tonic-gate 	    k->k_metric, flags);
11177c478bd9Sstevel@tonic-gate }
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate /* add a route the kernel told us */
11217c478bd9Sstevel@tonic-gate static void
11227c478bd9Sstevel@tonic-gate rtm_add(struct rt_msghdr *rtm,
11237c478bd9Sstevel@tonic-gate     struct rt_addrinfo *info,
11247c478bd9Sstevel@tonic-gate     time_t keep,
11257c478bd9Sstevel@tonic-gate     boolean_t interf_route,
11267c478bd9Sstevel@tonic-gate     struct interface *ifptr)
11277c478bd9Sstevel@tonic-gate {
11287c478bd9Sstevel@tonic-gate 	struct khash *k;
11297c478bd9Sstevel@tonic-gate 	struct interface *ifp = ifptr;
11307c478bd9Sstevel@tonic-gate 	in_addr_t mask, gate = 0;
11317c478bd9Sstevel@tonic-gate 	static struct msg_limit msg_no_ifp;
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate 	if (rtm->rtm_flags & RTF_HOST) {
11347c478bd9Sstevel@tonic-gate 		mask = HOST_MASK;
11357c478bd9Sstevel@tonic-gate 	} else if (INFO_MASK(info) != 0) {
11367c478bd9Sstevel@tonic-gate 		mask = ntohl(S_ADDR(INFO_MASK(info)));
11377c478bd9Sstevel@tonic-gate 	} else {
11387c478bd9Sstevel@tonic-gate 		writelog(LOG_WARNING,
11397c478bd9Sstevel@tonic-gate 		    "ignore %s without mask", rtm_type_name(rtm->rtm_type));
11407c478bd9Sstevel@tonic-gate 		return;
11417c478bd9Sstevel@tonic-gate 	}
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	/*
11447c478bd9Sstevel@tonic-gate 	 * Find the interface toward the gateway.
11457c478bd9Sstevel@tonic-gate 	 */
11467c478bd9Sstevel@tonic-gate 	if (INFO_GATE(info) != NULL)
11477c478bd9Sstevel@tonic-gate 		gate = S_ADDR(INFO_GATE(info));
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	if (ifp == NULL) {
11507c478bd9Sstevel@tonic-gate 		if (INFO_GATE(info) != NULL)
11517c478bd9Sstevel@tonic-gate 			ifp = iflookup(gate);
1152e1fa7f4dSsowmini 		if (ifp == NULL) {
11537c478bd9Sstevel@tonic-gate 			msglim(&msg_no_ifp, gate,
11547c478bd9Sstevel@tonic-gate 			    "route %s --> %s nexthop is not directly connected",
11557c478bd9Sstevel@tonic-gate 			    addrname(S_ADDR(INFO_DST(info)), mask, 0),
11567c478bd9Sstevel@tonic-gate 			    naddr_ntoa(gate));
1157e1fa7f4dSsowmini 		} else {
1158e1fa7f4dSsowmini 			if (ifp->int_phys != NULL) {
1159e1fa7f4dSsowmini 				ifp = ifwithname(ifp->int_phys->phyi_name);
1160e1fa7f4dSsowmini 			}
1161e1fa7f4dSsowmini 		}
11627c478bd9Sstevel@tonic-gate 	}
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate 	k = kern_add(S_ADDR(INFO_DST(info)), mask, gate, ifp);
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 	if (k->k_state & KS_NEW)
11677c478bd9Sstevel@tonic-gate 		k->k_keep = now.tv_sec+keep;
11687c478bd9Sstevel@tonic-gate 	if (INFO_GATE(info) == 0) {
11697c478bd9Sstevel@tonic-gate 		trace_act("note %s without gateway",
11707c478bd9Sstevel@tonic-gate 		    rtm_type_name(rtm->rtm_type));
11717c478bd9Sstevel@tonic-gate 		k->k_metric = HOPCNT_INFINITY;
11727c478bd9Sstevel@tonic-gate 	} else if (INFO_GATE(info)->ss_family != AF_INET) {
11737c478bd9Sstevel@tonic-gate 		trace_act("note %s with gateway AF=%d",
11747c478bd9Sstevel@tonic-gate 		    rtm_type_name(rtm->rtm_type),
11757c478bd9Sstevel@tonic-gate 		    INFO_GATE(info)->ss_family);
11767c478bd9Sstevel@tonic-gate 		k->k_metric = HOPCNT_INFINITY;
11777c478bd9Sstevel@tonic-gate 	} else {
11787c478bd9Sstevel@tonic-gate 		k->k_gate = S_ADDR(INFO_GATE(info));
11797c478bd9Sstevel@tonic-gate 		k->k_metric = rtm->rtm_rmx.rmx_hopcount;
11807c478bd9Sstevel@tonic-gate 		if (k->k_metric < 0)
11817c478bd9Sstevel@tonic-gate 			k->k_metric = 0;
11827c478bd9Sstevel@tonic-gate 		else if (k->k_metric > HOPCNT_INFINITY-1)
11837c478bd9Sstevel@tonic-gate 			k->k_metric = HOPCNT_INFINITY-1;
11847c478bd9Sstevel@tonic-gate 	}
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	if ((k->k_state & KS_NEW) && interf_route) {
11877c478bd9Sstevel@tonic-gate 		if (k->k_gate != 0 && findifaddr(k->k_gate) == NULL)
11887c478bd9Sstevel@tonic-gate 			k->k_state |= KS_DEPRE_IF;
11897c478bd9Sstevel@tonic-gate 		else
11907c478bd9Sstevel@tonic-gate 			k->k_state |= KS_IF;
11917c478bd9Sstevel@tonic-gate 	}
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 	k->k_state &= ~(KS_NEW | KS_DELETE | KS_ADD | KS_CHANGE | KS_DEL_ADD |
11947c478bd9Sstevel@tonic-gate 	    KS_STATIC | KS_GATEWAY | KS_DELETED | KS_PRIVATE | KS_CHECK);
11957c478bd9Sstevel@tonic-gate 	if (rtm->rtm_flags & RTF_GATEWAY)
11967c478bd9Sstevel@tonic-gate 		k->k_state |= KS_GATEWAY;
11977c478bd9Sstevel@tonic-gate 	if (rtm->rtm_flags & RTF_STATIC)
11987c478bd9Sstevel@tonic-gate 		k->k_state |= KS_STATIC;
11997c478bd9Sstevel@tonic-gate 	if (rtm->rtm_flags & RTF_PRIVATE)
12007c478bd9Sstevel@tonic-gate 		k->k_state |= KS_PRIVATE;
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate 	if (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED)) {
12047c478bd9Sstevel@tonic-gate 		if (INFO_AUTHOR(info) != 0 &&
12057c478bd9Sstevel@tonic-gate 		    INFO_AUTHOR(info)->ss_family == AF_INET)
12067c478bd9Sstevel@tonic-gate 			ifp = iflookup(S_ADDR(INFO_AUTHOR(info)));
12077c478bd9Sstevel@tonic-gate 		else
12087c478bd9Sstevel@tonic-gate 			ifp = NULL;
12097c478bd9Sstevel@tonic-gate 		if (should_supply(ifp) && (ifp == NULL ||
12107c478bd9Sstevel@tonic-gate 		    !(ifp->int_state & IS_REDIRECT_OK))) {
12117c478bd9Sstevel@tonic-gate 			/*
12127c478bd9Sstevel@tonic-gate 			 * Routers are not supposed to listen to redirects,
12137c478bd9Sstevel@tonic-gate 			 * so delete it if it came via an unknown interface
12147c478bd9Sstevel@tonic-gate 			 * or the interface does not have special permission.
12157c478bd9Sstevel@tonic-gate 			 */
12167c478bd9Sstevel@tonic-gate 			k->k_state &= ~KS_DYNAMIC;
12177c478bd9Sstevel@tonic-gate 			k->k_state |= KS_DELETE;
12187c478bd9Sstevel@tonic-gate 			LIM_SEC(need_kern, 0);
12197c478bd9Sstevel@tonic-gate 			trace_act("mark for deletion redirected %s --> %s"
12207c478bd9Sstevel@tonic-gate 			    " via %s",
12217c478bd9Sstevel@tonic-gate 			    addrname(k->k_dst, k->k_mask, 0),
12227c478bd9Sstevel@tonic-gate 			    naddr_ntoa(k->k_gate),
12237c478bd9Sstevel@tonic-gate 			    ifp ? ifp->int_name : "unknown interface");
12247c478bd9Sstevel@tonic-gate 		} else {
12257c478bd9Sstevel@tonic-gate 			k->k_state |= KS_DYNAMIC;
12267c478bd9Sstevel@tonic-gate 			k->k_redirect_time = now.tv_sec;
12277c478bd9Sstevel@tonic-gate 			trace_act("accept redirected %s --> %s via %s",
12287c478bd9Sstevel@tonic-gate 			    addrname(k->k_dst, k->k_mask, 0),
12297c478bd9Sstevel@tonic-gate 			    naddr_ntoa(k->k_gate),
12307c478bd9Sstevel@tonic-gate 			    ifp ? ifp->int_name : "unknown interface");
12317c478bd9Sstevel@tonic-gate 		}
12327c478bd9Sstevel@tonic-gate 		return;
12337c478bd9Sstevel@tonic-gate 	}
12347c478bd9Sstevel@tonic-gate 
12357c478bd9Sstevel@tonic-gate 	/*
12367c478bd9Sstevel@tonic-gate 	 * If it is not a static route, quit until the next comparison
12377c478bd9Sstevel@tonic-gate 	 * between the kernel and daemon tables, when it will be deleted.
12387c478bd9Sstevel@tonic-gate 	 */
12397c478bd9Sstevel@tonic-gate 	if (!(k->k_state & KS_STATIC)) {
12407c478bd9Sstevel@tonic-gate 		if (!(k->k_state & (KS_IF|KS_DEPRE_IF|KS_FILE)))
12417c478bd9Sstevel@tonic-gate 			k->k_state |= KS_DELETE;
12427c478bd9Sstevel@tonic-gate 		LIM_SEC(need_kern, k->k_keep);
12437c478bd9Sstevel@tonic-gate 		return;
12447c478bd9Sstevel@tonic-gate 	}
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 	/*
12477c478bd9Sstevel@tonic-gate 	 * Put static routes with real metrics into the daemon table so
12487c478bd9Sstevel@tonic-gate 	 * they can be advertised.
12497c478bd9Sstevel@tonic-gate 	 */
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 	kern_check_static(k, ifp);
12527c478bd9Sstevel@tonic-gate }
12537c478bd9Sstevel@tonic-gate 
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate /* deal with packet loss */
12567c478bd9Sstevel@tonic-gate static void
12577c478bd9Sstevel@tonic-gate rtm_lose(struct rt_msghdr *rtm, struct rt_addrinfo *info)
12587c478bd9Sstevel@tonic-gate {
12597c478bd9Sstevel@tonic-gate 	if (INFO_GATE(info) == NULL || INFO_GATE(info)->ss_family != AF_INET) {
12607c478bd9Sstevel@tonic-gate 		trace_act("ignore %s without gateway",
12617c478bd9Sstevel@tonic-gate 		    rtm_type_name(rtm->rtm_type));
12627c478bd9Sstevel@tonic-gate 		age(0);
12637c478bd9Sstevel@tonic-gate 		return;
12647c478bd9Sstevel@tonic-gate 	}
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate 	if (rdisc_ok)
12677c478bd9Sstevel@tonic-gate 		rdisc_age(S_ADDR(INFO_GATE(info)));
12687c478bd9Sstevel@tonic-gate 	age(S_ADDR(INFO_GATE(info)));
12697c478bd9Sstevel@tonic-gate }
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate /*
12737c478bd9Sstevel@tonic-gate  * Make the gateway slot of an info structure point to something
12747c478bd9Sstevel@tonic-gate  * useful.  If it is not already useful, but it specifies an interface,
12757c478bd9Sstevel@tonic-gate  * then fill in the sockaddr_in provided and point it there.
12767c478bd9Sstevel@tonic-gate  */
12777c478bd9Sstevel@tonic-gate static int
12787c478bd9Sstevel@tonic-gate get_info_gate(struct sockaddr_storage **ssp, struct sockaddr_in *sin)
12797c478bd9Sstevel@tonic-gate {
12807c478bd9Sstevel@tonic-gate 	struct sockaddr_dl *sdl = (struct sockaddr_dl *)*ssp;
12817c478bd9Sstevel@tonic-gate 	struct interface *ifp;
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate 	if (sdl == NULL)
12847c478bd9Sstevel@tonic-gate 		return (0);
12857c478bd9Sstevel@tonic-gate 	if ((sdl)->sdl_family == AF_INET)
12867c478bd9Sstevel@tonic-gate 		return (1);
12877c478bd9Sstevel@tonic-gate 	if ((sdl)->sdl_family != AF_LINK)
12887c478bd9Sstevel@tonic-gate 		return (0);
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate 	ifp = ifwithindex(sdl->sdl_index, _B_TRUE);
12917c478bd9Sstevel@tonic-gate 	if (ifp == NULL)
12927c478bd9Sstevel@tonic-gate 		return (0);
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 	sin->sin_addr.s_addr = ifp->int_addr;
12957c478bd9Sstevel@tonic-gate 	sin->sin_family = AF_INET;
12967c478bd9Sstevel@tonic-gate 	/* LINTED */
12977c478bd9Sstevel@tonic-gate 	*ssp = (struct sockaddr_storage *)sin;
12987c478bd9Sstevel@tonic-gate 
12997c478bd9Sstevel@tonic-gate 	return (1);
13007c478bd9Sstevel@tonic-gate }
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate /*
13047c478bd9Sstevel@tonic-gate  * Clean the kernel table by copying it to the daemon image.
13057c478bd9Sstevel@tonic-gate  * Eventually the daemon will delete any extra routes.
13067c478bd9Sstevel@tonic-gate  */
13077c478bd9Sstevel@tonic-gate void
13087c478bd9Sstevel@tonic-gate sync_kern(void)
13097c478bd9Sstevel@tonic-gate {
13107c478bd9Sstevel@tonic-gate 	int i;
13117c478bd9Sstevel@tonic-gate 	struct khash *k;
13127c478bd9Sstevel@tonic-gate 	struct {
13137c478bd9Sstevel@tonic-gate 		struct T_optmgmt_req req;
13147c478bd9Sstevel@tonic-gate 		struct opthdr hdr;
13157c478bd9Sstevel@tonic-gate 	} req;
13167c478bd9Sstevel@tonic-gate 	union {
13177c478bd9Sstevel@tonic-gate 		struct T_optmgmt_ack ack;
13187c478bd9Sstevel@tonic-gate 		unsigned char space[64];
13197c478bd9Sstevel@tonic-gate 	} ack;
13207c478bd9Sstevel@tonic-gate 	struct opthdr *rh;
13217c478bd9Sstevel@tonic-gate 	struct strbuf cbuf, dbuf;
13227c478bd9Sstevel@tonic-gate 	int ipfd, nroutes, flags, r;
13237c478bd9Sstevel@tonic-gate 	mib2_ipRouteEntry_t routes[8];
13247c478bd9Sstevel@tonic-gate 	mib2_ipRouteEntry_t *rp;
13257c478bd9Sstevel@tonic-gate 	struct rt_msghdr rtm;
13267c478bd9Sstevel@tonic-gate 	struct rt_addrinfo info;
13277c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin_dst;
13287c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin_gate;
13297c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin_mask;
13307c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin_author;
13317c478bd9Sstevel@tonic-gate 	struct interface *ifp;
13327c478bd9Sstevel@tonic-gate 	char ifname[LIFNAMSIZ + 1];
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 	for (i = 0; i < KHASH_SIZE; i++) {
13357c478bd9Sstevel@tonic-gate 		for (k = khash_bins[i]; k != NULL; k = k->k_next) {
13367c478bd9Sstevel@tonic-gate 			if (!(k->k_state & (KS_IF|KS_DEPRE_IF)))
13377c478bd9Sstevel@tonic-gate 				k->k_state |= KS_CHECK;
13387c478bd9Sstevel@tonic-gate 		}
13397c478bd9Sstevel@tonic-gate 	}
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate 	ipfd = open(IP_DEV_NAME, O_RDWR);
13427c478bd9Sstevel@tonic-gate 	if (ipfd == -1) {
13437c478bd9Sstevel@tonic-gate 		msglog("open " IP_DEV_NAME ": %s", rip_strerror(errno));
13447c478bd9Sstevel@tonic-gate 		goto hash_clean;
13457c478bd9Sstevel@tonic-gate 	}
13467c478bd9Sstevel@tonic-gate 
13477c478bd9Sstevel@tonic-gate 	req.req.PRIM_type = T_OPTMGMT_REQ;
13487c478bd9Sstevel@tonic-gate 	req.req.OPT_offset = (caddr_t)&req.hdr - (caddr_t)&req;
13497c478bd9Sstevel@tonic-gate 	req.req.OPT_length = sizeof (req.hdr);
13507c478bd9Sstevel@tonic-gate 	req.req.MGMT_flags = T_CURRENT;
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate 	req.hdr.level = MIB2_IP;
13537c478bd9Sstevel@tonic-gate 	req.hdr.name = 0;
13547c478bd9Sstevel@tonic-gate 	req.hdr.len = 0;
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 	cbuf.buf = (caddr_t)&req;
13577c478bd9Sstevel@tonic-gate 	cbuf.len = sizeof (req);
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	if (putmsg(ipfd, &cbuf, NULL, 0) == -1) {
13607c478bd9Sstevel@tonic-gate 		msglog("T_OPTMGMT_REQ putmsg: %s", rip_strerror(errno));
13617c478bd9Sstevel@tonic-gate 		goto hash_clean;
13627c478bd9Sstevel@tonic-gate 	}
13637c478bd9Sstevel@tonic-gate 
13647c478bd9Sstevel@tonic-gate 	for (;;) {
13657c478bd9Sstevel@tonic-gate 		cbuf.buf = (caddr_t)&ack;
13667c478bd9Sstevel@tonic-gate 		cbuf.maxlen = sizeof (ack);
13677c478bd9Sstevel@tonic-gate 		dbuf.buf = (caddr_t)routes;
13687c478bd9Sstevel@tonic-gate 		dbuf.maxlen = sizeof (routes);
13697c478bd9Sstevel@tonic-gate 		flags = 0;
13707c478bd9Sstevel@tonic-gate 		r = getmsg(ipfd, &cbuf, &dbuf, &flags);
13717c478bd9Sstevel@tonic-gate 		if (r == -1) {
13727c478bd9Sstevel@tonic-gate 			msglog("T_OPTMGMT_REQ getmsg: %s", rip_strerror(errno));
13737c478bd9Sstevel@tonic-gate 			goto hash_clean;
13747c478bd9Sstevel@tonic-gate 		}
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate 		if (cbuf.len < sizeof (struct T_optmgmt_ack) ||
13777c478bd9Sstevel@tonic-gate 		    ack.ack.PRIM_type != T_OPTMGMT_ACK ||
13787c478bd9Sstevel@tonic-gate 		    ack.ack.MGMT_flags != T_SUCCESS ||
13797c478bd9Sstevel@tonic-gate 		    ack.ack.OPT_length < sizeof (struct opthdr)) {
13807c478bd9Sstevel@tonic-gate 			msglog("bad T_OPTMGMT response; len=%d prim=%d "
13817c478bd9Sstevel@tonic-gate 			    "flags=%d optlen=%d", cbuf.len, ack.ack.PRIM_type,
13827c478bd9Sstevel@tonic-gate 			    ack.ack.MGMT_flags, ack.ack.OPT_length);
13837c478bd9Sstevel@tonic-gate 			goto hash_clean;
13847c478bd9Sstevel@tonic-gate 		}
13857c478bd9Sstevel@tonic-gate 		/* LINTED */
13867c478bd9Sstevel@tonic-gate 		rh = (struct opthdr *)((caddr_t)&ack + ack.ack.OPT_offset);
13877c478bd9Sstevel@tonic-gate 		if (rh->level == 0 && rh->name == 0) {
13887c478bd9Sstevel@tonic-gate 			break;
13897c478bd9Sstevel@tonic-gate 		}
13907c478bd9Sstevel@tonic-gate 		if (rh->level != MIB2_IP || rh->name != MIB2_IP_21) {
13917c478bd9Sstevel@tonic-gate 			while (r == MOREDATA) {
13927c478bd9Sstevel@tonic-gate 				r = getmsg(ipfd, NULL, &dbuf, &flags);
13937c478bd9Sstevel@tonic-gate 			}
13947c478bd9Sstevel@tonic-gate 			continue;
13957c478bd9Sstevel@tonic-gate 		}
13967c478bd9Sstevel@tonic-gate 		break;
13977c478bd9Sstevel@tonic-gate 	}
13987c478bd9Sstevel@tonic-gate 
13997c478bd9Sstevel@tonic-gate 	(void) memset(&rtm, 0, sizeof (rtm));
14007c478bd9Sstevel@tonic-gate 	(void) memset(&info, 0, sizeof (info));
14017c478bd9Sstevel@tonic-gate 	(void) memset(&sin_dst, 0, sizeof (sin_dst));
14027c478bd9Sstevel@tonic-gate 	(void) memset(&sin_gate, 0, sizeof (sin_gate));
14037c478bd9Sstevel@tonic-gate 	(void) memset(&sin_mask, 0, sizeof (sin_mask));
14047c478bd9Sstevel@tonic-gate 	(void) memset(&sin_author, 0, sizeof (sin_author));
14057c478bd9Sstevel@tonic-gate 	sin_dst.sin_family = AF_INET;
14067c478bd9Sstevel@tonic-gate 	/* LINTED */
14077c478bd9Sstevel@tonic-gate 	info.rti_info[RTAX_DST] = (struct sockaddr_storage *)&sin_dst;
14087c478bd9Sstevel@tonic-gate 	sin_gate.sin_family = AF_INET;
14097c478bd9Sstevel@tonic-gate 	/* LINTED */
14107c478bd9Sstevel@tonic-gate 	info.rti_info[RTAX_GATEWAY] = (struct sockaddr_storage *)&sin_gate;
14117c478bd9Sstevel@tonic-gate 	sin_mask.sin_family = AF_INET;
14127c478bd9Sstevel@tonic-gate 	/* LINTED */
14137c478bd9Sstevel@tonic-gate 	info.rti_info[RTAX_NETMASK] = (struct sockaddr_storage *)&sin_mask;
14147c478bd9Sstevel@tonic-gate 	sin_dst.sin_family = AF_INET;
14157c478bd9Sstevel@tonic-gate 	/* LINTED */
14167c478bd9Sstevel@tonic-gate 	info.rti_info[RTAX_AUTHOR] = (struct sockaddr_storage *)&sin_author;
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate 	for (;;) {
14197c478bd9Sstevel@tonic-gate 		nroutes = dbuf.len / sizeof (mib2_ipRouteEntry_t);
14207c478bd9Sstevel@tonic-gate 		for (rp = routes; nroutes > 0; ++rp, nroutes--) {
14217c478bd9Sstevel@tonic-gate 
14227c478bd9Sstevel@tonic-gate 			/*
14237c478bd9Sstevel@tonic-gate 			 * Ignore IRE cache, broadcast, and local address
14247c478bd9Sstevel@tonic-gate 			 * entries; they're not subject to routing socket
14257c478bd9Sstevel@tonic-gate 			 * control.
14267c478bd9Sstevel@tonic-gate 			 */
14277c478bd9Sstevel@tonic-gate 			if (rp->ipRouteInfo.re_ire_type &
14287c478bd9Sstevel@tonic-gate 			    (IRE_BROADCAST | IRE_CACHE | IRE_LOCAL))
14297c478bd9Sstevel@tonic-gate 				continue;
14307c478bd9Sstevel@tonic-gate 
1431*f9aa3e1eSkcpoon 			/* ignore multicast and link local addresses */
1432*f9aa3e1eSkcpoon 			if (IN_MULTICAST(ntohl(rp->ipRouteDest)) ||
1433*f9aa3e1eSkcpoon 			    IN_LINKLOCAL(ntohl(rp->ipRouteDest))) {
14347c478bd9Sstevel@tonic-gate 				continue;
1435*f9aa3e1eSkcpoon 			}
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate #ifdef DEBUG_KERNEL_ROUTE_READ
14397c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "route type %d, ire type %08X, "
14407c478bd9Sstevel@tonic-gate 			    "flags %08X: %s", rp->ipRouteType,
14417c478bd9Sstevel@tonic-gate 			    rp->ipRouteInfo.re_ire_type,
14427c478bd9Sstevel@tonic-gate 			    rp->ipRouteInfo.re_flags,
14437c478bd9Sstevel@tonic-gate 			    naddr_ntoa(rp->ipRouteDest));
14447c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, " %s",
14457c478bd9Sstevel@tonic-gate 			    naddr_ntoa(rp->ipRouteMask));
14467c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, " %s\n",
14477c478bd9Sstevel@tonic-gate 			    naddr_ntoa(rp->ipRouteNextHop));
14487c478bd9Sstevel@tonic-gate #endif
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate 			/* Fake up the needed entries */
14517c478bd9Sstevel@tonic-gate 			rtm.rtm_flags = rp->ipRouteInfo.re_flags;
14527c478bd9Sstevel@tonic-gate 			rtm.rtm_type = RTM_GET;
14537c478bd9Sstevel@tonic-gate 			rtm.rtm_rmx.rmx_hopcount = rp->ipRouteMetric1;
14547c478bd9Sstevel@tonic-gate 
14557c478bd9Sstevel@tonic-gate 			(void) memset(ifname, 0, sizeof (ifname));
14567c478bd9Sstevel@tonic-gate 			if (rp->ipRouteIfIndex.o_length <
14577c478bd9Sstevel@tonic-gate 			    sizeof (rp->ipRouteIfIndex.o_bytes))
14587c478bd9Sstevel@tonic-gate 				rp->ipRouteIfIndex.o_bytes[
14597c478bd9Sstevel@tonic-gate 				    rp->ipRouteIfIndex.o_length] = '\0';
14607c478bd9Sstevel@tonic-gate 				(void) strncpy(ifname,
14617c478bd9Sstevel@tonic-gate 				    rp->ipRouteIfIndex.o_bytes,
14627c478bd9Sstevel@tonic-gate 				    sizeof (ifname));
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate 			/*
14657c478bd9Sstevel@tonic-gate 			 * First try to match up on gwkludge entries
14667c478bd9Sstevel@tonic-gate 			 * before trying to match ifp by name.
14677c478bd9Sstevel@tonic-gate 			 */
14687c478bd9Sstevel@tonic-gate 			if ((ifp = gwkludge_iflookup(rp->ipRouteDest,
1469e79ff038Sbw 			    rp->ipRouteNextHop,
1470e1fa7f4dSsowmini 			    ntohl(rp->ipRouteMask))) == NULL) {
14717c478bd9Sstevel@tonic-gate 				ifp = ifwithname(ifname);
1472e1fa7f4dSsowmini 				if (ifp != NULL && ifp->int_phys != NULL) {
1473e1fa7f4dSsowmini 					ifp = ifwithname(
1474e1fa7f4dSsowmini 					    ifp->int_phys->phyi_name);
1475e1fa7f4dSsowmini 				}
1476e1fa7f4dSsowmini 			}
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate 			info.rti_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
14797c478bd9Sstevel@tonic-gate 			if (rp->ipRouteInfo.re_ire_type & IRE_HOST_REDIRECT)
14807c478bd9Sstevel@tonic-gate 				info.rti_addrs |= RTA_AUTHOR;
14817c478bd9Sstevel@tonic-gate 			sin_dst.sin_addr.s_addr = rp->ipRouteDest;
14827c478bd9Sstevel@tonic-gate 			sin_gate.sin_addr.s_addr = rp->ipRouteNextHop;
14837c478bd9Sstevel@tonic-gate 			sin_mask.sin_addr.s_addr = rp->ipRouteMask;
14847c478bd9Sstevel@tonic-gate 			sin_author.sin_addr.s_addr =
14857c478bd9Sstevel@tonic-gate 			    rp->ipRouteInfo.re_src_addr;
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 			/*
14887c478bd9Sstevel@tonic-gate 			 * Note static routes and interface routes, and also
14897c478bd9Sstevel@tonic-gate 			 * preload the image of the kernel table so that
14907c478bd9Sstevel@tonic-gate 			 * we can later clean it, as well as avoid making
14917c478bd9Sstevel@tonic-gate 			 * unneeded changes.  Keep the old kernel routes for a
14927c478bd9Sstevel@tonic-gate 			 * few seconds to allow a RIP or router-discovery
14937c478bd9Sstevel@tonic-gate 			 * response to be heard.
14947c478bd9Sstevel@tonic-gate 			 */
14957c478bd9Sstevel@tonic-gate 			rtm_add(&rtm, &info, MAX_WAITTIME,
14967c478bd9Sstevel@tonic-gate 			    ((rp->ipRouteInfo.re_ire_type &
14977c478bd9Sstevel@tonic-gate 			    (IRE_INTERFACE|IRE_LOOPBACK)) != 0), ifp);
14987c478bd9Sstevel@tonic-gate 		}
14997c478bd9Sstevel@tonic-gate 		if (r == 0) {
15007c478bd9Sstevel@tonic-gate 			break;
15017c478bd9Sstevel@tonic-gate 		}
15027c478bd9Sstevel@tonic-gate 		r = getmsg(ipfd, NULL, &dbuf, &flags);
15037c478bd9Sstevel@tonic-gate 	}
15047c478bd9Sstevel@tonic-gate 
15057c478bd9Sstevel@tonic-gate hash_clean:
15067c478bd9Sstevel@tonic-gate 	if (ipfd != -1)
15077c478bd9Sstevel@tonic-gate 		(void) close(ipfd);
15087c478bd9Sstevel@tonic-gate 	for (i = 0; i < KHASH_SIZE; i++) {
15097c478bd9Sstevel@tonic-gate 		for (k = khash_bins[i]; k != NULL; k = k->k_next) {
15107c478bd9Sstevel@tonic-gate 
15117c478bd9Sstevel@tonic-gate 			/*
15127c478bd9Sstevel@tonic-gate 			 * KS_DELETED routes have been removed from the
15137c478bd9Sstevel@tonic-gate 			 * kernel, but we keep them around for reasons
15147c478bd9Sstevel@tonic-gate 			 * stated in del_static(), so we skip the check
15157c478bd9Sstevel@tonic-gate 			 * for KS_DELETED routes here.
15167c478bd9Sstevel@tonic-gate 			 */
15177c478bd9Sstevel@tonic-gate 			if ((k->k_state & (KS_CHECK|KS_DELETED)) == KS_CHECK) {
15187c478bd9Sstevel@tonic-gate 
1519*f9aa3e1eSkcpoon 				if (!(k->k_state & KS_DYNAMIC)) {
1520*f9aa3e1eSkcpoon 					writelog(LOG_WARNING,
1521*f9aa3e1eSkcpoon 					    "%s --> %s disappeared from kernel",
1522*f9aa3e1eSkcpoon 					    addrname(k->k_dst, k->k_mask, 0),
1523*f9aa3e1eSkcpoon 					    naddr_ntoa(k->k_gate));
1524*f9aa3e1eSkcpoon 				}
15257c478bd9Sstevel@tonic-gate 				del_static(k->k_dst, k->k_mask, k->k_gate,
15267c478bd9Sstevel@tonic-gate 				    k->k_ifp, 1);
15277c478bd9Sstevel@tonic-gate 
15287c478bd9Sstevel@tonic-gate 			}
15297c478bd9Sstevel@tonic-gate 		}
15307c478bd9Sstevel@tonic-gate 	}
15317c478bd9Sstevel@tonic-gate }
15327c478bd9Sstevel@tonic-gate 
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate /* Listen to announcements from the kernel */
15357c478bd9Sstevel@tonic-gate void
15367c478bd9Sstevel@tonic-gate read_rt(void)
15377c478bd9Sstevel@tonic-gate {
15387c478bd9Sstevel@tonic-gate 	long cc;
15397c478bd9Sstevel@tonic-gate 	struct interface *ifp;
15407c478bd9Sstevel@tonic-gate 	struct sockaddr_in gate_sin;
15417c478bd9Sstevel@tonic-gate 	in_addr_t mask, gate;
15427c478bd9Sstevel@tonic-gate 	union {
15437c478bd9Sstevel@tonic-gate 		struct {
15447c478bd9Sstevel@tonic-gate 			struct rt_msghdr rtm;
15457c478bd9Sstevel@tonic-gate 			struct sockaddr_storage addrs[RTA_NUMBITS];
15467c478bd9Sstevel@tonic-gate 		} r;
15477c478bd9Sstevel@tonic-gate 		struct if_msghdr ifm;
15487c478bd9Sstevel@tonic-gate 	} m;
15497c478bd9Sstevel@tonic-gate 	char str[100], *strp;
15507c478bd9Sstevel@tonic-gate 	struct rt_addrinfo info;
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 
15537c478bd9Sstevel@tonic-gate 	for (;;) {
15547c478bd9Sstevel@tonic-gate 		cc = read(rt_sock, &m, sizeof (m));
15557c478bd9Sstevel@tonic-gate 		if (cc <= 0) {
15567c478bd9Sstevel@tonic-gate 			if (cc < 0 && errno != EWOULDBLOCK)
15577c478bd9Sstevel@tonic-gate 				LOGERR("read(rt_sock)");
15587c478bd9Sstevel@tonic-gate 			return;
15597c478bd9Sstevel@tonic-gate 		}
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 		if (TRACERTS)
15627c478bd9Sstevel@tonic-gate 			dump_rt_msg("read", &m.r.rtm, cc);
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 		if (cc < m.r.rtm.rtm_msglen) {
15657c478bd9Sstevel@tonic-gate 			msglog("routing message truncated (%d < %d)",
15667c478bd9Sstevel@tonic-gate 			    cc, m.r.rtm.rtm_msglen);
15677c478bd9Sstevel@tonic-gate 		}
15687c478bd9Sstevel@tonic-gate 
15697c478bd9Sstevel@tonic-gate 		if (m.r.rtm.rtm_version != RTM_VERSION) {
15707c478bd9Sstevel@tonic-gate 			msglog("bogus routing message version %d",
15717c478bd9Sstevel@tonic-gate 			    m.r.rtm.rtm_version);
15727c478bd9Sstevel@tonic-gate 			continue;
15737c478bd9Sstevel@tonic-gate 		}
15747c478bd9Sstevel@tonic-gate 
15757c478bd9Sstevel@tonic-gate 		ifp = NULL;
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 		if (m.r.rtm.rtm_type == RTM_IFINFO ||
15787c478bd9Sstevel@tonic-gate 		    m.r.rtm.rtm_type == RTM_NEWADDR ||
15797c478bd9Sstevel@tonic-gate 		    m.r.rtm.rtm_type == RTM_DELADDR) {
15807c478bd9Sstevel@tonic-gate 			strp = if_bit_string(m.ifm.ifm_flags, _B_TRUE);
15817c478bd9Sstevel@tonic-gate 			if (strp == NULL) {
15827c478bd9Sstevel@tonic-gate 				strp = str;
15837c478bd9Sstevel@tonic-gate 				(void) sprintf(str, "%#x", m.ifm.ifm_flags);
15847c478bd9Sstevel@tonic-gate 			}
15857c478bd9Sstevel@tonic-gate 			ifp = ifwithindex(m.ifm.ifm_index,
15867c478bd9Sstevel@tonic-gate 			    m.r.rtm.rtm_type != RTM_DELADDR);
15877c478bd9Sstevel@tonic-gate 			if (ifp == NULL) {
15887c478bd9Sstevel@tonic-gate 				char ifname[LIFNAMSIZ], *ifnamep;
15897c478bd9Sstevel@tonic-gate 
15907c478bd9Sstevel@tonic-gate 				ifnamep = if_indextoname(m.ifm.ifm_index,
15917c478bd9Sstevel@tonic-gate 				    ifname);
15927c478bd9Sstevel@tonic-gate 				if (ifnamep == NULL) {
15937c478bd9Sstevel@tonic-gate 					trace_act("note %s with flags %s"
15947c478bd9Sstevel@tonic-gate 					    " for unknown interface index #%d",
15957c478bd9Sstevel@tonic-gate 					    rtm_type_name(m.r.rtm.rtm_type),
15967c478bd9Sstevel@tonic-gate 					    strp, m.ifm.ifm_index);
15977c478bd9Sstevel@tonic-gate 				} else {
15987c478bd9Sstevel@tonic-gate 					trace_act("note %s with flags %s"
15997c478bd9Sstevel@tonic-gate 					    " for unknown interface %s",
16007c478bd9Sstevel@tonic-gate 					    rtm_type_name(m.r.rtm.rtm_type),
16017c478bd9Sstevel@tonic-gate 					    strp, ifnamep);
16027c478bd9Sstevel@tonic-gate 				}
16037c478bd9Sstevel@tonic-gate 			} else {
16047c478bd9Sstevel@tonic-gate 				trace_act("note %s with flags %s for %s",
16057c478bd9Sstevel@tonic-gate 				    rtm_type_name(m.r.rtm.rtm_type),
16067c478bd9Sstevel@tonic-gate 				    strp, ifp->int_name);
16077c478bd9Sstevel@tonic-gate 			}
16087c478bd9Sstevel@tonic-gate 			if (strp != str)
16097c478bd9Sstevel@tonic-gate 				free(strp);
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 			/*
16127c478bd9Sstevel@tonic-gate 			 * After being informed of a change to an interface,
16137c478bd9Sstevel@tonic-gate 			 * check them all now if the check would otherwise
16147c478bd9Sstevel@tonic-gate 			 * be a long time from now, if the interface is
16157c478bd9Sstevel@tonic-gate 			 * not known, or if the interface has been turned
16167c478bd9Sstevel@tonic-gate 			 * off or on.
16177c478bd9Sstevel@tonic-gate 			 */
16187c478bd9Sstevel@tonic-gate 			if (ifscan_timer.tv_sec-now.tv_sec >=
16197c478bd9Sstevel@tonic-gate 			    CHECK_BAD_INTERVAL || ifp == NULL ||
16207c478bd9Sstevel@tonic-gate 			    ((ifp->int_if_flags ^ m.ifm.ifm_flags) &
1621*f9aa3e1eSkcpoon 			    IFF_UP) != 0)
16227c478bd9Sstevel@tonic-gate 				ifscan_timer.tv_sec = now.tv_sec;
16237c478bd9Sstevel@tonic-gate 			continue;
16247c478bd9Sstevel@tonic-gate 		} else {
16257c478bd9Sstevel@tonic-gate 			if (m.r.rtm.rtm_index != 0)
16267c478bd9Sstevel@tonic-gate 				ifp = ifwithindex(m.r.rtm.rtm_index, 1);
16277c478bd9Sstevel@tonic-gate 		}
16287c478bd9Sstevel@tonic-gate 
16297c478bd9Sstevel@tonic-gate 		(void) strlcpy(str, rtm_type_name(m.r.rtm.rtm_type),
16307c478bd9Sstevel@tonic-gate 		    sizeof (str));
16317c478bd9Sstevel@tonic-gate 		strp = &str[strlen(str)];
16327c478bd9Sstevel@tonic-gate 		if (m.r.rtm.rtm_type <= RTM_CHANGE)
16337c478bd9Sstevel@tonic-gate 			strp += snprintf(strp, sizeof (str) - (strp - str),
16347c478bd9Sstevel@tonic-gate 			    " from pid %d", (int)m.r.rtm.rtm_pid);
16357c478bd9Sstevel@tonic-gate 
16367c478bd9Sstevel@tonic-gate 		/* LINTED */
16377c478bd9Sstevel@tonic-gate 		(void) rt_xaddrs(&info, (struct sockaddr_storage *)(&m.r.rtm +
16387c478bd9Sstevel@tonic-gate 		    1), (char *)&m + cc, m.r.rtm.rtm_addrs);
16397c478bd9Sstevel@tonic-gate 
16407c478bd9Sstevel@tonic-gate 		if (INFO_DST(&info) == 0) {
16417c478bd9Sstevel@tonic-gate 			trace_act("ignore %s without dst", str);
16427c478bd9Sstevel@tonic-gate 			continue;
16437c478bd9Sstevel@tonic-gate 		}
16447c478bd9Sstevel@tonic-gate 
16457c478bd9Sstevel@tonic-gate 		if (INFO_DST(&info)->ss_family != AF_INET) {
16467c478bd9Sstevel@tonic-gate 			trace_act("ignore %s for AF %d", str,
16477c478bd9Sstevel@tonic-gate 			    INFO_DST(&info)->ss_family);
16487c478bd9Sstevel@tonic-gate 			continue;
16497c478bd9Sstevel@tonic-gate 		}
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 		mask = ((INFO_MASK(&info) != 0) ?
16527c478bd9Sstevel@tonic-gate 		    ntohl(S_ADDR(INFO_MASK(&info))) :
16537c478bd9Sstevel@tonic-gate 		    (m.r.rtm.rtm_flags & RTF_HOST) ?
16547c478bd9Sstevel@tonic-gate 		    HOST_MASK : std_mask(S_ADDR(INFO_DST(&info))));
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 		strp += snprintf(strp, sizeof (str) - (strp - str), ": %s",
16577c478bd9Sstevel@tonic-gate 		    addrname(S_ADDR(INFO_DST(&info)), mask, 0));
16587c478bd9Sstevel@tonic-gate 
1659*f9aa3e1eSkcpoon 		if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info)))) ||
1660*f9aa3e1eSkcpoon 		    IN_LINKLOCAL(ntohl(S_ADDR(INFO_DST(&info))))) {
1661*f9aa3e1eSkcpoon 			trace_act("ignore multicast/link local %s", str);
16627c478bd9Sstevel@tonic-gate 			continue;
16637c478bd9Sstevel@tonic-gate 		}
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 		if (m.r.rtm.rtm_flags & RTF_LLINFO) {
16667c478bd9Sstevel@tonic-gate 			trace_act("ignore ARP %s", str);
16677c478bd9Sstevel@tonic-gate 			continue;
16687c478bd9Sstevel@tonic-gate 		}
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate 		if (get_info_gate(&INFO_GATE(&info), &gate_sin)) {
16717c478bd9Sstevel@tonic-gate 			gate = S_ADDR(INFO_GATE(&info));
16727c478bd9Sstevel@tonic-gate 			strp += snprintf(strp, sizeof (str) - (strp - str),
16737c478bd9Sstevel@tonic-gate 			    " --> %s", naddr_ntoa(gate));
16747c478bd9Sstevel@tonic-gate 		} else {
16757c478bd9Sstevel@tonic-gate 			gate = 0;
16767c478bd9Sstevel@tonic-gate 		}
16777c478bd9Sstevel@tonic-gate 
16787c478bd9Sstevel@tonic-gate 		if (INFO_AUTHOR(&info) != 0)
16797c478bd9Sstevel@tonic-gate 			strp += snprintf(strp, sizeof (str) - (strp - str),
16807c478bd9Sstevel@tonic-gate 			    " by authority of %s",
16817c478bd9Sstevel@tonic-gate 			    saddr_ntoa(INFO_AUTHOR(&info)));
16827c478bd9Sstevel@tonic-gate 
16837c478bd9Sstevel@tonic-gate 		switch (m.r.rtm.rtm_type) {
16847c478bd9Sstevel@tonic-gate 		case RTM_ADD:
16857c478bd9Sstevel@tonic-gate 		case RTM_CHANGE:
16867c478bd9Sstevel@tonic-gate 		case RTM_REDIRECT:
16877c478bd9Sstevel@tonic-gate 			if (m.r.rtm.rtm_errno != 0) {
16887c478bd9Sstevel@tonic-gate 				trace_act("ignore %s with \"%s\" error",
16897c478bd9Sstevel@tonic-gate 				    str, rip_strerror(m.r.rtm.rtm_errno));
16907c478bd9Sstevel@tonic-gate 			} else {
16917c478bd9Sstevel@tonic-gate 				trace_act("%s", str);
16927c478bd9Sstevel@tonic-gate 				rtm_add(&m.r.rtm, &info, 0,
16937c478bd9Sstevel@tonic-gate 				    !(m.r.rtm.rtm_flags & RTF_GATEWAY) &&
16947c478bd9Sstevel@tonic-gate 				    m.r.rtm.rtm_type != RTM_REDIRECT, ifp);
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 			}
16977c478bd9Sstevel@tonic-gate 			break;
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 		case RTM_DELETE:
17007c478bd9Sstevel@tonic-gate 			if (m.r.rtm.rtm_errno != 0 &&
17017c478bd9Sstevel@tonic-gate 			    m.r.rtm.rtm_errno != ESRCH) {
17027c478bd9Sstevel@tonic-gate 				trace_act("ignore %s with \"%s\" error",
17037c478bd9Sstevel@tonic-gate 				    str, rip_strerror(m.r.rtm.rtm_errno));
17047c478bd9Sstevel@tonic-gate 			} else {
17057c478bd9Sstevel@tonic-gate 				trace_act("%s", str);
17067c478bd9Sstevel@tonic-gate 				del_static(S_ADDR(INFO_DST(&info)), mask,
17077c478bd9Sstevel@tonic-gate 				    gate, ifp, 1);
17087c478bd9Sstevel@tonic-gate 			}
17097c478bd9Sstevel@tonic-gate 			break;
17107c478bd9Sstevel@tonic-gate 
17117c478bd9Sstevel@tonic-gate 		case RTM_LOSING:
17127c478bd9Sstevel@tonic-gate 			trace_act("%s", str);
17137c478bd9Sstevel@tonic-gate 			rtm_lose(&m.r.rtm, &info);
17147c478bd9Sstevel@tonic-gate 			break;
17157c478bd9Sstevel@tonic-gate 
17167c478bd9Sstevel@tonic-gate 		default:
17177c478bd9Sstevel@tonic-gate 			trace_act("ignore %s", str);
17187c478bd9Sstevel@tonic-gate 			break;
17197c478bd9Sstevel@tonic-gate 		}
17207c478bd9Sstevel@tonic-gate 	}
17217c478bd9Sstevel@tonic-gate }
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate /*
17257c478bd9Sstevel@tonic-gate  * Disassemble a routing message.  The result is an array of pointers
17267c478bd9Sstevel@tonic-gate  * to sockaddr_storage structures stored in the info argument.
17277c478bd9Sstevel@tonic-gate  *
17287c478bd9Sstevel@tonic-gate  * ss is a pointer to the beginning of the data following the
17297c478bd9Sstevel@tonic-gate  * rt_msghdr contained in the routing socket message, which consists
17307c478bd9Sstevel@tonic-gate  * of a string of concatenated sockaddr structure of different types.
173145916cd2Sjpk  *
173245916cd2Sjpk  * Extended attributes can be appended at the end of the list.
17337c478bd9Sstevel@tonic-gate  */
17347c478bd9Sstevel@tonic-gate static int
17357c478bd9Sstevel@tonic-gate rt_xaddrs(struct rt_addrinfo *info,
17367c478bd9Sstevel@tonic-gate     struct sockaddr_storage *ss,
17377c478bd9Sstevel@tonic-gate     char *lim,
17387c478bd9Sstevel@tonic-gate     int addrs)
17397c478bd9Sstevel@tonic-gate {
17407c478bd9Sstevel@tonic-gate 	int retv = 0;
17417c478bd9Sstevel@tonic-gate 	int i;
17427c478bd9Sstevel@tonic-gate 	int abit;
17437c478bd9Sstevel@tonic-gate 	int complaints;
17447c478bd9Sstevel@tonic-gate 	static int prev_complaints;
17457c478bd9Sstevel@tonic-gate 
17467c478bd9Sstevel@tonic-gate #define	XBAD_AF		0x1
17477c478bd9Sstevel@tonic-gate #define	XBAD_SHORT	0x2
17487c478bd9Sstevel@tonic-gate #define	XBAD_LONG	0x4
17497c478bd9Sstevel@tonic-gate 
17507c478bd9Sstevel@tonic-gate 	(void) memset(info, 0, sizeof (*info));
17517c478bd9Sstevel@tonic-gate 	info->rti_addrs = addrs;
17527c478bd9Sstevel@tonic-gate 	complaints = 0;
17537c478bd9Sstevel@tonic-gate 	for (i = 0, abit = 1; i < RTAX_MAX && (char *)ss < lim;
17547c478bd9Sstevel@tonic-gate 	    i++, abit <<= 1) {
17557c478bd9Sstevel@tonic-gate 		if ((addrs & abit) == 0)
17567c478bd9Sstevel@tonic-gate 			continue;
17577c478bd9Sstevel@tonic-gate 		info->rti_info[i] = ss;
17587c478bd9Sstevel@tonic-gate 		/* Horrible interface here */
17597c478bd9Sstevel@tonic-gate 		switch (ss->ss_family) {
17607c478bd9Sstevel@tonic-gate 		case AF_UNIX:
17617c478bd9Sstevel@tonic-gate 			/* LINTED */
17627c478bd9Sstevel@tonic-gate 			ss = (struct sockaddr_storage *)(
17637c478bd9Sstevel@tonic-gate 			    (struct sockaddr_un *)ss + 1);
17647c478bd9Sstevel@tonic-gate 			break;
17657c478bd9Sstevel@tonic-gate 		case AF_INET:
17667c478bd9Sstevel@tonic-gate 			/* LINTED */
17677c478bd9Sstevel@tonic-gate 			ss = (struct sockaddr_storage *)(
17687c478bd9Sstevel@tonic-gate 			    (struct sockaddr_in *)ss + 1);
17697c478bd9Sstevel@tonic-gate 			break;
17707c478bd9Sstevel@tonic-gate 		case AF_LINK:
17717c478bd9Sstevel@tonic-gate 			/* LINTED */
17727c478bd9Sstevel@tonic-gate 			ss = (struct sockaddr_storage *)(
17737c478bd9Sstevel@tonic-gate 			    (struct sockaddr_dl *)ss + 1);
17747c478bd9Sstevel@tonic-gate 			break;
17757c478bd9Sstevel@tonic-gate 		case AF_INET6:
17767c478bd9Sstevel@tonic-gate 			/* LINTED */
17777c478bd9Sstevel@tonic-gate 			ss = (struct sockaddr_storage *)(
17787c478bd9Sstevel@tonic-gate 			    (struct sockaddr_in6 *)ss + 1);
17797c478bd9Sstevel@tonic-gate 			break;
17807c478bd9Sstevel@tonic-gate 		default:
17817c478bd9Sstevel@tonic-gate 			if (!(prev_complaints & XBAD_AF))
17827c478bd9Sstevel@tonic-gate 				writelog(LOG_WARNING,
17837c478bd9Sstevel@tonic-gate 				    "unknown address family %d "
17847c478bd9Sstevel@tonic-gate 				    "encountered", ss->ss_family);
17857c478bd9Sstevel@tonic-gate 			if (complaints & XBAD_AF)
17867c478bd9Sstevel@tonic-gate 				goto xaddr_done;
17877c478bd9Sstevel@tonic-gate 			/* LINTED */
17887c478bd9Sstevel@tonic-gate 			ss = (struct sockaddr_storage *)(
17897c478bd9Sstevel@tonic-gate 			    (struct sockaddr *)ss + 1);
17907c478bd9Sstevel@tonic-gate 			complaints |= XBAD_AF;
17917c478bd9Sstevel@tonic-gate 			info->rti_addrs &= abit - 1;
17927c478bd9Sstevel@tonic-gate 			addrs = info->rti_addrs;
17937c478bd9Sstevel@tonic-gate 			retv = -1;
17947c478bd9Sstevel@tonic-gate 			break;
17957c478bd9Sstevel@tonic-gate 		}
17967c478bd9Sstevel@tonic-gate 		if ((char *)ss > lim) {
17977c478bd9Sstevel@tonic-gate 			if (!(prev_complaints & XBAD_SHORT))
17987c478bd9Sstevel@tonic-gate 				msglog("sockaddr %d too short by %d "
17997c478bd9Sstevel@tonic-gate 				    "bytes", i + 1, (char *)ss - lim);
18007c478bd9Sstevel@tonic-gate 			complaints |= XBAD_SHORT;
18017c478bd9Sstevel@tonic-gate 			info->rti_info[i] = NULL;
18027c478bd9Sstevel@tonic-gate 			info->rti_addrs &= abit - 1;
18037c478bd9Sstevel@tonic-gate 			retv = -1;
18047c478bd9Sstevel@tonic-gate 			goto xaddr_done;
18057c478bd9Sstevel@tonic-gate 		}
18067c478bd9Sstevel@tonic-gate 	}
180745916cd2Sjpk 
180845916cd2Sjpk 	while (((char *)ss + sizeof (rtm_ext_t)) <= lim) {
180945916cd2Sjpk 		rtm_ext_t *tp;
181045916cd2Sjpk 		char *nxt;
181145916cd2Sjpk 
181245916cd2Sjpk 		/* LINTED: alignment */
181345916cd2Sjpk 		tp = (rtm_ext_t *)ss;
181445916cd2Sjpk 		nxt = (char *)(tp + 1) + tp->rtmex_len;
181545916cd2Sjpk 
181645916cd2Sjpk 		if (!IS_P2ALIGNED(tp->rtmex_len, sizeof (uint32_t)) ||
181745916cd2Sjpk 		    nxt > lim) {
181845916cd2Sjpk 			break;
181945916cd2Sjpk 		}
182045916cd2Sjpk 
182145916cd2Sjpk 		/* LINTED: alignment */
182245916cd2Sjpk 		ss = (struct sockaddr_storage *)nxt;
182345916cd2Sjpk 	}
182445916cd2Sjpk 
18257c478bd9Sstevel@tonic-gate 	if ((char *)ss != lim) {
182645916cd2Sjpk 		if ((char *)ss > lim) {
182745916cd2Sjpk 			if (!(prev_complaints & XBAD_SHORT))
182845916cd2Sjpk 				msglog("routing message too short by %d bytes",
182945916cd2Sjpk 				    (char *)ss - lim);
183045916cd2Sjpk 			complaints |= XBAD_SHORT;
183145916cd2Sjpk 		} else if (!(prev_complaints & XBAD_LONG)) {
18327c478bd9Sstevel@tonic-gate 			msglog("%d bytes of routing message left over",
18337c478bd9Sstevel@tonic-gate 			    lim - (char *)ss);
183445916cd2Sjpk 			complaints |= XBAD_LONG;
183545916cd2Sjpk 		}
18367c478bd9Sstevel@tonic-gate 		retv = -1;
18377c478bd9Sstevel@tonic-gate 	}
18387c478bd9Sstevel@tonic-gate xaddr_done:
18397c478bd9Sstevel@tonic-gate 	prev_complaints = complaints;
18407c478bd9Sstevel@tonic-gate 	return (retv);
18417c478bd9Sstevel@tonic-gate }
18427c478bd9Sstevel@tonic-gate 
18437c478bd9Sstevel@tonic-gate 
18447c478bd9Sstevel@tonic-gate /* after aggregating, note routes that belong in the kernel */
18457c478bd9Sstevel@tonic-gate static void
18467c478bd9Sstevel@tonic-gate kern_out(struct ag_info *ag)
18477c478bd9Sstevel@tonic-gate {
18487c478bd9Sstevel@tonic-gate 	struct khash *k;
1849e1fa7f4dSsowmini 	struct interface *ifp;
1850e1fa7f4dSsowmini 
1851e1fa7f4dSsowmini 	ifp = ag->ag_ifp;
1852e1fa7f4dSsowmini 
1853e1fa7f4dSsowmini 	if (ifp != NULL && ifp->int_phys != NULL) {
1854e1fa7f4dSsowmini 		ifp = ifwithname(ifp->int_phys->phyi_name);
1855e1fa7f4dSsowmini 	}
18567c478bd9Sstevel@tonic-gate 
18577c478bd9Sstevel@tonic-gate 	/*
18587c478bd9Sstevel@tonic-gate 	 * Do not install bad routes if they are not already present.
18597c478bd9Sstevel@tonic-gate 	 * This includes routes that had RS_NET_SYN for interfaces that
18607c478bd9Sstevel@tonic-gate 	 * recently died.
18617c478bd9Sstevel@tonic-gate 	 */
18627c478bd9Sstevel@tonic-gate 	if (ag->ag_metric == HOPCNT_INFINITY) {
18637c478bd9Sstevel@tonic-gate 		k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask,
18647c478bd9Sstevel@tonic-gate 		    ag->ag_nhop, ag->ag_ifp, NULL);
18657c478bd9Sstevel@tonic-gate 		if (k == NULL)
18667c478bd9Sstevel@tonic-gate 			return;
18677c478bd9Sstevel@tonic-gate 	} else {
18687c478bd9Sstevel@tonic-gate 		k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask, ag->ag_nhop,
1869e1fa7f4dSsowmini 		    ifp);
18707c478bd9Sstevel@tonic-gate 	}
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate 	if (k->k_state & KS_NEW) {
18737c478bd9Sstevel@tonic-gate 		/* will need to add new entry to the kernel table */
18747c478bd9Sstevel@tonic-gate 		k->k_state = KS_ADD;
18757c478bd9Sstevel@tonic-gate 		if (ag->ag_state & AGS_GATEWAY)
18767c478bd9Sstevel@tonic-gate 			k->k_state |= KS_GATEWAY;
18777c478bd9Sstevel@tonic-gate 		if (ag->ag_state & AGS_IF)
18787c478bd9Sstevel@tonic-gate 			k->k_state |= KS_IF;
18797c478bd9Sstevel@tonic-gate 		if (ag->ag_state & AGS_PASSIVE)
18807c478bd9Sstevel@tonic-gate 			k->k_state |= KS_PASSIVE;
18817c478bd9Sstevel@tonic-gate 		if (ag->ag_state & AGS_FILE)
18827c478bd9Sstevel@tonic-gate 			k->k_state |= KS_FILE;
18837c478bd9Sstevel@tonic-gate 		k->k_gate = ag->ag_nhop;
1884e1fa7f4dSsowmini 		k->k_ifp = ifp;
18857c478bd9Sstevel@tonic-gate 		k->k_metric = ag->ag_metric;
18867c478bd9Sstevel@tonic-gate 		return;
18877c478bd9Sstevel@tonic-gate 	}
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate 	if ((k->k_state & (KS_STATIC|KS_DEPRE_IF)) ||
18907c478bd9Sstevel@tonic-gate 	    ((k->k_state & (KS_IF|KS_PASSIVE)) == KS_IF)) {
18917c478bd9Sstevel@tonic-gate 		return;
18927c478bd9Sstevel@tonic-gate 	}
18937c478bd9Sstevel@tonic-gate 
18947c478bd9Sstevel@tonic-gate 	/* modify existing kernel entry if necessary */
18957c478bd9Sstevel@tonic-gate 	if (k->k_gate == ag->ag_nhop && k->k_ifp == ag->ag_ifp &&
18967c478bd9Sstevel@tonic-gate 	    k->k_metric != ag->ag_metric) {
18977c478bd9Sstevel@tonic-gate 			/*
18987c478bd9Sstevel@tonic-gate 			 * Must delete bad interface routes etc.
18997c478bd9Sstevel@tonic-gate 			 * to change them.
19007c478bd9Sstevel@tonic-gate 			 */
19017c478bd9Sstevel@tonic-gate 			if (k->k_metric == HOPCNT_INFINITY)
19027c478bd9Sstevel@tonic-gate 				k->k_state |= KS_DEL_ADD;
19037c478bd9Sstevel@tonic-gate 			k->k_gate = ag->ag_nhop;
19047c478bd9Sstevel@tonic-gate 			k->k_metric = ag->ag_metric;
19057c478bd9Sstevel@tonic-gate 			k->k_state |= KS_CHANGE;
19067c478bd9Sstevel@tonic-gate 	}
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate 	/*
19097c478bd9Sstevel@tonic-gate 	 * If the daemon thinks the route should exist, forget
19107c478bd9Sstevel@tonic-gate 	 * about any redirections.
19117c478bd9Sstevel@tonic-gate 	 * If the daemon thinks the route should exist, eventually
19127c478bd9Sstevel@tonic-gate 	 * override manual intervention by the operator.
19137c478bd9Sstevel@tonic-gate 	 */
19147c478bd9Sstevel@tonic-gate 	if ((k->k_state & (KS_DYNAMIC | KS_DELETED)) != 0) {
19157c478bd9Sstevel@tonic-gate 		k->k_state &= ~KS_DYNAMIC;
19167c478bd9Sstevel@tonic-gate 		k->k_state |= (KS_ADD | KS_DEL_ADD);
19177c478bd9Sstevel@tonic-gate 	}
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 	if ((k->k_state & KS_GATEWAY) && !(ag->ag_state & AGS_GATEWAY)) {
19207c478bd9Sstevel@tonic-gate 		k->k_state &= ~KS_GATEWAY;
19217c478bd9Sstevel@tonic-gate 		k->k_state |= (KS_ADD | KS_DEL_ADD);
19227c478bd9Sstevel@tonic-gate 	} else if (!(k->k_state & KS_GATEWAY) && (ag->ag_state & AGS_GATEWAY)) {
19237c478bd9Sstevel@tonic-gate 		k->k_state |= KS_GATEWAY;
19247c478bd9Sstevel@tonic-gate 		k->k_state |= (KS_ADD | KS_DEL_ADD);
19257c478bd9Sstevel@tonic-gate 	}
19267c478bd9Sstevel@tonic-gate 
19277c478bd9Sstevel@tonic-gate 	/*
19287c478bd9Sstevel@tonic-gate 	 * Deleting-and-adding is necessary to change aspects of a route.
19297c478bd9Sstevel@tonic-gate 	 * Just delete instead of deleting and then adding a bad route.
19307c478bd9Sstevel@tonic-gate 	 * Otherwise, we want to keep the route in the kernel.
19317c478bd9Sstevel@tonic-gate 	 */
19327c478bd9Sstevel@tonic-gate 	if (k->k_metric == HOPCNT_INFINITY && (k->k_state & KS_DEL_ADD))
19337c478bd9Sstevel@tonic-gate 		k->k_state |= KS_DELETE;
19347c478bd9Sstevel@tonic-gate 	else
19357c478bd9Sstevel@tonic-gate 		k->k_state &= ~KS_DELETE;
19367c478bd9Sstevel@tonic-gate #undef RT
19377c478bd9Sstevel@tonic-gate }
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate /*
19407c478bd9Sstevel@tonic-gate  * Update our image of the kernel forwarding table using the given
19417c478bd9Sstevel@tonic-gate  * route from our internal routing table.
19427c478bd9Sstevel@tonic-gate  */
19437c478bd9Sstevel@tonic-gate 
19447c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
19457c478bd9Sstevel@tonic-gate static int
19467c478bd9Sstevel@tonic-gate walk_kern(struct radix_node *rn, void *argp)
19477c478bd9Sstevel@tonic-gate {
19487c478bd9Sstevel@tonic-gate #define	RT ((struct rt_entry *)rn)
19497c478bd9Sstevel@tonic-gate 	uint8_t metric, pref;
19507c478bd9Sstevel@tonic-gate 	uint_t ags = 0;
19517c478bd9Sstevel@tonic-gate 	int i;
19527c478bd9Sstevel@tonic-gate 	struct rt_spare *rts;
19537c478bd9Sstevel@tonic-gate 
19547c478bd9Sstevel@tonic-gate 	/* Do not install synthetic routes */
19557c478bd9Sstevel@tonic-gate 	if (RT->rt_state & RS_NET_SYN)
19567c478bd9Sstevel@tonic-gate 		return (0);
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 	/*
19597c478bd9Sstevel@tonic-gate 	 * Do not install static routes here. Only
19607c478bd9Sstevel@tonic-gate 	 * read_rt->rtm_add->kern_add should install those
19617c478bd9Sstevel@tonic-gate 	 */
19627c478bd9Sstevel@tonic-gate 	if ((RT->rt_state & RS_STATIC) &&
19637c478bd9Sstevel@tonic-gate 	    (RT->rt_spares[0].rts_origin != RO_FILE))
19647c478bd9Sstevel@tonic-gate 		return (0);
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate 	/* Do not clobber kernel if this is a route for a dead interface */
19677c478bd9Sstevel@tonic-gate 	if (RT->rt_state & RS_BADIF)
19687c478bd9Sstevel@tonic-gate 		return (0);
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate 	if (!(RT->rt_state & RS_IF)) {
19717c478bd9Sstevel@tonic-gate 		/* This is an ordinary route, not for an interface. */
19727c478bd9Sstevel@tonic-gate 
19737c478bd9Sstevel@tonic-gate 		/*
19747c478bd9Sstevel@tonic-gate 		 * aggregate, ordinary good routes without regard to
19757c478bd9Sstevel@tonic-gate 		 * their metric
19767c478bd9Sstevel@tonic-gate 		 */
19777c478bd9Sstevel@tonic-gate 		pref = 1;
19787c478bd9Sstevel@tonic-gate 		ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE);
19797c478bd9Sstevel@tonic-gate 
19807c478bd9Sstevel@tonic-gate 		/*
19817c478bd9Sstevel@tonic-gate 		 * Do not install host routes directly to hosts, to avoid
19827c478bd9Sstevel@tonic-gate 		 * interfering with ARP entries in the kernel table.
19837c478bd9Sstevel@tonic-gate 		 */
19847c478bd9Sstevel@tonic-gate 		if (RT_ISHOST(RT) && ntohl(RT->rt_dst) == RT->rt_gate)
19857c478bd9Sstevel@tonic-gate 			return (0);
19867c478bd9Sstevel@tonic-gate 
19877c478bd9Sstevel@tonic-gate 	} else {
19887c478bd9Sstevel@tonic-gate 		/*
19897c478bd9Sstevel@tonic-gate 		 * This is an interface route.
19907c478bd9Sstevel@tonic-gate 		 * Do not install routes for "external" remote interfaces.
19917c478bd9Sstevel@tonic-gate 		 */
19927c478bd9Sstevel@tonic-gate 		if (RT->rt_ifp != NULL && (RT->rt_ifp->int_state & IS_EXTERNAL))
19937c478bd9Sstevel@tonic-gate 			return (0);
19947c478bd9Sstevel@tonic-gate 
19957c478bd9Sstevel@tonic-gate 		/* Interfaces should override received routes. */
19967c478bd9Sstevel@tonic-gate 		pref = 0;
19977c478bd9Sstevel@tonic-gate 		ags |= (AGS_IF | AGS_CORS_GATE);
19987c478bd9Sstevel@tonic-gate 		if (RT->rt_ifp != NULL &&
19997c478bd9Sstevel@tonic-gate 		    !(RT->rt_ifp->int_if_flags & IFF_LOOPBACK) &&
20007c478bd9Sstevel@tonic-gate 		    (RT->rt_ifp->int_state & (IS_PASSIVE|IS_ALIAS)) ==
20017c478bd9Sstevel@tonic-gate 		    IS_PASSIVE) {
20027c478bd9Sstevel@tonic-gate 			ags |= AGS_PASSIVE;
20037c478bd9Sstevel@tonic-gate 		}
20047c478bd9Sstevel@tonic-gate 
20057c478bd9Sstevel@tonic-gate 		/*
20067c478bd9Sstevel@tonic-gate 		 * If it is not an interface, or an alias for an interface,
20077c478bd9Sstevel@tonic-gate 		 * it must be a "gateway."
20087c478bd9Sstevel@tonic-gate 		 *
20097c478bd9Sstevel@tonic-gate 		 * If it is a "remote" interface, it is also a "gateway" to
20107c478bd9Sstevel@tonic-gate 		 * the kernel if is not a alias.
20117c478bd9Sstevel@tonic-gate 		 */
2012c9116dbcSbw 		if (RT->rt_ifp == NULL || (RT->rt_ifp->int_state & IS_REMOTE)) {
2013c9116dbcSbw 
2014c9116dbcSbw 			ags |= (AGS_GATEWAY | AGS_SUPPRESS);
2015c9116dbcSbw 
2016c9116dbcSbw 			/*
2017c9116dbcSbw 			 * Do not aggregate IS_PASSIVE routes.
2018c9116dbcSbw 			 */
2019c9116dbcSbw 			if (!(RT->rt_ifp->int_state & IS_PASSIVE))
2020c9116dbcSbw 				ags |= AGS_AGGREGATE;
2021c9116dbcSbw 		}
20227c478bd9Sstevel@tonic-gate 	}
20237c478bd9Sstevel@tonic-gate 
20247c478bd9Sstevel@tonic-gate 	metric = RT->rt_metric;
20257c478bd9Sstevel@tonic-gate 	if (metric == HOPCNT_INFINITY) {
20267c478bd9Sstevel@tonic-gate 		/* If the route is dead, try hard to aggregate. */
20277c478bd9Sstevel@tonic-gate 		pref = HOPCNT_INFINITY;
20287c478bd9Sstevel@tonic-gate 		ags |= (AGS_FINE_GATE | AGS_SUPPRESS);
20297c478bd9Sstevel@tonic-gate 		ags &= ~(AGS_IF | AGS_CORS_GATE);
20307c478bd9Sstevel@tonic-gate 	}
20317c478bd9Sstevel@tonic-gate 
20327c478bd9Sstevel@tonic-gate 	/*
20337c478bd9Sstevel@tonic-gate 	 * dump all routes that have the same metric as rt_spares[0]
20347c478bd9Sstevel@tonic-gate 	 * into the kern_table, to be added to the kernel.
20357c478bd9Sstevel@tonic-gate 	 */
20367c478bd9Sstevel@tonic-gate 	for (i = 0; i < RT->rt_num_spares; i++) {
20377c478bd9Sstevel@tonic-gate 		rts = &RT->rt_spares[i];
20387c478bd9Sstevel@tonic-gate 
20397c478bd9Sstevel@tonic-gate 		/* Do not install external routes */
20407c478bd9Sstevel@tonic-gate 		if (rts->rts_flags & RTS_EXTERNAL)
20417c478bd9Sstevel@tonic-gate 			continue;
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 		if (rts->rts_metric == metric) {
20447c478bd9Sstevel@tonic-gate 			ag_check(RT->rt_dst, RT->rt_mask,
20457c478bd9Sstevel@tonic-gate 			    rts->rts_router, rts->rts_ifp, rts->rts_gate,
20467c478bd9Sstevel@tonic-gate 			    metric, pref, 0, 0,
20477c478bd9Sstevel@tonic-gate 			    (rts->rts_origin & RO_FILE) ? (ags|AGS_FILE) : ags,
20487c478bd9Sstevel@tonic-gate 			    kern_out);
20497c478bd9Sstevel@tonic-gate 		}
20507c478bd9Sstevel@tonic-gate 	}
20517c478bd9Sstevel@tonic-gate 	return (0);
20527c478bd9Sstevel@tonic-gate #undef RT
20537c478bd9Sstevel@tonic-gate }
20547c478bd9Sstevel@tonic-gate 
20557c478bd9Sstevel@tonic-gate 
20567c478bd9Sstevel@tonic-gate /* Update the kernel table to match the daemon table. */
20577c478bd9Sstevel@tonic-gate static void
20587c478bd9Sstevel@tonic-gate fix_kern(void)
20597c478bd9Sstevel@tonic-gate {
20607c478bd9Sstevel@tonic-gate 	int i;
20617c478bd9Sstevel@tonic-gate 	struct khash *k, *pk, *knext;
20627c478bd9Sstevel@tonic-gate 
20637c478bd9Sstevel@tonic-gate 
20647c478bd9Sstevel@tonic-gate 	need_kern = age_timer;
20657c478bd9Sstevel@tonic-gate 
20667c478bd9Sstevel@tonic-gate 	/* Walk daemon table, updating the copy of the kernel table. */
20677c478bd9Sstevel@tonic-gate 	(void) rn_walktree(rhead, walk_kern, NULL);
20687c478bd9Sstevel@tonic-gate 	ag_flush(0, 0, kern_out);
20697c478bd9Sstevel@tonic-gate 
20707c478bd9Sstevel@tonic-gate 	for (i = 0; i < KHASH_SIZE; i++) {
20717c478bd9Sstevel@tonic-gate 		pk = NULL;
20727c478bd9Sstevel@tonic-gate 		for (k = khash_bins[i]; k != NULL;  k = knext) {
20737c478bd9Sstevel@tonic-gate 			knext = k->k_next;
20747c478bd9Sstevel@tonic-gate 
20757c478bd9Sstevel@tonic-gate 			/* Do not touch local interface routes */
20767c478bd9Sstevel@tonic-gate 			if ((k->k_state & KS_DEPRE_IF) ||
20777c478bd9Sstevel@tonic-gate 			    (k->k_state & (KS_IF|KS_PASSIVE)) == KS_IF) {
20787c478bd9Sstevel@tonic-gate 				pk = k;
20797c478bd9Sstevel@tonic-gate 				continue;
20807c478bd9Sstevel@tonic-gate 			}
20817c478bd9Sstevel@tonic-gate 
20827c478bd9Sstevel@tonic-gate 			/* Do not touch static routes */
20837c478bd9Sstevel@tonic-gate 			if (k->k_state & KS_STATIC) {
20847c478bd9Sstevel@tonic-gate 				kern_check_static(k, 0);
20857c478bd9Sstevel@tonic-gate 				pk = k;
20867c478bd9Sstevel@tonic-gate 				continue;
20877c478bd9Sstevel@tonic-gate 			}
20887c478bd9Sstevel@tonic-gate 
20897c478bd9Sstevel@tonic-gate 			/* check hold on routes deleted by the operator */
20907c478bd9Sstevel@tonic-gate 			if (k->k_keep > now.tv_sec) {
20917c478bd9Sstevel@tonic-gate 				/* ensure we check when the hold is over */
20927c478bd9Sstevel@tonic-gate 				LIM_SEC(need_kern, k->k_keep);
20937c478bd9Sstevel@tonic-gate 				pk = k;
20947c478bd9Sstevel@tonic-gate 				continue;
20957c478bd9Sstevel@tonic-gate 			}
20967c478bd9Sstevel@tonic-gate 
20977c478bd9Sstevel@tonic-gate 			if ((k->k_state & KS_DELETE) &&
20987c478bd9Sstevel@tonic-gate 			    !(k->k_state & KS_DYNAMIC)) {
20997c478bd9Sstevel@tonic-gate 				if ((k->k_dst == RIP_DEFAULT) &&
21007c478bd9Sstevel@tonic-gate 				    (k->k_ifp != NULL) &&
21017c478bd9Sstevel@tonic-gate 				    (kern_alternate(RIP_DEFAULT,
21027c478bd9Sstevel@tonic-gate 				    k->k_mask, k->k_gate, k->k_ifp,
21037c478bd9Sstevel@tonic-gate 				    NULL) == NULL))
21047c478bd9Sstevel@tonic-gate 					rdisc_restore(k->k_ifp);
21057c478bd9Sstevel@tonic-gate 				kern_ioctl(k, RTM_DELETE, 0);
21067c478bd9Sstevel@tonic-gate 				if (pk != NULL)
21077c478bd9Sstevel@tonic-gate 					pk->k_next = knext;
21087c478bd9Sstevel@tonic-gate 				else
21097c478bd9Sstevel@tonic-gate 					khash_bins[i] = knext;
21107c478bd9Sstevel@tonic-gate 				free(k);
21117c478bd9Sstevel@tonic-gate 				continue;
21127c478bd9Sstevel@tonic-gate 			}
21137c478bd9Sstevel@tonic-gate 
21147c478bd9Sstevel@tonic-gate 			if (k->k_state & KS_DEL_ADD)
21157c478bd9Sstevel@tonic-gate 				kern_ioctl(k, RTM_DELETE, 0);
21167c478bd9Sstevel@tonic-gate 
21177c478bd9Sstevel@tonic-gate 			if (k->k_state & KS_ADD) {
21187c478bd9Sstevel@tonic-gate 				if ((k->k_dst == RIP_DEFAULT) &&
21197c478bd9Sstevel@tonic-gate 				    (k->k_ifp != NULL))
21207c478bd9Sstevel@tonic-gate 					rdisc_suppress(k->k_ifp);
21217c478bd9Sstevel@tonic-gate 				kern_ioctl(k, RTM_ADD,
21227c478bd9Sstevel@tonic-gate 				    ((0 != (k->k_state & (KS_GATEWAY |
2123*f9aa3e1eSkcpoon 				    KS_DYNAMIC))) ? RTF_GATEWAY : 0));
21247c478bd9Sstevel@tonic-gate 			} else if (k->k_state & KS_CHANGE) {
21257c478bd9Sstevel@tonic-gate 				/*
21267c478bd9Sstevel@tonic-gate 				 * Should be using RTM_CHANGE here, but
21277c478bd9Sstevel@tonic-gate 				 * since RTM_CHANGE is currently
21287c478bd9Sstevel@tonic-gate 				 * not multipath-aware, and assumes
21297c478bd9Sstevel@tonic-gate 				 * that RTF_GATEWAY implies the gateway
21307c478bd9Sstevel@tonic-gate 				 * of the route for dst has to be
21317c478bd9Sstevel@tonic-gate 				 * changed, we play safe, and do a del + add.
21327c478bd9Sstevel@tonic-gate 				 */
21337c478bd9Sstevel@tonic-gate 				kern_ioctl(k,  RTM_DELETE, 0);
21347c478bd9Sstevel@tonic-gate 				kern_ioctl(k, RTM_ADD,
21357c478bd9Sstevel@tonic-gate 				    ((0 != (k->k_state & (KS_GATEWAY |
2136*f9aa3e1eSkcpoon 				    KS_DYNAMIC))) ? RTF_GATEWAY : 0));
21377c478bd9Sstevel@tonic-gate 			}
21387c478bd9Sstevel@tonic-gate 			k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD);
21397c478bd9Sstevel@tonic-gate 
21407c478bd9Sstevel@tonic-gate 			/*
21417c478bd9Sstevel@tonic-gate 			 * Mark this route to be deleted in the next cycle.
21427c478bd9Sstevel@tonic-gate 			 * This deletes routes that disappear from the
21437c478bd9Sstevel@tonic-gate 			 * daemon table, since the normal aging code
21447c478bd9Sstevel@tonic-gate 			 * will clear the bit for routes that have not
21457c478bd9Sstevel@tonic-gate 			 * disappeared from the daemon table.
21467c478bd9Sstevel@tonic-gate 			 */
21477c478bd9Sstevel@tonic-gate 			k->k_state |= KS_DELETE;
21487c478bd9Sstevel@tonic-gate 			pk = k;
21497c478bd9Sstevel@tonic-gate 		}
21507c478bd9Sstevel@tonic-gate 	}
21517c478bd9Sstevel@tonic-gate }
21527c478bd9Sstevel@tonic-gate 
21537c478bd9Sstevel@tonic-gate 
21547c478bd9Sstevel@tonic-gate /* Delete a static route in the image of the kernel table. */
21557c478bd9Sstevel@tonic-gate void
21567c478bd9Sstevel@tonic-gate del_static(in_addr_t dst, in_addr_t mask, in_addr_t gate,
21577c478bd9Sstevel@tonic-gate     struct interface *ifp, int gone)
21587c478bd9Sstevel@tonic-gate {
21597c478bd9Sstevel@tonic-gate 	struct khash *k;
21607c478bd9Sstevel@tonic-gate 	struct rt_entry *rt;
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate 	/*
21637c478bd9Sstevel@tonic-gate 	 * Just mark it in the table to be deleted next time the kernel
21647c478bd9Sstevel@tonic-gate 	 * table is updated.
21657c478bd9Sstevel@tonic-gate 	 * If it has already been deleted, mark it as such, and set its
21667c478bd9Sstevel@tonic-gate 	 * keep-timer so that it will not be deleted again for a while.
21677c478bd9Sstevel@tonic-gate 	 * This lets the operator delete a route added by the daemon
21687c478bd9Sstevel@tonic-gate 	 * and add a replacement.
21697c478bd9Sstevel@tonic-gate 	 */
21707c478bd9Sstevel@tonic-gate 	k = kern_find(dst, mask, gate, ifp, NULL);
21717c478bd9Sstevel@tonic-gate 	if (k != NULL && (gate == 0 || k->k_gate == gate)) {
21727c478bd9Sstevel@tonic-gate 		k->k_state &= ~(KS_STATIC | KS_DYNAMIC | KS_CHECK);
21737c478bd9Sstevel@tonic-gate 		k->k_state |= KS_DELETE;
21747c478bd9Sstevel@tonic-gate 		if (gone) {
21757c478bd9Sstevel@tonic-gate 			k->k_state |= KS_DELETED;
21767c478bd9Sstevel@tonic-gate 			k->k_keep = now.tv_sec + K_KEEP_LIM;
21777c478bd9Sstevel@tonic-gate 		}
21787c478bd9Sstevel@tonic-gate 	}
21797c478bd9Sstevel@tonic-gate 
21807c478bd9Sstevel@tonic-gate 	rt = rtget(dst, mask);
21817c478bd9Sstevel@tonic-gate 	if (rt != NULL && (rt->rt_state & RS_STATIC))
21827c478bd9Sstevel@tonic-gate 		rtbad(rt, NULL);
21837c478bd9Sstevel@tonic-gate }
21847c478bd9Sstevel@tonic-gate 
21857c478bd9Sstevel@tonic-gate 
21867c478bd9Sstevel@tonic-gate /*
21877c478bd9Sstevel@tonic-gate  * Delete all routes generated from ICMP Redirects that use a given gateway,
21887c478bd9Sstevel@tonic-gate  * as well as old redirected routes.
21897c478bd9Sstevel@tonic-gate  */
21907c478bd9Sstevel@tonic-gate void
21917c478bd9Sstevel@tonic-gate del_redirects(in_addr_t bad_gate, time_t old)
21927c478bd9Sstevel@tonic-gate {
21937c478bd9Sstevel@tonic-gate 	int i;
21947c478bd9Sstevel@tonic-gate 	struct khash *k;
21957c478bd9Sstevel@tonic-gate 	boolean_t dosupply = should_supply(NULL);
21967c478bd9Sstevel@tonic-gate 
21977c478bd9Sstevel@tonic-gate 	for (i = 0; i < KHASH_SIZE; i++) {
21987c478bd9Sstevel@tonic-gate 		for (k = khash_bins[i]; k != NULL; k = k->k_next) {
21997c478bd9Sstevel@tonic-gate 			if (!(k->k_state & KS_DYNAMIC) ||
22007c478bd9Sstevel@tonic-gate 			    (k->k_state & (KS_STATIC|KS_IF|KS_DEPRE_IF)))
22017c478bd9Sstevel@tonic-gate 				continue;
22027c478bd9Sstevel@tonic-gate 
22037c478bd9Sstevel@tonic-gate 			if (k->k_gate != bad_gate && k->k_redirect_time > old &&
22047c478bd9Sstevel@tonic-gate 			    !dosupply)
22057c478bd9Sstevel@tonic-gate 				continue;
22067c478bd9Sstevel@tonic-gate 
22077c478bd9Sstevel@tonic-gate 			k->k_state |= KS_DELETE;
22087c478bd9Sstevel@tonic-gate 			k->k_state &= ~KS_DYNAMIC;
22097c478bd9Sstevel@tonic-gate 			need_kern.tv_sec = now.tv_sec;
22107c478bd9Sstevel@tonic-gate 			trace_act("mark redirected %s --> %s for deletion",
22117c478bd9Sstevel@tonic-gate 			    addrname(k->k_dst, k->k_mask, 0),
22127c478bd9Sstevel@tonic-gate 			    naddr_ntoa(k->k_gate));
22137c478bd9Sstevel@tonic-gate 		}
22147c478bd9Sstevel@tonic-gate 	}
22157c478bd9Sstevel@tonic-gate }
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate /* Start the daemon tables. */
22187c478bd9Sstevel@tonic-gate void
22197c478bd9Sstevel@tonic-gate rtinit(void)
22207c478bd9Sstevel@tonic-gate {
22217c478bd9Sstevel@tonic-gate 	int i;
22227c478bd9Sstevel@tonic-gate 	struct ag_info *ag;
22237c478bd9Sstevel@tonic-gate 
22247c478bd9Sstevel@tonic-gate 	/* Initialize the radix trees */
22257c478bd9Sstevel@tonic-gate 	rn_init();
22267c478bd9Sstevel@tonic-gate 	(void) rn_inithead((void**)&rhead, 32);
22277c478bd9Sstevel@tonic-gate 
22287c478bd9Sstevel@tonic-gate 	/* mark all of the slots in the table free */
22297c478bd9Sstevel@tonic-gate 	ag_avail = ag_slots;
22307c478bd9Sstevel@tonic-gate 	for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) {
22317c478bd9Sstevel@tonic-gate 		ag->ag_fine = ag+1;
22327c478bd9Sstevel@tonic-gate 		ag++;
22337c478bd9Sstevel@tonic-gate 	}
22347c478bd9Sstevel@tonic-gate }
22357c478bd9Sstevel@tonic-gate 
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate static struct sockaddr_in dst_sock = {AF_INET};
22387c478bd9Sstevel@tonic-gate static struct sockaddr_in mask_sock = {AF_INET};
22397c478bd9Sstevel@tonic-gate 
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate static void
22427c478bd9Sstevel@tonic-gate set_need_flash(void)
22437c478bd9Sstevel@tonic-gate {
22447c478bd9Sstevel@tonic-gate 	if (!need_flash) {
22457c478bd9Sstevel@tonic-gate 		need_flash = _B_TRUE;
22467c478bd9Sstevel@tonic-gate 		/*
22477c478bd9Sstevel@tonic-gate 		 * Do not send the flash update immediately.  Wait a little
22487c478bd9Sstevel@tonic-gate 		 * while to hear from other routers.
22497c478bd9Sstevel@tonic-gate 		 */
22507c478bd9Sstevel@tonic-gate 		no_flash.tv_sec = now.tv_sec + MIN_WAITTIME;
22517c478bd9Sstevel@tonic-gate 	}
22527c478bd9Sstevel@tonic-gate }
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate 
22557c478bd9Sstevel@tonic-gate /* Get a particular routing table entry */
22567c478bd9Sstevel@tonic-gate struct rt_entry *
22577c478bd9Sstevel@tonic-gate rtget(in_addr_t dst, in_addr_t mask)
22587c478bd9Sstevel@tonic-gate {
22597c478bd9Sstevel@tonic-gate 	struct rt_entry *rt;
22607c478bd9Sstevel@tonic-gate 
22617c478bd9Sstevel@tonic-gate 	dst_sock.sin_addr.s_addr = dst;
22627c478bd9Sstevel@tonic-gate 	mask_sock.sin_addr.s_addr = htonl(mask);
22637c478bd9Sstevel@tonic-gate 	rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock, &mask_sock, rhead);
22647c478bd9Sstevel@tonic-gate 	if (rt == NULL || rt->rt_dst != dst || rt->rt_mask != mask)
22657c478bd9Sstevel@tonic-gate 		return (NULL);
22667c478bd9Sstevel@tonic-gate 
22677c478bd9Sstevel@tonic-gate 	return (rt);
22687c478bd9Sstevel@tonic-gate }
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate 
22717c478bd9Sstevel@tonic-gate /* Find a route to dst as the kernel would. */
22727c478bd9Sstevel@tonic-gate struct rt_entry *
22737c478bd9Sstevel@tonic-gate rtfind(in_addr_t dst)
22747c478bd9Sstevel@tonic-gate {
22757c478bd9Sstevel@tonic-gate 	dst_sock.sin_addr.s_addr = dst;
22767c478bd9Sstevel@tonic-gate 	return ((struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead));
22777c478bd9Sstevel@tonic-gate }
22787c478bd9Sstevel@tonic-gate 
22797c478bd9Sstevel@tonic-gate 
22807c478bd9Sstevel@tonic-gate /* add a route to the table */
22817c478bd9Sstevel@tonic-gate void
22827c478bd9Sstevel@tonic-gate rtadd(in_addr_t	dst,
22837c478bd9Sstevel@tonic-gate     in_addr_t	mask,
22847c478bd9Sstevel@tonic-gate     uint16_t	state,			/* rt_state for the entry */
22857c478bd9Sstevel@tonic-gate     struct	rt_spare *new)
22867c478bd9Sstevel@tonic-gate {
22877c478bd9Sstevel@tonic-gate 	struct rt_entry *rt;
22887c478bd9Sstevel@tonic-gate 	in_addr_t smask;
22897c478bd9Sstevel@tonic-gate 	int i;
22907c478bd9Sstevel@tonic-gate 	struct rt_spare *rts;
22917c478bd9Sstevel@tonic-gate 
22927c478bd9Sstevel@tonic-gate 	/* This is the only function that increments total_routes. */
22937c478bd9Sstevel@tonic-gate 	if (total_routes == MAX_ROUTES) {
22947c478bd9Sstevel@tonic-gate 		msglog("have maximum (%d) routes", total_routes);
22957c478bd9Sstevel@tonic-gate 		return;
22967c478bd9Sstevel@tonic-gate 	}
22977c478bd9Sstevel@tonic-gate 
22987c478bd9Sstevel@tonic-gate 	rt = rtmalloc(sizeof (*rt), "rtadd");
22997c478bd9Sstevel@tonic-gate 	(void) memset(rt, 0, sizeof (*rt));
23007c478bd9Sstevel@tonic-gate 	rt->rt_spares = rtmalloc(SPARE_INC  * sizeof (struct rt_spare),
23017c478bd9Sstevel@tonic-gate 	    "rtadd");
23027c478bd9Sstevel@tonic-gate 	rt->rt_num_spares = SPARE_INC;
23037c478bd9Sstevel@tonic-gate 	(void) memset(rt->rt_spares, 0, SPARE_INC  * sizeof (struct rt_spare));
23047c478bd9Sstevel@tonic-gate 	for (rts = rt->rt_spares, i = rt->rt_num_spares; i != 0; i--, rts++)
23057c478bd9Sstevel@tonic-gate 		rts->rts_metric = HOPCNT_INFINITY;
23067c478bd9Sstevel@tonic-gate 
23077c478bd9Sstevel@tonic-gate 	rt->rt_nodes->rn_key = (uint8_t *)&rt->rt_dst_sock;
23087c478bd9Sstevel@tonic-gate 	rt->rt_dst = dst;
23097c478bd9Sstevel@tonic-gate 	rt->rt_dst_sock.sin_family = AF_INET;
23107c478bd9Sstevel@tonic-gate 	if (mask != HOST_MASK) {
23117c478bd9Sstevel@tonic-gate 		smask = std_mask(dst);
23127c478bd9Sstevel@tonic-gate 		if ((smask & ~mask) == 0 && mask > smask)
23137c478bd9Sstevel@tonic-gate 			state |= RS_SUBNET;
23147c478bd9Sstevel@tonic-gate 	}
23157c478bd9Sstevel@tonic-gate 	mask_sock.sin_addr.s_addr = htonl(mask);
23167c478bd9Sstevel@tonic-gate 	rt->rt_mask = mask;
23177c478bd9Sstevel@tonic-gate 	rt->rt_spares[0] = *new;
23187c478bd9Sstevel@tonic-gate 	rt->rt_state = state;
23197c478bd9Sstevel@tonic-gate 	rt->rt_time = now.tv_sec;
23207c478bd9Sstevel@tonic-gate 	rt->rt_poison_metric = HOPCNT_INFINITY;
23217c478bd9Sstevel@tonic-gate 	rt->rt_seqno = update_seqno;
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 	if (TRACEACTIONS)
23247c478bd9Sstevel@tonic-gate 		trace_add_del("Add", rt);
23257c478bd9Sstevel@tonic-gate 
23267c478bd9Sstevel@tonic-gate 	need_kern.tv_sec = now.tv_sec;
23277c478bd9Sstevel@tonic-gate 	set_need_flash();
23287c478bd9Sstevel@tonic-gate 
23297c478bd9Sstevel@tonic-gate 	if (NULL == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock, rhead,
23307c478bd9Sstevel@tonic-gate 	    rt->rt_nodes)) {
23317c478bd9Sstevel@tonic-gate 		msglog("rnh_addaddr() failed for %s mask=%s",
23327c478bd9Sstevel@tonic-gate 		    naddr_ntoa(dst), naddr_ntoa(htonl(mask)));
23337c478bd9Sstevel@tonic-gate 		free(rt);
23347c478bd9Sstevel@tonic-gate 	}
23357c478bd9Sstevel@tonic-gate 
23367c478bd9Sstevel@tonic-gate 	total_routes++;
23377c478bd9Sstevel@tonic-gate }
23387c478bd9Sstevel@tonic-gate 
23397c478bd9Sstevel@tonic-gate 
23407c478bd9Sstevel@tonic-gate /* notice a changed route */
23417c478bd9Sstevel@tonic-gate void
23427c478bd9Sstevel@tonic-gate rtchange(struct rt_entry *rt,
23437c478bd9Sstevel@tonic-gate     uint16_t	state,			/* new state bits */
23447c478bd9Sstevel@tonic-gate     struct rt_spare *new,
23457c478bd9Sstevel@tonic-gate     char	*label)
23467c478bd9Sstevel@tonic-gate {
23477c478bd9Sstevel@tonic-gate 	if (rt->rt_metric != new->rts_metric) {
23487c478bd9Sstevel@tonic-gate 		/*
23497c478bd9Sstevel@tonic-gate 		 * Fix the kernel immediately if it seems the route
23507c478bd9Sstevel@tonic-gate 		 * has gone bad, since there may be a working route that
23517c478bd9Sstevel@tonic-gate 		 * aggregates this route.
23527c478bd9Sstevel@tonic-gate 		 */
23537c478bd9Sstevel@tonic-gate 		if (new->rts_metric == HOPCNT_INFINITY) {
23547c478bd9Sstevel@tonic-gate 			need_kern.tv_sec = now.tv_sec;
23557c478bd9Sstevel@tonic-gate 			if (new->rts_time >= now.tv_sec - EXPIRE_TIME)
23567c478bd9Sstevel@tonic-gate 				new->rts_time = now.tv_sec - EXPIRE_TIME;
23577c478bd9Sstevel@tonic-gate 		}
23587c478bd9Sstevel@tonic-gate 		rt->rt_seqno = update_seqno;
23597c478bd9Sstevel@tonic-gate 		set_need_flash();
23607c478bd9Sstevel@tonic-gate 	}
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate 	if (rt->rt_gate != new->rts_gate) {
23637c478bd9Sstevel@tonic-gate 		need_kern.tv_sec = now.tv_sec;
23647c478bd9Sstevel@tonic-gate 		rt->rt_seqno = update_seqno;
23657c478bd9Sstevel@tonic-gate 		set_need_flash();
23667c478bd9Sstevel@tonic-gate 	}
23677c478bd9Sstevel@tonic-gate 
23687c478bd9Sstevel@tonic-gate 	state |= (rt->rt_state & RS_SUBNET);
23697c478bd9Sstevel@tonic-gate 
23707c478bd9Sstevel@tonic-gate 	/* Keep various things from deciding ageless routes are stale. */
23717c478bd9Sstevel@tonic-gate 	if (!AGE_RT(state, rt->rt_spares[0].rts_origin, new->rts_ifp))
23727c478bd9Sstevel@tonic-gate 		new->rts_time = now.tv_sec;
23737c478bd9Sstevel@tonic-gate 
23747c478bd9Sstevel@tonic-gate 	if (TRACEACTIONS)
23757c478bd9Sstevel@tonic-gate 		trace_change(rt, state, new,
23767c478bd9Sstevel@tonic-gate 		    label ? label : "Chg   ");
23777c478bd9Sstevel@tonic-gate 
23787c478bd9Sstevel@tonic-gate 	rt->rt_state = state;
23797c478bd9Sstevel@tonic-gate 	/*
23807c478bd9Sstevel@tonic-gate 	 * If the interface state of the new primary route is good,
23817c478bd9Sstevel@tonic-gate 	 * turn off RS_BADIF flag
23827c478bd9Sstevel@tonic-gate 	 */
23837c478bd9Sstevel@tonic-gate 	if ((rt->rt_state & RS_BADIF) &&
23847c478bd9Sstevel@tonic-gate 	    IS_IFF_UP(new->rts_ifp->int_if_flags) &&
23857c478bd9Sstevel@tonic-gate 	    !(new->rts_ifp->int_state & (IS_BROKE | IS_SICK)))
23867c478bd9Sstevel@tonic-gate 		rt->rt_state &= ~(RS_BADIF);
23877c478bd9Sstevel@tonic-gate 
23887c478bd9Sstevel@tonic-gate 	rt->rt_spares[0] = *new;
23897c478bd9Sstevel@tonic-gate }
23907c478bd9Sstevel@tonic-gate 
23917c478bd9Sstevel@tonic-gate 
23927c478bd9Sstevel@tonic-gate /* check for a better route among the spares */
23937c478bd9Sstevel@tonic-gate static struct rt_spare *
23947c478bd9Sstevel@tonic-gate rts_better(struct rt_entry *rt)
23957c478bd9Sstevel@tonic-gate {
23967c478bd9Sstevel@tonic-gate 	struct rt_spare *rts, *rts1;
23977c478bd9Sstevel@tonic-gate 	int i;
23987c478bd9Sstevel@tonic-gate 
23997c478bd9Sstevel@tonic-gate 	/* find the best alternative among the spares */
24007c478bd9Sstevel@tonic-gate 	rts = rt->rt_spares+1;
24017c478bd9Sstevel@tonic-gate 	for (i = rt->rt_num_spares, rts1 = rts+1; i > 2; i--, rts1++) {
24027c478bd9Sstevel@tonic-gate 		if (BETTER_LINK(rt, rts1, rts))
24037c478bd9Sstevel@tonic-gate 			rts = rts1;
24047c478bd9Sstevel@tonic-gate 	}
24057c478bd9Sstevel@tonic-gate 
24067c478bd9Sstevel@tonic-gate 	return (rts);
24077c478bd9Sstevel@tonic-gate }
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate 
24107c478bd9Sstevel@tonic-gate /* switch to a backup route */
24117c478bd9Sstevel@tonic-gate void
24127c478bd9Sstevel@tonic-gate rtswitch(struct rt_entry *rt,
24137c478bd9Sstevel@tonic-gate     struct rt_spare *rts)
24147c478bd9Sstevel@tonic-gate {
24157c478bd9Sstevel@tonic-gate 	struct rt_spare swap;
24167c478bd9Sstevel@tonic-gate 	char label[10];
24177c478bd9Sstevel@tonic-gate 
24187c478bd9Sstevel@tonic-gate 	/* Do not change permanent routes */
24197c478bd9Sstevel@tonic-gate 	if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC |
24207c478bd9Sstevel@tonic-gate 	    RS_NET_SYN | RS_IF)))
24217c478bd9Sstevel@tonic-gate 		return;
24227c478bd9Sstevel@tonic-gate 
24237c478bd9Sstevel@tonic-gate 	/* find the best alternative among the spares */
24247c478bd9Sstevel@tonic-gate 	if (rts == NULL)
24257c478bd9Sstevel@tonic-gate 		rts = rts_better(rt);
24267c478bd9Sstevel@tonic-gate 
24277c478bd9Sstevel@tonic-gate 	/* Do not bother if it is not worthwhile. */
24287c478bd9Sstevel@tonic-gate 	if (!BETTER_LINK(rt, rts, rt->rt_spares))
24297c478bd9Sstevel@tonic-gate 		return;
24307c478bd9Sstevel@tonic-gate 
24317c478bd9Sstevel@tonic-gate 	swap = rt->rt_spares[0];
24327c478bd9Sstevel@tonic-gate 	(void) snprintf(label, sizeof (label), "Use #%d",
24337c478bd9Sstevel@tonic-gate 	    (int)(rts - rt->rt_spares));
24347c478bd9Sstevel@tonic-gate 	rtchange(rt, rt->rt_state & ~(RS_NET_SYN), rts, label);
24357c478bd9Sstevel@tonic-gate 
24367c478bd9Sstevel@tonic-gate 	if (swap.rts_metric == HOPCNT_INFINITY) {
24377c478bd9Sstevel@tonic-gate 		*rts = rts_empty;
24387c478bd9Sstevel@tonic-gate 	} else {
24397c478bd9Sstevel@tonic-gate 		*rts = swap;
24407c478bd9Sstevel@tonic-gate 	}
24417c478bd9Sstevel@tonic-gate 
24427c478bd9Sstevel@tonic-gate }
24437c478bd9Sstevel@tonic-gate 
24447c478bd9Sstevel@tonic-gate 
24457c478bd9Sstevel@tonic-gate void
24467c478bd9Sstevel@tonic-gate rtdelete(struct rt_entry *rt)
24477c478bd9Sstevel@tonic-gate {
24487c478bd9Sstevel@tonic-gate 	struct rt_entry *deleted_rt;
24497c478bd9Sstevel@tonic-gate 	struct rt_spare *rts;
24507c478bd9Sstevel@tonic-gate 	int i;
24517c478bd9Sstevel@tonic-gate 	in_addr_t gate = rt->rt_gate; /* for debugging */
24527c478bd9Sstevel@tonic-gate 
24537c478bd9Sstevel@tonic-gate 	if (TRACEACTIONS)
24547c478bd9Sstevel@tonic-gate 		trace_add_del("Del", rt);
24557c478bd9Sstevel@tonic-gate 
24567c478bd9Sstevel@tonic-gate 	for (i = 0; i < rt->rt_num_spares; i++) {
24577c478bd9Sstevel@tonic-gate 		rts = &rt->rt_spares[i];
24587c478bd9Sstevel@tonic-gate 		rts_delete(rt, rts);
24597c478bd9Sstevel@tonic-gate 	}
24607c478bd9Sstevel@tonic-gate 
24617c478bd9Sstevel@tonic-gate 	dst_sock.sin_addr.s_addr = rt->rt_dst;
24627c478bd9Sstevel@tonic-gate 	mask_sock.sin_addr.s_addr = htonl(rt->rt_mask);
24637c478bd9Sstevel@tonic-gate 	if (rt != (deleted_rt =
24647c478bd9Sstevel@tonic-gate 	    ((struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock,
24657c478bd9Sstevel@tonic-gate 	    rhead)))) {
24667c478bd9Sstevel@tonic-gate 		msglog("rnh_deladdr(%s) failed; found rt 0x%lx",
24677c478bd9Sstevel@tonic-gate 		    rtname(rt->rt_dst, rt->rt_mask, gate), deleted_rt);
24687c478bd9Sstevel@tonic-gate 		if (deleted_rt != NULL)
24697c478bd9Sstevel@tonic-gate 			free(deleted_rt);
24707c478bd9Sstevel@tonic-gate 	}
24717c478bd9Sstevel@tonic-gate 	total_routes--;
2472d6b3210dSsowmini 	free(rt->rt_spares);
24737c478bd9Sstevel@tonic-gate 	free(rt);
24747c478bd9Sstevel@tonic-gate 
24757c478bd9Sstevel@tonic-gate 	if (dst_sock.sin_addr.s_addr == RIP_DEFAULT) {
24767c478bd9Sstevel@tonic-gate 		/*
24777c478bd9Sstevel@tonic-gate 		 * we just deleted the default route. Trigger rdisc_sort
24787c478bd9Sstevel@tonic-gate 		 * so that we can recover from any rdisc information that
24797c478bd9Sstevel@tonic-gate 		 * is valid
24807c478bd9Sstevel@tonic-gate 		 */
24817c478bd9Sstevel@tonic-gate 		rdisc_timer.tv_sec = 0;
24827c478bd9Sstevel@tonic-gate 	}
24837c478bd9Sstevel@tonic-gate }
24847c478bd9Sstevel@tonic-gate 
24857c478bd9Sstevel@tonic-gate void
24867c478bd9Sstevel@tonic-gate rts_delete(struct rt_entry *rt, struct rt_spare *rts)
24877c478bd9Sstevel@tonic-gate {
24887c478bd9Sstevel@tonic-gate 	struct khash *k;
24897c478bd9Sstevel@tonic-gate 
24907c478bd9Sstevel@tonic-gate 	trace_upslot(rt, rts, &rts_empty);
24917c478bd9Sstevel@tonic-gate 	k = kern_find(rt->rt_dst, rt->rt_mask,
24927c478bd9Sstevel@tonic-gate 	    rts->rts_gate, rts->rts_ifp, NULL);
24937c478bd9Sstevel@tonic-gate 	if (k != NULL &&
24947c478bd9Sstevel@tonic-gate 	    !(k->k_state & KS_DEPRE_IF) &&
24957c478bd9Sstevel@tonic-gate 	    ((k->k_state & (KS_IF|KS_PASSIVE)) != KS_IF)) {
24967c478bd9Sstevel@tonic-gate 		k->k_state |= KS_DELETE;
24977c478bd9Sstevel@tonic-gate 		need_kern.tv_sec = now.tv_sec;
24987c478bd9Sstevel@tonic-gate 	}
24997c478bd9Sstevel@tonic-gate 
25007c478bd9Sstevel@tonic-gate 	*rts = rts_empty;
25017c478bd9Sstevel@tonic-gate }
25027c478bd9Sstevel@tonic-gate 
25037c478bd9Sstevel@tonic-gate /*
25047c478bd9Sstevel@tonic-gate  * Get rid of a bad route, and try to switch to a replacement.
25057c478bd9Sstevel@tonic-gate  * If the route has gone bad because of a bad interface,
25067c478bd9Sstevel@tonic-gate  * the information about the dead interface is available in badifp
25077c478bd9Sstevel@tonic-gate  * for the purpose of sanity checks, if_flags checks etc.
25087c478bd9Sstevel@tonic-gate  */
25097c478bd9Sstevel@tonic-gate static void
25107c478bd9Sstevel@tonic-gate rtbad(struct rt_entry *rt, struct interface *badifp)
25117c478bd9Sstevel@tonic-gate {
25127c478bd9Sstevel@tonic-gate 	struct rt_spare new;
25137c478bd9Sstevel@tonic-gate 	uint16_t rt_state;
25147c478bd9Sstevel@tonic-gate 
25157c478bd9Sstevel@tonic-gate 
25167c478bd9Sstevel@tonic-gate 	if (badifp == NULL || (rt->rt_spares[0].rts_ifp == badifp)) {
25177c478bd9Sstevel@tonic-gate 		/* Poison the route */
25187c478bd9Sstevel@tonic-gate 		new = rt->rt_spares[0];
25197c478bd9Sstevel@tonic-gate 		new.rts_metric = HOPCNT_INFINITY;
25207c478bd9Sstevel@tonic-gate 		rt_state = rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC);
25217c478bd9Sstevel@tonic-gate 	}
25227c478bd9Sstevel@tonic-gate 
25237c478bd9Sstevel@tonic-gate 	if (badifp != NULL) {
25247c478bd9Sstevel@tonic-gate 		/*
25257c478bd9Sstevel@tonic-gate 		 * Dont mark the rtentry bad unless the ifp for the primary
25267c478bd9Sstevel@tonic-gate 		 * route is the bad ifp
25277c478bd9Sstevel@tonic-gate 		 */
25287c478bd9Sstevel@tonic-gate 		if (rt->rt_spares[0].rts_ifp != badifp)
25297c478bd9Sstevel@tonic-gate 			return;
25307c478bd9Sstevel@tonic-gate 		/*
25317c478bd9Sstevel@tonic-gate 		 * badifp has just gone bad. We want to keep this
25327c478bd9Sstevel@tonic-gate 		 * rt_entry around so that we tell our rip-neighbors
25337c478bd9Sstevel@tonic-gate 		 * about the bad route, but we can't do anything
25347c478bd9Sstevel@tonic-gate 		 * to the kernel itself, so mark it as RS_BADIF
25357c478bd9Sstevel@tonic-gate 		 */
25367c478bd9Sstevel@tonic-gate 		trace_misc("rtbad:Setting RS_BADIF (%s)", badifp->int_name);
25377c478bd9Sstevel@tonic-gate 		rt_state |= RS_BADIF;
25387c478bd9Sstevel@tonic-gate 		new.rts_ifp = &dummy_ifp;
25397c478bd9Sstevel@tonic-gate 	}
25407c478bd9Sstevel@tonic-gate 	rtchange(rt, rt_state, &new, 0);
25417c478bd9Sstevel@tonic-gate 	rtswitch(rt, 0);
25427c478bd9Sstevel@tonic-gate }
25437c478bd9Sstevel@tonic-gate 
25447c478bd9Sstevel@tonic-gate 
25457c478bd9Sstevel@tonic-gate /*
25467c478bd9Sstevel@tonic-gate  * Junk a RS_NET_SYN or RS_LOCAL route,
25477c478bd9Sstevel@tonic-gate  *	unless it is needed by another interface.
25487c478bd9Sstevel@tonic-gate  */
25497c478bd9Sstevel@tonic-gate void
25507c478bd9Sstevel@tonic-gate rtbad_sub(struct rt_entry *rt, struct interface *badifp)
25517c478bd9Sstevel@tonic-gate {
25527c478bd9Sstevel@tonic-gate 	struct interface *ifp, *ifp1;
25537c478bd9Sstevel@tonic-gate 	struct intnet *intnetp;
25547c478bd9Sstevel@tonic-gate 	uint_t state;
25557c478bd9Sstevel@tonic-gate 
25567c478bd9Sstevel@tonic-gate 
25577c478bd9Sstevel@tonic-gate 	ifp1 = NULL;
25587c478bd9Sstevel@tonic-gate 	state = 0;
25597c478bd9Sstevel@tonic-gate 
25607c478bd9Sstevel@tonic-gate 	if (rt->rt_state & RS_LOCAL) {
25617c478bd9Sstevel@tonic-gate 		/*
25627c478bd9Sstevel@tonic-gate 		 * Is this the route through loopback for the interface?
25637c478bd9Sstevel@tonic-gate 		 * If so, see if it is used by any other interfaces, such
25647c478bd9Sstevel@tonic-gate 		 * as a point-to-point interface with the same local address.
25657c478bd9Sstevel@tonic-gate 		 */
25667c478bd9Sstevel@tonic-gate 		for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
25677c478bd9Sstevel@tonic-gate 			/* Retain it if another interface needs it. */
25687c478bd9Sstevel@tonic-gate 			if (ifp->int_addr == rt->rt_ifp->int_addr) {
25697c478bd9Sstevel@tonic-gate 				state |= RS_LOCAL;
25707c478bd9Sstevel@tonic-gate 				ifp1 = ifp;
25717c478bd9Sstevel@tonic-gate 				break;
25727c478bd9Sstevel@tonic-gate 			}
25737c478bd9Sstevel@tonic-gate 		}
25747c478bd9Sstevel@tonic-gate 
25757c478bd9Sstevel@tonic-gate 	}
25767c478bd9Sstevel@tonic-gate 
25777c478bd9Sstevel@tonic-gate 	if (!(state & RS_LOCAL)) {
25787c478bd9Sstevel@tonic-gate 		/*
25797c478bd9Sstevel@tonic-gate 		 * Retain RIPv1 logical network route if there is another
25807c478bd9Sstevel@tonic-gate 		 * interface that justifies it.
25817c478bd9Sstevel@tonic-gate 		 */
25827c478bd9Sstevel@tonic-gate 		if (rt->rt_state & RS_NET_SYN) {
25837c478bd9Sstevel@tonic-gate 			for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
25847c478bd9Sstevel@tonic-gate 				if ((ifp->int_state & IS_NEED_NET_SYN) &&
25857c478bd9Sstevel@tonic-gate 				    rt->rt_mask == ifp->int_std_mask &&
25867c478bd9Sstevel@tonic-gate 				    rt->rt_dst == ifp->int_std_addr) {
25877c478bd9Sstevel@tonic-gate 					state |= RS_NET_SYN;
25887c478bd9Sstevel@tonic-gate 					ifp1 = ifp;
25897c478bd9Sstevel@tonic-gate 					break;
25907c478bd9Sstevel@tonic-gate 				}
25917c478bd9Sstevel@tonic-gate 			}
25927c478bd9Sstevel@tonic-gate 		}
25937c478bd9Sstevel@tonic-gate 
25947c478bd9Sstevel@tonic-gate 		/* or if there is an authority route that needs it. */
25957c478bd9Sstevel@tonic-gate 		for (intnetp = intnets; intnetp != NULL;
25967c478bd9Sstevel@tonic-gate 		    intnetp = intnetp->intnet_next) {
25977c478bd9Sstevel@tonic-gate 			if (intnetp->intnet_addr == rt->rt_dst &&
25987c478bd9Sstevel@tonic-gate 			    intnetp->intnet_mask == rt->rt_mask) {
25997c478bd9Sstevel@tonic-gate 				state |= (RS_NET_SYN | RS_NET_INT);
26007c478bd9Sstevel@tonic-gate 				break;
26017c478bd9Sstevel@tonic-gate 			}
26027c478bd9Sstevel@tonic-gate 		}
26037c478bd9Sstevel@tonic-gate 	}
26047c478bd9Sstevel@tonic-gate 
26057c478bd9Sstevel@tonic-gate 	if (ifp1 != NULL || (state & RS_NET_SYN)) {
26067c478bd9Sstevel@tonic-gate 		struct rt_spare new = rt->rt_spares[0];
26077c478bd9Sstevel@tonic-gate 		new.rts_ifp = ifp1;
26087c478bd9Sstevel@tonic-gate 		rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN|RS_LOCAL)) | state),
26097c478bd9Sstevel@tonic-gate 		    &new, 0);
26107c478bd9Sstevel@tonic-gate 	} else {
26117c478bd9Sstevel@tonic-gate 		rtbad(rt, badifp);
26127c478bd9Sstevel@tonic-gate 	}
26137c478bd9Sstevel@tonic-gate }
26147c478bd9Sstevel@tonic-gate 
26157c478bd9Sstevel@tonic-gate /*
26167c478bd9Sstevel@tonic-gate  * Called while walking the table looking for sick interfaces
26177c478bd9Sstevel@tonic-gate  * or after a time change.
26187c478bd9Sstevel@tonic-gate  */
26197c478bd9Sstevel@tonic-gate int
26207c478bd9Sstevel@tonic-gate walk_bad(struct radix_node *rn,
26217c478bd9Sstevel@tonic-gate     void *argp)
26227c478bd9Sstevel@tonic-gate {
26237c478bd9Sstevel@tonic-gate #define	RT ((struct rt_entry *)rn)
26247c478bd9Sstevel@tonic-gate 	struct rt_spare *rts;
26257c478bd9Sstevel@tonic-gate 	int i, j = -1;
26267c478bd9Sstevel@tonic-gate 
26277c478bd9Sstevel@tonic-gate 	/* fix any spare routes through the interface */
26287c478bd9Sstevel@tonic-gate 	for (i = 1; i < RT->rt_num_spares; i++) {
26297c478bd9Sstevel@tonic-gate 		rts = &((struct rt_entry *)rn)->rt_spares[i];
26307c478bd9Sstevel@tonic-gate 
26317c478bd9Sstevel@tonic-gate 		if (rts->rts_metric < HOPCNT_INFINITY &&
26327c478bd9Sstevel@tonic-gate 		    (rts->rts_ifp == NULL ||
26337c478bd9Sstevel@tonic-gate 		    (rts->rts_ifp->int_state & IS_BROKE)))
26347c478bd9Sstevel@tonic-gate 			rts_delete(RT, rts);
26357c478bd9Sstevel@tonic-gate 		else {
26367c478bd9Sstevel@tonic-gate 			if (rts->rts_origin != RO_NONE)
26377c478bd9Sstevel@tonic-gate 				j = i;
26387c478bd9Sstevel@tonic-gate 		}
26397c478bd9Sstevel@tonic-gate 	}
26407c478bd9Sstevel@tonic-gate 
26417c478bd9Sstevel@tonic-gate 	/*
26427c478bd9Sstevel@tonic-gate 	 * Deal with the main route
26437c478bd9Sstevel@tonic-gate 	 * finished if it has been handled before or if its interface is ok
26447c478bd9Sstevel@tonic-gate 	 */
26457c478bd9Sstevel@tonic-gate 	if (RT->rt_ifp == NULL || !(RT->rt_ifp->int_state & IS_BROKE))
26467c478bd9Sstevel@tonic-gate 		return (0);
26477c478bd9Sstevel@tonic-gate 
26487c478bd9Sstevel@tonic-gate 	/* Bad routes for other than interfaces are easy. */
26497c478bd9Sstevel@tonic-gate 	if (!(RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) {
265009f979dcSsowmini 		if (j > 0) {
265109f979dcSsowmini 			RT->rt_spares[0].rts_metric = HOPCNT_INFINITY;
26527c478bd9Sstevel@tonic-gate 			rtswitch(RT, NULL);
265309f979dcSsowmini 		} else {
26547c478bd9Sstevel@tonic-gate 			rtbad(RT, (struct interface *)argp);
265509f979dcSsowmini 		}
26567c478bd9Sstevel@tonic-gate 		return (0);
26577c478bd9Sstevel@tonic-gate 	}
26587c478bd9Sstevel@tonic-gate 
26597c478bd9Sstevel@tonic-gate 	rtbad_sub(RT, (struct interface *)argp);
26607c478bd9Sstevel@tonic-gate 	return (0);
26617c478bd9Sstevel@tonic-gate #undef RT
26627c478bd9Sstevel@tonic-gate }
26637c478bd9Sstevel@tonic-gate 
26647c478bd9Sstevel@tonic-gate /*
26657c478bd9Sstevel@tonic-gate  * Called while walking the table to replace a duplicate interface
26667c478bd9Sstevel@tonic-gate  * with a backup.
26677c478bd9Sstevel@tonic-gate  */
26687c478bd9Sstevel@tonic-gate int
26697c478bd9Sstevel@tonic-gate walk_rewire(struct radix_node *rn, void *argp)
26707c478bd9Sstevel@tonic-gate {
26717c478bd9Sstevel@tonic-gate 	struct rt_entry *RT = (struct rt_entry *)rn;
26727c478bd9Sstevel@tonic-gate 	struct rewire_data *wire = (struct rewire_data *)argp;
26737c478bd9Sstevel@tonic-gate 	struct rt_spare *rts;
26747c478bd9Sstevel@tonic-gate 	int i;
26757c478bd9Sstevel@tonic-gate 
26767c478bd9Sstevel@tonic-gate 	/* fix any spare routes through the interface */
26777c478bd9Sstevel@tonic-gate 	rts = RT->rt_spares;
26787c478bd9Sstevel@tonic-gate 	for (i = RT->rt_num_spares; i > 0; i--, rts++) {
26797c478bd9Sstevel@tonic-gate 		if (rts->rts_ifp == wire->if_old) {
26807c478bd9Sstevel@tonic-gate 			rts->rts_ifp = wire->if_new;
26817c478bd9Sstevel@tonic-gate 			if ((RT->rt_dst == RIP_DEFAULT) &&
26827c478bd9Sstevel@tonic-gate 			    (wire->if_old->int_state & IS_SUPPRESS_RDISC))
26837c478bd9Sstevel@tonic-gate 				rdisc_suppress(rts->rts_ifp);
26847c478bd9Sstevel@tonic-gate 			if ((rts->rts_metric += wire->metric_delta) >
26857c478bd9Sstevel@tonic-gate 			    HOPCNT_INFINITY)
26867c478bd9Sstevel@tonic-gate 				rts->rts_metric = HOPCNT_INFINITY;
26877c478bd9Sstevel@tonic-gate 
26887c478bd9Sstevel@tonic-gate 			/*
26897c478bd9Sstevel@tonic-gate 			 * If the main route is getting a worse metric,
26907c478bd9Sstevel@tonic-gate 			 * then it may be time to switch to a backup.
26917c478bd9Sstevel@tonic-gate 			 */
26927c478bd9Sstevel@tonic-gate 			if (i == RT->rt_num_spares && wire->metric_delta > 0) {
26937c478bd9Sstevel@tonic-gate 				rtswitch(RT, NULL);
26947c478bd9Sstevel@tonic-gate 			}
26957c478bd9Sstevel@tonic-gate 		}
26967c478bd9Sstevel@tonic-gate 	}
26977c478bd9Sstevel@tonic-gate 
26987c478bd9Sstevel@tonic-gate 	return (0);
26997c478bd9Sstevel@tonic-gate }
27007c478bd9Sstevel@tonic-gate 
27017c478bd9Sstevel@tonic-gate /* Check the age of an individual route. */
27027c478bd9Sstevel@tonic-gate static int
27037c478bd9Sstevel@tonic-gate walk_age(struct radix_node *rn, void *argp)
27047c478bd9Sstevel@tonic-gate {
27057c478bd9Sstevel@tonic-gate #define	RT ((struct rt_entry *)rn)
27067c478bd9Sstevel@tonic-gate 	struct interface *ifp;
27077c478bd9Sstevel@tonic-gate 	struct rt_spare *rts;
27087c478bd9Sstevel@tonic-gate 	int i;
27097c478bd9Sstevel@tonic-gate 	in_addr_t age_bad_gate = *(in_addr_t *)argp;
27107c478bd9Sstevel@tonic-gate 
27117c478bd9Sstevel@tonic-gate 
27127c478bd9Sstevel@tonic-gate 	/*
27137c478bd9Sstevel@tonic-gate 	 * age all of the spare routes, including the primary route
27147c478bd9Sstevel@tonic-gate 	 * currently in use
27157c478bd9Sstevel@tonic-gate 	 */
27167c478bd9Sstevel@tonic-gate 	rts = RT->rt_spares;
27177c478bd9Sstevel@tonic-gate 	for (i = RT->rt_num_spares; i != 0; i--, rts++) {
27187c478bd9Sstevel@tonic-gate 
27197c478bd9Sstevel@tonic-gate 		ifp = rts->rts_ifp;
27207c478bd9Sstevel@tonic-gate 		if (i == RT->rt_num_spares) {
27217c478bd9Sstevel@tonic-gate 			if (!AGE_RT(RT->rt_state, rts->rts_origin, ifp)) {
27227c478bd9Sstevel@tonic-gate 				/*
27237c478bd9Sstevel@tonic-gate 				 * Keep various things from deciding ageless
27247c478bd9Sstevel@tonic-gate 				 * routes are stale
27257c478bd9Sstevel@tonic-gate 				 */
27267c478bd9Sstevel@tonic-gate 				rts->rts_time = now.tv_sec;
27277c478bd9Sstevel@tonic-gate 				continue;
27287c478bd9Sstevel@tonic-gate 			}
27297c478bd9Sstevel@tonic-gate 
27307c478bd9Sstevel@tonic-gate 			/* forget RIP routes after RIP has been turned off. */
27317c478bd9Sstevel@tonic-gate 			if (rip_sock < 0) {
27327c478bd9Sstevel@tonic-gate 				rts->rts_time = now_stale + 1;
27337c478bd9Sstevel@tonic-gate 			}
27347c478bd9Sstevel@tonic-gate 		}
27357c478bd9Sstevel@tonic-gate 
27367c478bd9Sstevel@tonic-gate 		/* age failing routes */
27377c478bd9Sstevel@tonic-gate 		if (age_bad_gate == rts->rts_gate &&
27387c478bd9Sstevel@tonic-gate 		    rts->rts_time >= now_stale) {
27397c478bd9Sstevel@tonic-gate 			rts->rts_time -= SUPPLY_INTERVAL;
27407c478bd9Sstevel@tonic-gate 		}
27417c478bd9Sstevel@tonic-gate 
27427c478bd9Sstevel@tonic-gate 		/* trash the spare routes when they go bad */
27437c478bd9Sstevel@tonic-gate 		if (rts->rts_origin == RO_RIP &&
27447c478bd9Sstevel@tonic-gate 		    ((rip_sock < 0) ||
27457c478bd9Sstevel@tonic-gate 		    (rts->rts_metric < HOPCNT_INFINITY &&
27467c478bd9Sstevel@tonic-gate 		    now_garbage > rts->rts_time)) &&
27477c478bd9Sstevel@tonic-gate 		    i != RT->rt_num_spares) {
27487c478bd9Sstevel@tonic-gate 			rts_delete(RT, rts);
27497c478bd9Sstevel@tonic-gate 		}
27507c478bd9Sstevel@tonic-gate 	}
27517c478bd9Sstevel@tonic-gate 
27527c478bd9Sstevel@tonic-gate 
27537c478bd9Sstevel@tonic-gate 	/* finished if the active route is still fresh */
27547c478bd9Sstevel@tonic-gate 	if (now_stale <= RT->rt_time)
27557c478bd9Sstevel@tonic-gate 		return (0);
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate 	/* try to switch to an alternative */
27587c478bd9Sstevel@tonic-gate 	rtswitch(RT, NULL);
27597c478bd9Sstevel@tonic-gate 
27607c478bd9Sstevel@tonic-gate 	/* Delete a dead route after it has been publically mourned. */
27617c478bd9Sstevel@tonic-gate 	if (now_garbage > RT->rt_time) {
27627c478bd9Sstevel@tonic-gate 		rtdelete(RT);
27637c478bd9Sstevel@tonic-gate 		return (0);
27647c478bd9Sstevel@tonic-gate 	}
27657c478bd9Sstevel@tonic-gate 
27667c478bd9Sstevel@tonic-gate 	/* Start poisoning a bad route before deleting it. */
27677c478bd9Sstevel@tonic-gate 	if (now.tv_sec - RT->rt_time > EXPIRE_TIME) {
27687c478bd9Sstevel@tonic-gate 		struct rt_spare new = RT->rt_spares[0];
27697c478bd9Sstevel@tonic-gate 
27707c478bd9Sstevel@tonic-gate 		new.rts_metric = HOPCNT_INFINITY;
27717c478bd9Sstevel@tonic-gate 		rtchange(RT, RT->rt_state, &new, 0);
27727c478bd9Sstevel@tonic-gate 	}
27737c478bd9Sstevel@tonic-gate 	return (0);
27747c478bd9Sstevel@tonic-gate }
27757c478bd9Sstevel@tonic-gate 
27767c478bd9Sstevel@tonic-gate 
27777c478bd9Sstevel@tonic-gate /* Watch for dead routes and interfaces. */
27787c478bd9Sstevel@tonic-gate void
27797c478bd9Sstevel@tonic-gate age(in_addr_t bad_gate)
27807c478bd9Sstevel@tonic-gate {
27817c478bd9Sstevel@tonic-gate 	struct interface *ifp;
27827c478bd9Sstevel@tonic-gate 	int need_query = 0;
27837c478bd9Sstevel@tonic-gate 
27847c478bd9Sstevel@tonic-gate 	/*
27857c478bd9Sstevel@tonic-gate 	 * If not listening to RIP, there is no need to age the routes in
27867c478bd9Sstevel@tonic-gate 	 * the table.
27877c478bd9Sstevel@tonic-gate 	 */
27887c478bd9Sstevel@tonic-gate 	age_timer.tv_sec = (now.tv_sec
27897c478bd9Sstevel@tonic-gate 	    + ((rip_sock < 0) ? NEVER : SUPPLY_INTERVAL));
27907c478bd9Sstevel@tonic-gate 
27917c478bd9Sstevel@tonic-gate 	/*
27927c478bd9Sstevel@tonic-gate 	 * Check for dead IS_REMOTE interfaces by timing their
27937c478bd9Sstevel@tonic-gate 	 * transmissions.
27947c478bd9Sstevel@tonic-gate 	 */
27957c478bd9Sstevel@tonic-gate 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
27967c478bd9Sstevel@tonic-gate 		if (!(ifp->int_state & IS_REMOTE))
27977c478bd9Sstevel@tonic-gate 			continue;
27987c478bd9Sstevel@tonic-gate 
27997c478bd9Sstevel@tonic-gate 		/* ignore unreachable remote interfaces */
28007c478bd9Sstevel@tonic-gate 		if (!check_remote(ifp))
28017c478bd9Sstevel@tonic-gate 			continue;
28027c478bd9Sstevel@tonic-gate 
28037c478bd9Sstevel@tonic-gate 		/* Restore remote interface that has become reachable */
28047c478bd9Sstevel@tonic-gate 		if (ifp->int_state & IS_BROKE)
28057c478bd9Sstevel@tonic-gate 			if_ok(ifp, "remote ", _B_FALSE);
28067c478bd9Sstevel@tonic-gate 
28077c478bd9Sstevel@tonic-gate 		if (ifp->int_act_time != NEVER &&
28087c478bd9Sstevel@tonic-gate 		    now.tv_sec - ifp->int_act_time > EXPIRE_TIME) {
28097c478bd9Sstevel@tonic-gate 			writelog(LOG_NOTICE,
28107c478bd9Sstevel@tonic-gate 			    "remote interface %s to %s timed out after"
28117c478bd9Sstevel@tonic-gate 			    " %ld:%ld",
28127c478bd9Sstevel@tonic-gate 			    ifp->int_name,
28137c478bd9Sstevel@tonic-gate 			    naddr_ntoa(ifp->int_dstaddr),
28147c478bd9Sstevel@tonic-gate 			    (now.tv_sec - ifp->int_act_time)/60,
28157c478bd9Sstevel@tonic-gate 			    (now.tv_sec - ifp->int_act_time)%60);
28167c478bd9Sstevel@tonic-gate 			if_sick(ifp, _B_FALSE);
28177c478bd9Sstevel@tonic-gate 		}
28187c478bd9Sstevel@tonic-gate 
28197c478bd9Sstevel@tonic-gate 		/*
28207c478bd9Sstevel@tonic-gate 		 * If we have not heard from the other router
28217c478bd9Sstevel@tonic-gate 		 * recently, ask it.
28227c478bd9Sstevel@tonic-gate 		 */
28237c478bd9Sstevel@tonic-gate 		if (now.tv_sec >= ifp->int_query_time) {
28247c478bd9Sstevel@tonic-gate 			ifp->int_query_time = NEVER;
28257c478bd9Sstevel@tonic-gate 			need_query = 1;
28267c478bd9Sstevel@tonic-gate 		}
28277c478bd9Sstevel@tonic-gate 	}
28287c478bd9Sstevel@tonic-gate 
28297c478bd9Sstevel@tonic-gate 	/* Age routes. */
28307c478bd9Sstevel@tonic-gate 	(void) rn_walktree(rhead, walk_age, &bad_gate);
28317c478bd9Sstevel@tonic-gate 
28327c478bd9Sstevel@tonic-gate 	/*
28337c478bd9Sstevel@tonic-gate 	 * delete old redirected routes to keep the kernel table small
28347c478bd9Sstevel@tonic-gate 	 * and prevent blackholes
28357c478bd9Sstevel@tonic-gate 	 */
28367c478bd9Sstevel@tonic-gate 	del_redirects(bad_gate, now.tv_sec-STALE_TIME);
28377c478bd9Sstevel@tonic-gate 
28387c478bd9Sstevel@tonic-gate 	/* Update the kernel routing table. */
28397c478bd9Sstevel@tonic-gate 	fix_kern();
28407c478bd9Sstevel@tonic-gate 
28417c478bd9Sstevel@tonic-gate 	/* poke reticent remote gateways */
28427c478bd9Sstevel@tonic-gate 	if (need_query)
28437c478bd9Sstevel@tonic-gate 		rip_query();
28447c478bd9Sstevel@tonic-gate }
28457c478bd9Sstevel@tonic-gate 
28467c478bd9Sstevel@tonic-gate void
28477c478bd9Sstevel@tonic-gate kern_dump(void)
28487c478bd9Sstevel@tonic-gate {
28497c478bd9Sstevel@tonic-gate 	int i;
28507c478bd9Sstevel@tonic-gate 	struct khash *k;
28517c478bd9Sstevel@tonic-gate 
28527c478bd9Sstevel@tonic-gate 	for (i = 0; i < KHASH_SIZE; i++) {
28537c478bd9Sstevel@tonic-gate 		for (k = khash_bins[i]; k != NULL; k = k->k_next)
28547c478bd9Sstevel@tonic-gate 			trace_khash(k);
28557c478bd9Sstevel@tonic-gate 	}
28567c478bd9Sstevel@tonic-gate }
28577c478bd9Sstevel@tonic-gate 
28587c478bd9Sstevel@tonic-gate 
28597c478bd9Sstevel@tonic-gate static struct interface *
28607c478bd9Sstevel@tonic-gate gwkludge_iflookup(in_addr_t dstaddr, in_addr_t addr, in_addr_t mask)
28617c478bd9Sstevel@tonic-gate {
28627c478bd9Sstevel@tonic-gate 	uint32_t int_state;
28637c478bd9Sstevel@tonic-gate 	struct interface *ifp;
28647c478bd9Sstevel@tonic-gate 
28657c478bd9Sstevel@tonic-gate 	for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
28667c478bd9Sstevel@tonic-gate 		int_state = ifp->int_state;
28677c478bd9Sstevel@tonic-gate 
28687c478bd9Sstevel@tonic-gate 		if (!(int_state & IS_REMOTE))
28697c478bd9Sstevel@tonic-gate 			continue;
28707c478bd9Sstevel@tonic-gate 
28717c478bd9Sstevel@tonic-gate 		if (ifp->int_dstaddr == dstaddr && ifp->int_addr == addr &&
28727c478bd9Sstevel@tonic-gate 		    ifp->int_mask == mask)
28737c478bd9Sstevel@tonic-gate 			return (ifp);
28747c478bd9Sstevel@tonic-gate 	}
28757c478bd9Sstevel@tonic-gate 	return (NULL);
28767c478bd9Sstevel@tonic-gate }
2877