17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
57c478bdstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
67c478bdstevel@tonic-gate * (the "License").  You may not use this file except in compliance
77c478bdstevel@tonic-gate * with the License.
87c478bdstevel@tonic-gate *
97c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
117c478bdstevel@tonic-gate * See the License for the specific language governing permissions
127c478bdstevel@tonic-gate * and limitations under the License.
137c478bdstevel@tonic-gate *
147c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
157c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
177c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
187c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bdstevel@tonic-gate *
207c478bdstevel@tonic-gate * CDDL HEADER END
217c478bdstevel@tonic-gate */
227c478bdstevel@tonic-gate/*
237c478bdstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
247c478bdstevel@tonic-gate * Use is subject to license terms.
257c478bdstevel@tonic-gate */
267c478bdstevel@tonic-gate
277c478bdstevel@tonic-gate/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
287c478bdstevel@tonic-gate/*	  All Rights Reserved  	*/
297c478bdstevel@tonic-gate
307c478bdstevel@tonic-gate/*
317c478bdstevel@tonic-gate * Portions of this source code were derived from Berkeley 4.3 BSD
327c478bdstevel@tonic-gate * under license from the Regents of the University of California.
337c478bdstevel@tonic-gate */
347c478bdstevel@tonic-gate
357c478bdstevel@tonic-gate#pragma ident	"%Z%%M%	%I%	%E% SMI"
367c478bdstevel@tonic-gate
377c478bdstevel@tonic-gate/*
387c478bdstevel@tonic-gate * Routing Table Management Daemon
397c478bdstevel@tonic-gate */
407c478bdstevel@tonic-gate#include "defs.h"
417c478bdstevel@tonic-gate
427c478bdstevel@tonic-gateboolean_t	install = _B_TRUE;	/* update kernel routing table */
437c478bdstevel@tonic-gatestruct rthash	*net_hashes[IPV6_ABITS + 1];
447c478bdstevel@tonic-gate
457c478bdstevel@tonic-gate/*
467c478bdstevel@tonic-gate * Size of routing socket message used by in.ripngd which includes the header,
477c478bdstevel@tonic-gate * space for the RTA_DST, RTA_GATEWAY and RTA_NETMASK (each a sockaddr_in6)
487c478bdstevel@tonic-gate * plus space for the RTA_IFP (a sockaddr_dl).
497c478bdstevel@tonic-gate */
507c478bdstevel@tonic-gate#define	RIPNG_RTM_MSGLEN	sizeof (struct rt_msghdr) +	\
517c478bdstevel@tonic-gate				sizeof (struct sockaddr_in6) +	\
527c478bdstevel@tonic-gate				sizeof (struct sockaddr_in6) +	\
537c478bdstevel@tonic-gate				sizeof (struct sockaddr_in6) +	\
547c478bdstevel@tonic-gate				sizeof (struct sockaddr_dl)
557c478bdstevel@tonic-gate
567c478bdstevel@tonic-gatestatic int	rtmseq;				/* rtm_seq sequence number */
577c478bdstevel@tonic-gatestatic int	rtsock;				/* Routing socket */
587c478bdstevel@tonic-gatestatic struct	rt_msghdr	*rt_msg;	/* Routing socket message */
597c478bdstevel@tonic-gatestatic struct	sockaddr_in6	*rta_dst;	/* RTA_DST sockaddr */
607c478bdstevel@tonic-gatestatic struct	sockaddr_in6	*rta_gateway;	/* RTA_GATEWAY sockaddr */
617c478bdstevel@tonic-gatestatic struct	sockaddr_in6	*rta_netmask;	/* RTA_NETMASK sockaddr */
627c478bdstevel@tonic-gatestatic struct	sockaddr_dl	*rta_ifp;	/* RTA_IFP sockaddr */
637c478bdstevel@tonic-gate
647c478bdstevel@tonic-gate/* simulate vax insque and remque instructions. */
657c478bdstevel@tonic-gate
667c478bdstevel@tonic-gatetypedef struct vq {
677c478bdstevel@tonic-gate	caddr_t	 fwd, back;
687c478bdstevel@tonic-gate} vq_t;
697c478bdstevel@tonic-gate
707c478bdstevel@tonic-gate#define	insque(e, p)	((vq_t *)(e))->back = (caddr_t)(p); \
717c478bdstevel@tonic-gate			((vq_t *)(e))->fwd = \
727c478bdstevel@tonic-gate				(caddr_t)((vq_t *)((vq_t *)(p))->fwd); \
737c478bdstevel@tonic-gate			((vq_t *)((vq_t *)(p))->fwd)->back = (caddr_t)(e); \
747c478bdstevel@tonic-gate			((vq_t *)(p))->fwd = (caddr_t)(e);
757c478bdstevel@tonic-gate
767c478bdstevel@tonic-gate#define	remque(e)	((vq_t *)((vq_t *)(e))->back)->fwd =  \
777c478bdstevel@tonic-gate				(caddr_t)((vq_t *)(e))->fwd; \
787c478bdstevel@tonic-gate			((vq_t *)((vq_t *)(e))->fwd)->back = \
797c478bdstevel@tonic-gate				(caddr_t)((vq_t *)(e))->back; \
807c478bdstevel@tonic-gate			((vq_t *)(e))->fwd = NULL; \
817c478bdstevel@tonic-gate			((vq_t *)(e))->back = NULL;
827c478bdstevel@tonic-gate
837c478bdstevel@tonic-gatestatic void
847c478bdstevel@tonic-gatelog_change(int level, struct rt_entry *orig, struct rt_entry *new)
857c478bdstevel@tonic-gate{
867c478bdstevel@tonic-gate	char buf1[INET6_ADDRSTRLEN];
877c478bdstevel@tonic-gate	char buf2[INET6_ADDRSTRLEN];
887c478bdstevel@tonic-gate	char buf3[INET6_ADDRSTRLEN];
897c478bdstevel@tonic-gate
907c478bdstevel@tonic-gate	(void) inet_ntop(AF_INET6, (void *) &new->rt_dst, buf1, sizeof (buf1));
917c478bdstevel@tonic-gate	(void) inet_ntop(AF_INET6, (void *) &orig->rt_router, buf2,
927c478bdstevel@tonic-gate	    sizeof (buf2));
937c478bdstevel@tonic-gate	(void) inet_ntop(AF_INET6, (void *) &new->rt_router, buf3,
947c478bdstevel@tonic-gate	    sizeof (buf3));
957c478bdstevel@tonic-gate
967c478bdstevel@tonic-gate	syslog(level, "\tdst %s from gw %s if %s to gw %s if %s metric %d",
977c478bdstevel@tonic-gate	    buf1, buf2,
987c478bdstevel@tonic-gate	    (orig->rt_ifp != NULL && orig->rt_ifp->int_name != NULL) ?
997c478bdstevel@tonic-gate		orig->rt_ifp->int_name : "(noname)",
1007c478bdstevel@tonic-gate	    buf3,
1017c478bdstevel@tonic-gate	    (new->rt_ifp != NULL && new->rt_ifp->int_name != NULL) ?
1027c478bdstevel@tonic-gate		new->rt_ifp->int_name : "(noname)", new->rt_metric);
1037c478bdstevel@tonic-gate}
1047c478bdstevel@tonic-gate
1057c478bdstevel@tonic-gatestatic void
1067c478bdstevel@tonic-gatelog_single(int level, struct rt_entry *rt)
1077c478bdstevel@tonic-gate{
1087c478bdstevel@tonic-gate	char buf1[INET6_ADDRSTRLEN];
1097c478bdstevel@tonic-gate	char buf2[INET6_ADDRSTRLEN];
1107c478bdstevel@tonic-gate
1117c478bdstevel@tonic-gate	(void) inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1));
1127c478bdstevel@tonic-gate	(void) inet_ntop(AF_INET6, (void *)&rt->rt_router, buf2, sizeof (buf2));
1137c478bdstevel@tonic-gate
1147c478bdstevel@tonic-gate	syslog(level, "\tdst %s gw %s if %s metric %d",
1157c478bdstevel@tonic-gate	    buf1, buf2,
1167c478bdstevel@tonic-gate	    (rt->rt_ifp != NULL && rt->rt_ifp->int_name != NULL) ?
1177c478bdstevel@tonic-gate		rt->rt_ifp->int_name : "(noname)",
1187c478bdstevel@tonic-gate	    rt->rt_metric);
1197c478bdstevel@tonic-gate}
1207c478bdstevel@tonic-gate
1217c478bdstevel@tonic-gate/*
1227c478bdstevel@tonic-gate * Computes a hash by XOR-ing the (up to sixteen) octets that make up an IPv6
1237c478bdstevel@tonic-gate * address.  This function assumes that that there are no one-bits in the
1247c478bdstevel@tonic-gate * address beyond the prefix length.
1257c478bdstevel@tonic-gate */
1267c478bdstevel@tonic-gatestatic uint8_t
1277c478bdstevel@tonic-gaterthash(struct in6_addr *dst, int prefix_length)
1287c478bdstevel@tonic-gate{
1297c478bdstevel@tonic-gate	uint8_t val = 0;
1307c478bdstevel@tonic-gate	int i;
1317c478bdstevel@tonic-gate
1327c478bdstevel@tonic-gate	for (i = 0; prefix_length > 0; prefix_length -= 8, i++)
1337c478bdstevel@tonic-gate		val ^= dst->s6_addr[i];
1347c478bdstevel@tonic-gate	return (val);
1357c478bdstevel@tonic-gate}
1367c478bdstevel@tonic-gate
1377c478bdstevel@tonic-gate/*
1387c478bdstevel@tonic-gate * Given a prefix length, fill in the struct in6_addr representing an IPv6
1397c478bdstevel@tonic-gate * netmask.
1407c478bdstevel@tonic-gate */
1417c478bdstevel@tonic-gatestatic void
1427c478bdstevel@tonic-gatertmask_to_bits(uint_t prefix_length, struct in6_addr *prefix)
1437c478bdstevel@tonic-gate{
1447c478bdstevel@tonic-gate	uint_t mask = 0xff;
1457c478bdstevel@tonic-gate	int i;
1467c478bdstevel@tonic-gate
1477c478bdstevel@tonic-gate	bzero((caddr_t)prefix, sizeof (struct in6_addr));
1487c478bdstevel@tonic-gate	for (i = 0; prefix_length >= 8; prefix_length -= 8, i++)
1497c478bdstevel@tonic-gate		prefix->s6_addr[i] = 0xff;
1507c478bdstevel@tonic-gate	mask = (mask << (8 - prefix_length));
1517c478bdstevel@tonic-gate	if (mask != 0)
1527c478bdstevel@tonic-gate		prefix->s6_addr[i] = mask;
1537c478bdstevel@tonic-gate}
1547c478bdstevel@tonic-gate
1557c478bdstevel@tonic-gatevoid
1567c478bdstevel@tonic-gatertcreate_prefix(struct in6_addr *p1, struct in6_addr *dst, int bits)
1577c478bdstevel@tonic-gate{
1587c478bdstevel@tonic-gate	uchar_t mask;
1597c478bdstevel@tonic-gate	int j;
1607c478bdstevel@tonic-gate
1617c478bdstevel@tonic-gate	for (j = 0; bits >= 8; bits -= 8, j++)
1627c478bdstevel@tonic-gate		dst->s6_addr[j] = p1->s6_addr[j];
1637c478bdstevel@tonic-gate
1647c478bdstevel@tonic-gate	if (bits != 0) {
1657c478bdstevel@tonic-gate		mask = 0xff << (8 - bits);
1667c478bdstevel@tonic-gate		dst->s6_addr[j] = p1->s6_addr[j] & mask;
1677c478bdstevel@tonic-gate		j++;
1687c478bdstevel@tonic-gate	}
1697c478bdstevel@tonic-gate
1707c478bdstevel@tonic-gate	for (; j < 16; j++)
1717c478bdstevel@tonic-gate		dst->s6_addr[j] = 0;
1727c478bdstevel@tonic-gate}
1737c478bdstevel@tonic-gate
1747c478bdstevel@tonic-gate/*
1757c478bdstevel@tonic-gate * Lookup dst in the tables for an exact match.
1767c478bdstevel@tonic-gate */
1777c478bdstevel@tonic-gatestruct rt_entry *
1787c478bdstevel@tonic-gatertlookup(struct in6_addr *dst, int prefix_length)
1797c478bdstevel@tonic-gate{
1807c478bdstevel@tonic-gate	struct rt_entry *rt;
1817c478bdstevel@tonic-gate	struct rthash *rh;
1827c478bdstevel@tonic-gate	uint_t	hash;
1837c478bdstevel@tonic-gate
1847c478bdstevel@tonic-gate	if (net_hashes[prefix_length] == NULL)
1857c478bdstevel@tonic-gate		return (NULL);
1867c478bdstevel@tonic-gate
1877c478bdstevel@tonic-gate	hash = rthash(dst, prefix_length);
1887c478bdstevel@tonic-gate
1897c478bdstevel@tonic-gate	rh = &net_hashes[prefix_length][hash & ROUTEHASHMASK];
1907c478bdstevel@tonic-gate
1917c478bdstevel@tonic-gate	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
1927c478bdstevel@tonic-gate		if (rt->rt_hash != hash)
1937c478bdstevel@tonic-gate			continue;
1947c478bdstevel@tonic-gate		if (IN6_ARE_ADDR_EQUAL(&rt->rt_dst, dst) &&
1957c478bdstevel@tonic-gate		    rt->rt_prefix_length == prefix_length)
1967c478bdstevel@tonic-gate			return (rt);
1977c478bdstevel@tonic-gate	}
1987c478bdstevel@tonic-gate	return (NULL);
1997c478bdstevel@tonic-gate}
2007c478bdstevel@tonic-gate
2017c478bdstevel@tonic-gate/*
2027c478bdstevel@tonic-gate * Given an IPv6 prefix (destination and prefix length), a gateway, an
2037c478bdstevel@tonic-gate * interface name and route flags, send down the requested command returning
2047c478bdstevel@tonic-gate * the return value and errno (in the case of error) from the write() on the
2057c478bdstevel@tonic-gate * routing socket.
2067c478bdstevel@tonic-gate */
2077c478bdstevel@tonic-gatestatic int
2087c478bdstevel@tonic-gatertcmd(uchar_t type, struct in6_addr *dst, struct in6_addr *gateway,
2097c478bdstevel@tonic-gate    uint_t prefix_length, char *name, int flags)
2107c478bdstevel@tonic-gate{
2117c478bdstevel@tonic-gate	int rlen;
2127c478bdstevel@tonic-gate
2137c478bdstevel@tonic-gate	rta_ifp->sdl_index = if_nametoindex(name);
2147c478bdstevel@tonic-gate	if (rta_ifp->sdl_index == 0)
2157c478bdstevel@tonic-gate		return (-1);
2167c478bdstevel@tonic-gate
2177c478bdstevel@tonic-gate	rta_dst->sin6_addr = *dst;
2187c478bdstevel@tonic-gate	rta_gateway->sin6_addr = *gateway;
2197c478bdstevel@tonic-gate	rtmask_to_bits(prefix_length, &rta_netmask->sin6_addr);
2207c478bdstevel@tonic-gate
2217c478bdstevel@tonic-gate	rt_msg->rtm_type = type;
2227c478bdstevel@tonic-gate	rt_msg->rtm_flags = flags;
2237c478bdstevel@tonic-gate	rt_msg->rtm_seq = ++rtmseq;
2247c478bdstevel@tonic-gate	rlen = write(rtsock, rt_msg, RIPNG_RTM_MSGLEN);
2257c478bdstevel@tonic-gate	if (rlen >= 0 && rlen < RIPNG_RTM_MSGLEN) {
2267c478bdstevel@tonic-gate		syslog(LOG_ERR,
2277c478bdstevel@tonic-gate		    "rtcmd: write to routing socket got only %d for rlen\n",
2287c478bdstevel@tonic-gate		    rlen);
2297c478bdstevel@tonic-gate	}
2307c478bdstevel@tonic-gate	return (rlen);
2317c478bdstevel@tonic-gate}
2327c478bdstevel@tonic-gate
2337c478bdstevel@tonic-gatevoid
2347c478bdstevel@tonic-gatertadd(struct in6_addr *dst, struct in6_addr *gate, int prefix_length,
2357c478bdstevel@tonic-gate    int metric, int tag, boolean_t ifroute, struct interface *ifp)
2367c478bdstevel@tonic-gate{
2377c478bdstevel@tonic-gate	struct rt_entry *rt;
2387c478bdstevel@tonic-gate	struct rthash *rh;
2397c478bdstevel@tonic-gate	uint_t hash;
2407c478bdstevel@tonic-gate	struct in6_addr pdst;
2417c478bdstevel@tonic-gate	int rlen;
2427c478bdstevel@tonic-gate
2437c478bdstevel@tonic-gate	if (metric >= HOPCNT_INFINITY)
2447c478bdstevel@tonic-gate		return;
2457c478bdstevel@tonic-gate
2467c478bdstevel@tonic-gate	if (net_hashes[prefix_length] == NULL) {
2477c478bdstevel@tonic-gate		struct rthash *trh;
2487c478bdstevel@tonic-gate
2497c478bdstevel@tonic-gate		rh = (struct rthash *)
2507c478bdstevel@tonic-gate		    calloc(ROUTEHASHSIZ, sizeof (struct rt_entry));
2517c478bdstevel@tonic-gate		if (rh == NULL)
2527c478bdstevel@tonic-gate			return;
2537c478bdstevel@tonic-gate		for (trh = rh; trh < &rh[ROUTEHASHSIZ]; trh++)
2547c478bdstevel@tonic-gate			trh->rt_forw = trh->rt_back = (struct rt_entry *)trh;
2557c478bdstevel@tonic-gate		net_hashes[prefix_length] = rh;
2567c478bdstevel@tonic-gate	}
2577c478bdstevel@tonic-gate	rtcreate_prefix(dst, &pdst, prefix_length);
2587c478bdstevel@tonic-gate
2597c478bdstevel@tonic-gate	hash = rthash(&pdst, prefix_length);
2607c478bdstevel@tonic-gate	rh = &net_hashes[prefix_length][hash & ROUTEHASHMASK];
2617c478bdstevel@tonic-gate	rt = (struct rt_entry *)malloc(sizeof (*rt));
2627c478bdstevel@tonic-gate	if (rt == NULL) {
2637c478bdstevel@tonic-gate		/*
2647c478bdstevel@tonic-gate		 * In the event of an allocation failure, log the error and
2657c478bdstevel@tonic-gate		 * continue since on the next update another attempt will be
2667c478bdstevel@tonic-gate		 * made.
2677c478bdstevel@tonic-gate		 */
2687c478bdstevel@tonic-gate		syslog(LOG_ERR, "rtadd: malloc: %m");
2697c478bdstevel@tonic-gate		return;
2707c478bdstevel@tonic-gate	}
2717c478bdstevel@tonic-gate	rt->rt_hash = hash;
2727c478bdstevel@tonic-gate	rt->rt_dst = pdst;
2737c478bdstevel@tonic-gate	rt->rt_prefix_length = prefix_length;
2747c478bdstevel@tonic-gate	rt->rt_router = *gate;
2757c478bdstevel@tonic-gate	rt->rt_metric = metric;
2767c478bdstevel@tonic-gate	rt->rt_tag = tag;
2777c478bdstevel@tonic-gate	rt->rt_timer = 0;
2787c478bdstevel@tonic-gate	rt->rt_flags = RTF_UP;
2797c478bdstevel@tonic-gate	if (prefix_length == IPV6_ABITS)
2807c478bdstevel@tonic-gate		rt->rt_flags |= RTF_HOST;
2817c478bdstevel@tonic-gate	rt->rt_state = RTS_CHANGED;
2827c478bdstevel@tonic-gate	if (ifroute) {
2837c478bdstevel@tonic-gate		rt->rt_state |= RTS_INTERFACE;
2847c478bdstevel@tonic-gate		if (ifp->int_flags & RIP6_IFF_PRIVATE)
2857c478bdstevel@tonic-gate			rt->rt_state |= RTS_PRIVATE;
2867c478bdstevel@tonic-gate	} else {
2877c478bdstevel@tonic-gate		rt->rt_flags |= RTF_GATEWAY;
2887c478bdstevel@tonic-gate	}
2897c478bdstevel@tonic-gate	rt->rt_ifp = ifp;
2907c478bdstevel@tonic-gate
2917c478bdstevel@tonic-gate	insque(rt, rh);
2927c478bdstevel@tonic-gate	TRACE_ACTION("ADD", rt);
2937c478bdstevel@tonic-gate	/*
2947c478bdstevel@tonic-gate	 * If the RTM_ADD fails because the gateway is unreachable
2957c478bdstevel@tonic-gate	 * from this host, discard the entry.  This should never
2967c478bdstevel@tonic-gate	 * happen.
2977c478bdstevel@tonic-gate	 */
2987c478bdstevel@tonic-gate	if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
2997c478bdstevel@tonic-gate		rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
3007c478bdstevel@tonic-gate		    prefix_length, ifp->int_name, rt->rt_flags);
3017c478bdstevel@tonic-gate		if (rlen < 0) {
3027c478bdstevel@tonic-gate			if (errno != EEXIST) {
3037c478bdstevel@tonic-gate				syslog(LOG_ERR, "rtadd: RTM_ADD: %m");
3047c478bdstevel@tonic-gate				log_single(LOG_ERR, rt);
3057c478bdstevel@tonic-gate			}
3067c478bdstevel@tonic-gate			if (errno == ENETUNREACH) {
3077c478bdstevel@tonic-gate				TRACE_ACTION("DELETE", rt);
3087c478bdstevel@tonic-gate				remque(rt);
3097c478bdstevel@tonic-gate				free((char *)rt);
3107c478bdstevel@tonic-gate			}
3117c478bdstevel@tonic-gate		} else if (rlen < RIPNG_RTM_MSGLEN) {
3127c478bdstevel@tonic-gate			log_single(LOG_ERR, rt);
3137c478bdstevel@tonic-gate		}
3147c478bdstevel@tonic-gate	}
3157c478bdstevel@tonic-gate}
3167c478bdstevel@tonic-gate
3177c478bdstevel@tonic-gate/*
3187c478bdstevel@tonic-gate * Handle the case when the metric changes but the gateway is the same (or the
3197c478bdstevel@tonic-gate * interface index associated with the gateway changes), or when both gateway
3207c478bdstevel@tonic-gate * and metric changes, or when only the gateway changes but the existing route
3217c478bdstevel@tonic-gate * is more than one-half of EXPIRE_TIME in age. Note that routes with metric >=
3227c478bdstevel@tonic-gate * HOPCNT_INFINITY are not in the kernel.
3237c478bdstevel@tonic-gate */
3247c478bdstevel@tonic-gatevoid
3257c478bdstevel@tonic-gatertchange(struct rt_entry *rt, struct in6_addr *gate, short metric,
3267c478bdstevel@tonic-gate    struct interface *ifp)
3277c478bdstevel@tonic-gate{
3287c478bdstevel@tonic-gate	boolean_t dokern = _B_FALSE;
3297c478bdstevel@tonic-gate	boolean_t dokerndelete;
3307c478bdstevel@tonic-gate	boolean_t metricchanged = _B_FALSE;
3317c478bdstevel@tonic-gate	int oldmetric;
3327c478bdstevel@tonic-gate	struct rt_entry oldroute;
3337c478bdstevel@tonic-gate	int rlen;
3347c478bdstevel@tonic-gate
3357c478bdstevel@tonic-gate	if (metric >= HOPCNT_INFINITY) {
3367c478bdstevel@tonic-gate		rtdown(rt);
3377c478bdstevel@tonic-gate		return;
3387c478bdstevel@tonic-gate	}
3397c478bdstevel@tonic-gate
3407c478bdstevel@tonic-gate	if (!IN6_ARE_ADDR_EQUAL(&rt->rt_router, gate) || rt->rt_ifp != ifp)
3417c478bdstevel@tonic-gate		dokern = _B_TRUE;
3427c478bdstevel@tonic-gate	oldmetric = rt->rt_metric;
3437c478bdstevel@tonic-gate	if (oldmetric >= HOPCNT_INFINITY)
3447c478bdstevel@tonic-gate		dokerndelete = _B_FALSE;
3457c478bdstevel@tonic-gate	else
3467c478bdstevel@tonic-gate		dokerndelete = dokern;
3477c478bdstevel@tonic-gate	if (metric != rt->rt_metric)
3487c478bdstevel@tonic-gate		metricchanged = _B_TRUE;
3497c478bdstevel@tonic-gate	rt->rt_timer = 0;
3507c478bdstevel@tonic-gate	if (dokern || metricchanged) {
3517c478bdstevel@tonic-gate		TRACE_ACTION("CHANGE FROM", rt);
3527c478bdstevel@tonic-gate		if ((rt->rt_state & RTS_INTERFACE) && metric != 0) {
3537c478bdstevel@tonic-gate			rt->rt_state &= ~RTS_INTERFACE;
3547c478bdstevel@tonic-gate			if (rt->rt_ifp != NULL) {
3557c478bdstevel@tonic-gate				syslog(LOG_ERR,
3567c478bdstevel@tonic-gate				    "rtchange: changing route from "
3577c478bdstevel@tonic-gate				    "interface %s (timed out)",
3587c478bdstevel@tonic-gate				    (rt->rt_ifp->int_name != NULL) ?
3597c478bdstevel@tonic-gate					rt->rt_ifp->int_name : "(noname)");
3607c478bdstevel@tonic-gate			} else {
3617c478bdstevel@tonic-gate				syslog(LOG_ERR,
3627c478bdstevel@tonic-gate				    "rtchange: "
3637c478bdstevel@tonic-gate				    "changing route no interface for route");
3647c478bdstevel@tonic-gate			}
3657c478bdstevel@tonic-gate		}
3667c478bdstevel@tonic-gate		if (dokern) {
3677c478bdstevel@tonic-gate			oldroute = *rt;
3687c478bdstevel@tonic-gate			rt->rt_router = *gate;
3697c478bdstevel@tonic-gate			rt->rt_ifp = ifp;
3707c478bdstevel@tonic-gate		}
3717c478bdstevel@tonic-gate		rt->rt_metric = metric;
3727c478bdstevel@tonic-gate		if (!(rt->rt_state & RTS_INTERFACE))
3737c478bdstevel@tonic-gate			rt->rt_flags |= RTF_GATEWAY;
3747c478bdstevel@tonic-gate		else
3757c478bdstevel@tonic-gate			rt->rt_flags &= ~RTF_GATEWAY;
3767c478bdstevel@tonic-gate		rt->rt_state |= RTS_CHANGED;
3777c478bdstevel@tonic-gate		TRACE_ACTION("CHANGE TO", rt);
3787c478bdstevel@tonic-gate	}
3797c478bdstevel@tonic-gate	if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
3807c478bdstevel@tonic-gate		if (dokerndelete) {
3817c478bdstevel@tonic-gate			rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
3827c478bdstevel@tonic-gate			    rt->rt_prefix_length, rt->rt_ifp->int_name,
3837c478bdstevel@tonic-gate			    rt->rt_flags);
3847c478bdstevel@tonic-gate			if (rlen < 0) {
3857c478bdstevel@tonic-gate				if (errno != EEXIST) {
3867c478bdstevel@tonic-gate					syslog(LOG_ERR,
3877c478bdstevel@tonic-gate					    "rtchange: RTM_ADD: %m");
3887c478bdstevel@tonic-gate					log_change(LOG_ERR, rt,
3897c478bdstevel@tonic-gate					    (struct rt_entry *)&oldroute);
3907c478bdstevel@tonic-gate				}
3917c478bdstevel@tonic-gate			} else if (rlen < RIPNG_RTM_MSGLEN) {
3927c478bdstevel@tonic-gate				log_change(LOG_ERR, rt,
3937c478bdstevel@tonic-gate				    (struct rt_entry *)&oldroute);
3947c478bdstevel@tonic-gate			}
3957c478bdstevel@tonic-gate
3967c478bdstevel@tonic-gate			rlen = rtcmd(RTM_DELETE, &oldroute.rt_dst,
3977c478bdstevel@tonic-gate			    &oldroute.rt_router, oldroute.rt_prefix_length,
3987c478bdstevel@tonic-gate			    oldroute.rt_ifp->int_name, oldroute.rt_flags);
3997c478bdstevel@tonic-gate			if (rlen < 0) {
4007c478bdstevel@tonic-gate				syslog(LOG_ERR, "rtchange: RTM_DELETE: %m");
4017c478bdstevel@tonic-gate				log_change(LOG_ERR, rt,
4027c478bdstevel@tonic-gate				    (struct rt_entry *)&oldroute);
4037c478bdstevel@tonic-gate			} else if (rlen < RIPNG_RTM_MSGLEN) {
4047c478bdstevel@tonic-gate				log_change(LOG_ERR, rt,
4057c478bdstevel@tonic-gate				    (struct rt_entry *)&oldroute);
4067c478bdstevel@tonic-gate			}
4077c478bdstevel@tonic-gate		} else if (dokern || oldmetric >= HOPCNT_INFINITY) {
4087c478bdstevel@tonic-gate			rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
4097c478bdstevel@tonic-gate			    rt->rt_prefix_length, ifp->int_name, rt->rt_flags);
4107c478bdstevel@tonic-gate			if (rlen < 0 && errno != EEXIST) {
4117c478bdstevel@tonic-gate				syslog(LOG_ERR, "rtchange: RTM_ADD: %m");
4127c478bdstevel@tonic-gate				log_change(LOG_ERR, rt,
4137c478bdstevel@tonic-gate				    (struct rt_entry *)&oldroute);
4147c478bdstevel@tonic-gate			} else if (rlen < RIPNG_RTM_MSGLEN) {
4157c478bdstevel@tonic-gate				log_change(LOG_ERR, rt,
4167c478bdstevel@tonic-gate				    (struct rt_entry *)&oldroute);
4177c478bdstevel@tonic-gate			}
4187c478bdstevel@tonic-gate		}
4197c478bdstevel@tonic-gate	}
4207c478bdstevel@tonic-gate}
4217c478bdstevel@tonic-gate
4227c478bdstevel@tonic-gatevoid
4237c478bdstevel@tonic-gatertdown(struct rt_entry *rt)
4247c478bdstevel@tonic-gate{
4257c478bdstevel@tonic-gate	int rlen;
4267c478bdstevel@tonic-gate
4277c478bdstevel@tonic-gate	if (rt->rt_metric != HOPCNT_INFINITY) {
4287c478bdstevel@tonic-gate		TRACE_ACTION("DELETE", rt);
4297c478bdstevel@tonic-gate		if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
4307c478bdstevel@tonic-gate			rlen = rtcmd(RTM_DELETE, &rt->rt_dst,
4317c478bdstevel@tonic-gate			    &rt->rt_router, rt->rt_prefix_length,
4327c478bdstevel@tonic-gate			    rt->rt_ifp->int_name, rt->rt_flags);
4337c478bdstevel@tonic-gate			if (rlen < 0) {
4347c478bdstevel@tonic-gate				syslog(LOG_ERR, "rtdown: RTM_DELETE: %m");
4357c478bdstevel@tonic-gate				log_single(LOG_ERR, rt);
4367c478bdstevel@tonic-gate			} else if (rlen < RIPNG_RTM_MSGLEN) {
4377c478bdstevel@tonic-gate				log_single(LOG_ERR, rt);
4387c478bdstevel@tonic-gate			}
4397c478bdstevel@tonic-gate		}
4407c478bdstevel@tonic-gate		rt->rt_metric = HOPCNT_INFINITY;
4417c478bdstevel@tonic-gate		rt->rt_state |= RTS_CHANGED;
4427c478bdstevel@tonic-gate	}
4437c478bdstevel@tonic-gate	if (rt->rt_timer < EXPIRE_TIME)
4447c478bdstevel@tonic-gate		rt->rt_timer = EXPIRE_TIME;
4457c478bdstevel@tonic-gate}
4467c478bdstevel@tonic-gate
4477c478bdstevel@tonic-gatevoid
4487c478bdstevel@tonic-gatertdelete(struct rt_entry *rt)
4497c478bdstevel@tonic-gate{
4507c478bdstevel@tonic-gate
4517c478bdstevel@tonic-gate	if (rt->rt_state & RTS_INTERFACE) {
4527c478bdstevel@tonic-gate		if (rt->rt_ifp != NULL) {
4537c478bdstevel@tonic-gate			syslog(LOG_ERR,
4547c478bdstevel@tonic-gate			    "rtdelete: "
4557c478bdstevel@tonic-gate			    "deleting route to interface %s (timed out)",
4567c478bdstevel@tonic-gate			    (rt->rt_ifp->int_name != NULL) ?
4577c478bdstevel@tonic-gate				rt->rt_ifp->int_name : "(noname)");
4587c478bdstevel@tonic-gate			log_single(LOG_ERR, rt);
4597c478bdstevel@tonic-gate		}
4607c478bdstevel@tonic-gate	}
4617c478bdstevel@tonic-gate	rtdown(rt);
4627c478bdstevel@tonic-gate	remque(rt);
4637c478bdstevel@tonic-gate	free((char *)rt);
4647c478bdstevel@tonic-gate}
4657c478bdstevel@tonic-gate
4667c478bdstevel@tonic-gate/*
4677c478bdstevel@tonic-gate * Mark all the routes heard off a particular interface "down".  Unlike the
4687c478bdstevel@tonic-gate * routes managed by in.routed, all of these routes have an interface associated
4697c478bdstevel@tonic-gate * with them.
4707c478bdstevel@tonic-gate */
4717c478bdstevel@tonic-gatevoid
4727c478bdstevel@tonic-gatertpurgeif(struct interface *ifp)
4737c478bdstevel@tonic-gate{
4747c478bdstevel@tonic-gate	struct rthash *rh;
4757c478bdstevel@tonic-gate	struct rt_entry *rt;
4767c478bdstevel@tonic-gate	int i;
4777c478bdstevel@tonic-gate
4787c478bdstevel@tonic-gate	for (i = IPV6_ABITS; i >= 0; i--) {
4797c478bdstevel@tonic-gate		if (net_hashes[i] == NULL)
4807c478bdstevel@tonic-gate			continue;
4817c478bdstevel@tonic-gate
4827c478bdstevel@tonic-gate		for (rh = net_hashes[i];
4837c478bdstevel@tonic-gate		    rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
4847c478bdstevel@tonic-gate			for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
4857c478bdstevel@tonic-gate			    rt = rt->rt_forw) {
4867c478bdstevel@tonic-gate				if (rt->rt_ifp == ifp) {
4877c478bdstevel@tonic-gate					rtdown(rt);
4887c478bdstevel@tonic-gate					rt->rt_ifp = NULL;
4897c478bdstevel@tonic-gate					rt->rt_state &= ~RTS_INTERFACE;
4907c478bdstevel@tonic-gate				}
4917c478bdstevel@tonic-gate			}
4927c478bdstevel@tonic-gate		}
4937c478bdstevel@tonic-gate	}
4947c478bdstevel@tonic-gate}
4957c478bdstevel@tonic-gate
4967c478bdstevel@tonic-gate/*
4977c478bdstevel@tonic-gate * Called when the subnetmask has changed on one or more interfaces.
4987c478bdstevel@tonic-gate * Re-evaluates all non-interface routes by doing a rtchange so that
4997c478bdstevel@tonic-gate * routes that were believed to be host routes before the netmask change
5007c478bdstevel@tonic-gate * can be converted to network routes and vice versa.
5017c478bdstevel@tonic-gate */
5027c478bdstevel@tonic-gatevoid
5037c478bdstevel@tonic-gatertchangeall(void)
5047c478bdstevel@tonic-gate{
5057c478bdstevel@tonic-gate	struct rthash *rh;
5067c478bdstevel@tonic-gate	struct rt_entry *rt;
5077c478bdstevel@tonic-gate	int i;
5087c478bdstevel@tonic-gate
5097c478bdstevel@tonic-gate	for (i = IPV6_ABITS; i >= 0; i--) {
5107c478bdstevel@tonic-gate		if (net_hashes[i] == NULL)
5117c478bdstevel@tonic-gate			continue;
5127c478bdstevel@tonic-gate
5137c478bdstevel@tonic-gate		for (rh = net_hashes[i];
5147c478bdstevel@tonic-gate		    rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
5157c478bdstevel@tonic-gate			for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
5167c478bdstevel@tonic-gate			    rt = rt->rt_forw) {
5177c478bdstevel@tonic-gate				if ((rt->rt_state & RTS_INTERFACE) == 0) {
5187c478bdstevel@tonic-gate					rtchange(rt, &rt->rt_router,
5197c478bdstevel@tonic-gate					    rt->rt_metric, rt->rt_ifp);
5207c478bdstevel@tonic-gate				}
5217c478bdstevel@tonic-gate			}
5227c478bdstevel@tonic-gate		}
5237c478bdstevel@tonic-gate	}
5247c478bdstevel@tonic-gate}
5257c478bdstevel@tonic-gate
5267c478bdstevel@tonic-gatestatic void
5277c478bdstevel@tonic-gatertdumpentry(FILE *fp, struct rt_entry *rt)
5287c478bdstevel@tonic-gate{
5297c478bdstevel@tonic-gate	char buf1[INET6_ADDRSTRLEN];
5307c478bdstevel@tonic-gate	static struct bits {
5317c478bdstevel@tonic-gate		ulong_t	t_bits;
5327c478bdstevel@tonic-gate		char	*t_name;
5337c478bdstevel@tonic-gate	} flagbits[] = {
5347c478bdstevel@tonic-gate		/* BEGIN CSTYLED */
5357c478bdstevel@tonic-gate		{ RTF_UP,		"UP" },
5367c478bdstevel@tonic-gate		{ RTF_GATEWAY,		"GATEWAY" },
5377c478bdstevel@tonic-gate		{ RTF_HOST,		"HOST" },
5387c478bdstevel@tonic-gate		{ 0,			NULL }
5397c478bdstevel@tonic-gate		/* END CSTYLED */
5407c478bdstevel@tonic-gate	}, statebits[] = {
5417c478bdstevel@tonic-gate		/* BEGIN CSTYLED */
5427c478bdstevel@tonic-gate		{ RTS_INTERFACE,	"INTERFACE" },
5437c478bdstevel@tonic-gate		{ RTS_CHANGED,		"CHANGED" },
5447c478bdstevel@tonic-gate		{ RTS_PRIVATE,		"PRIVATE" },
5457c478bdstevel@tonic-gate		{ 0,			NULL }
5467c478bdstevel@tonic-gate		/* END CSTYLED */
5477c478bdstevel@tonic-gate	};
5487c478bdstevel@tonic-gate	struct bits *p;
5497c478bdstevel@tonic-gate	boolean_t first;
5507c478bdstevel@tonic-gate	char c;
5517c478bdstevel@tonic-gate
5527c478bdstevel@tonic-gate	(void) fprintf(fp, "prefix %s/%d ",
5537c478bdstevel@tonic-gate	    inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1)),
5547c478bdstevel@tonic-gate	    rt->rt_prefix_length);
5557c478bdstevel@tonic-gate	(void) fprintf(fp, "via %s metric %d timer %d",
5567c478bdstevel@tonic-gate	    inet_ntop(AF_INET6, (void *)&rt->rt_router, buf1, sizeof (buf1)),
5577c478bdstevel@tonic-gate	    rt->rt_metric, rt->rt_timer);
5587c478bdstevel@tonic-gate	if (rt->rt_ifp != NULL) {
5597c478bdstevel@tonic-gate		(void) fprintf(fp, " if %s",
5607c478bdstevel@tonic-gate		    (rt->rt_ifp->int_name != NULL) ?
5617c478bdstevel@tonic-gate			rt->rt_ifp->int_name : "(noname)");
5627c478bdstevel@tonic-gate	}
5637c478bdstevel@tonic-gate	(void) fprintf(fp, " state");
5647c478bdstevel@tonic-gate	c = ' ';
5657c478bdstevel@tonic-gate	for (first = _B_TRUE, p = statebits; p->t_bits > 0; p++) {
5667c478bdstevel@tonic-gate		if ((rt->rt_state & p->t_bits) == 0)
5677c478bdstevel@tonic-gate			continue;
5687c478bdstevel@tonic-gate		(void) fprintf(fp, "%c%s", c, p->t_name);
5697c478bdstevel@tonic-gate		if (first) {
5707c478bdstevel@tonic-gate			c = '|';
5717c478bdstevel@tonic-gate			first = _B_FALSE;
5727c478bdstevel@tonic-gate		}
5737c478bdstevel@tonic-gate	}
5747c478bdstevel@tonic-gate	if (first)
5757c478bdstevel@tonic-gate		(void) fprintf(fp, " 0");
5767c478bdstevel@tonic-gate	if (rt->rt_flags & (RTF_UP | RTF_GATEWAY)) {
5777c478bdstevel@tonic-gate		c = ' ';
5787c478bdstevel@tonic-gate		for (first = _B_TRUE, p = flagbits; p->t_bits > 0; p++) {
5797c478bdstevel@tonic-gate			if ((rt->rt_flags & p->t_bits) == 0)
5807c478bdstevel@tonic-gate				continue;
5817c478bdstevel@tonic-gate			(void) fprintf(fp, "%c%s", c, p->t_name);
5827c478bdstevel@tonic-gate			if (first) {
5837c478bdstevel@tonic-gate				c = '|';
5847c478bdstevel@tonic-gate				first = _B_FALSE;
5857c478bdstevel@tonic-gate			}
5867c478bdstevel@tonic-gate		}
5877c478bdstevel@tonic-gate	}
5887c478bdstevel@tonic-gate	(void) putc('\n', fp);
5897c478bdstevel@tonic-gate	(void) fflush(fp);
5907c478bdstevel@tonic-gate}
5917c478bdstevel@tonic-gate
5927c478bdstevel@tonic-gatestatic void
5937c478bdstevel@tonic-gatertdump2(FILE *fp)
5947c478bdstevel@tonic-gate{
5957c478bdstevel@tonic-gate	struct rthash *rh;
5967c478bdstevel@tonic-gate	struct rt_entry *rt;
5977c478bdstevel@tonic-gate	int i;
5987c478bdstevel@tonic-gate
5997c478bdstevel@tonic-gate	for (i = IPV6_ABITS; i >= 0; i--) {
6007c478bdstevel@tonic-gate		if (net_hashes[i] == NULL)
6017c478bdstevel@tonic-gate			continue;
6027c478bdstevel@tonic-gate
6037c478bdstevel@tonic-gate		for (rh = net_hashes[i];
6047c478bdstevel@tonic-gate		    rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
6057c478bdstevel@tonic-gate			for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
6067c478bdstevel@tonic-gate			    rt = rt->rt_forw) {
6077c478bdstevel@tonic-gate				rtdumpentry(fp, rt);
6087c478bdstevel@tonic-gate			}
6097c478bdstevel@tonic-gate		}
6107c478bdstevel@tonic-gate	}
6117c478bdstevel@tonic-gate}
6127c478bdstevel@tonic-gate
6137c478bdstevel@tonic-gatevoid
6147c478bdstevel@tonic-gatertdump(void)
6157c478bdstevel@tonic-gate{
6167c478bdstevel@tonic-gate	if (ftrace != NULL)
6177c478bdstevel@tonic-gate		rtdump2(ftrace);
6187c478bdstevel@tonic-gate	else
6197c478bdstevel@tonic-gate		rtdump2(stderr);
6207c478bdstevel@tonic-gate}
6217c478bdstevel@tonic-gate
6227c478bdstevel@tonic-gate/*
6237c478bdstevel@tonic-gate * Create a routing socket for sending RTM_ADD and RTM_DELETE messages and
6247c478bdstevel@tonic-gate * initialize the routing socket message header and as much of the sockaddrs
6257c478bdstevel@tonic-gate * as possible.
6267c478bdstevel@tonic-gate */
6277c478bdstevel@tonic-gatevoid
6287c478bdstevel@tonic-gatesetup_rtsock(void)
6297c478bdstevel@tonic-gate{
6307c478bdstevel@tonic-gate	char *cp;
6317c478bdstevel@tonic-gate	int off = 0;
6327c478bdstevel@tonic-gate
6337c478bdstevel@tonic-gate	rtsock = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
6347c478bdstevel@tonic-gate	if (rtsock < 0) {
6357c478bdstevel@tonic-gate		syslog(LOG_ERR, "setup_rtsock: socket: %m");
6367c478bdstevel@tonic-gate		exit(EXIT_FAILURE);
6377c478bdstevel@tonic-gate	}
6387c478bdstevel@tonic-gate
6397c478bdstevel@tonic-gate	/* We don't want to listen to our own messages */
6407c478bdstevel@tonic-gate	if (setsockopt(rtsock, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
6417c478bdstevel@tonic-gate	    sizeof (off)) < 0) {
6427c478bdstevel@tonic-gate		syslog(LOG_ERR, "setup_rtsock: setsockopt: SO_USELOOPBACK: %m");
6437c478bdstevel@tonic-gate		exit(EXIT_FAILURE);
6447c478bdstevel@tonic-gate	}
6457c478bdstevel@tonic-gate
6467c478bdstevel@tonic-gate	/*
6477c478bdstevel@tonic-gate	 * Allocate storage for the routing socket message.
6487c478bdstevel@tonic-gate	 */
6497c478bdstevel@tonic-gate	rt_msg = (struct rt_msghdr *)malloc(RIPNG_RTM_MSGLEN);
6507c478bdstevel@tonic-gate	if (rt_msg == NULL) {
6517c478bdstevel@tonic-gate		syslog(LOG_ERR, "setup_rtsock: malloc: %m");
6527c478bdstevel@tonic-gate		exit(EXIT_FAILURE);
6537c478bdstevel@tonic-gate	}
6547c478bdstevel@tonic-gate
6557c478bdstevel@tonic-gate	/*
6567c478bdstevel@tonic-gate	 * Initialize the routing socket message by zero-filling it and then
6577c478bdstevel@tonic-gate	 * setting the fields where are constant through the lifetime of the
6587c478bdstevel@tonic-gate	 * process.
6597c478bdstevel@tonic-gate	 */
6607c478bdstevel@tonic-gate	bzero(rt_msg, RIPNG_RTM_MSGLEN);
6617c478bdstevel@tonic-gate	rt_msg->rtm_msglen = RIPNG_RTM_MSGLEN;
6627c478bdstevel@tonic-gate	rt_msg->rtm_version = RTM_VERSION;
6637c478bdstevel@tonic-gate	rt_msg->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP;
6647c478bdstevel@tonic-gate	rt_msg->rtm_pid = getpid();
6657c478bdstevel@tonic-gate	if (rt_msg->rtm_pid < 0) {
6667c478bdstevel@tonic-gate		syslog(LOG_ERR, "setup_rtsock: getpid: %m");
6677c478bdstevel@tonic-gate		exit(EXIT_FAILURE);
6687c478bdstevel@tonic-gate	}
6697c478bdstevel@tonic-gate
6707c478bdstevel@tonic-gate	/*
6717c478bdstevel@tonic-gate	 * Initialize the constant portion of the RTA_DST sockaddr.
6727c478bdstevel@tonic-gate	 */
6737c478bdstevel@tonic-gate	cp = (char *)rt_msg + sizeof (struct rt_msghdr);
6747c478bdstevel@tonic-gate	rta_dst = (struct sockaddr_in6 *)cp;
6757c478bdstevel@tonic-gate	rta_dst->sin6_family = AF_INET6;
6767c478bdstevel@tonic-gate
6777c478bdstevel@tonic-gate	/*
6787c478bdstevel@tonic-gate	 * Initialize the constant portion of the RTA_GATEWAY sockaddr.
6797c478bdstevel@tonic-gate	 */
6807c478bdstevel@tonic-gate	cp += sizeof (struct sockaddr_in6);
6817c478bdstevel@tonic-gate	rta_gateway = (struct sockaddr_in6 *)cp;
6827c478bdstevel@tonic-gate	rta_gateway->sin6_family = AF_INET6;
6837c478bdstevel@tonic-gate
6847c478bdstevel@tonic-gate	/*
6857c478bdstevel@tonic-gate	 * Initialize the constant portion of the RTA_NETMASK sockaddr.
6867c478bdstevel@tonic-gate	 */
6877c478bdstevel@tonic-gate	cp += sizeof (struct sockaddr_in6);
6887c478bdstevel@tonic-gate	rta_netmask = (struct sockaddr_in6 *)cp;
6897c478bdstevel@tonic-gate	rta_netmask->sin6_family = AF_INET6;
6907c478bdstevel@tonic-gate
6917c478bdstevel@tonic-gate	/*
6927c478bdstevel@tonic-gate	 * Initialize the constant portion of the RTA_IFP sockaddr.
6937c478bdstevel@tonic-gate	 */
6947c478bdstevel@tonic-gate	cp += sizeof (struct sockaddr_in6);
6957c478bdstevel@tonic-gate	rta_ifp = (struct sockaddr_dl *)cp;
6967c478bdstevel@tonic-gate	rta_ifp->sdl_family = AF_LINK;
6977c478bdstevel@tonic-gate}
698