1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30/*
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
33 */
34
35#pragma ident	"%Z%%M%	%I%	%E% SMI"
36
37/*
38 * Routing Table Management Daemon
39 */
40#include "defs.h"
41
42boolean_t	install = _B_TRUE;	/* update kernel routing table */
43struct rthash	*net_hashes[IPV6_ABITS + 1];
44
45/*
46 * Size of routing socket message used by in.ripngd which includes the header,
47 * space for the RTA_DST, RTA_GATEWAY and RTA_NETMASK (each a sockaddr_in6)
48 * plus space for the RTA_IFP (a sockaddr_dl).
49 */
50#define	RIPNG_RTM_MSGLEN	sizeof (struct rt_msghdr) +	\
51				sizeof (struct sockaddr_in6) +	\
52				sizeof (struct sockaddr_in6) +	\
53				sizeof (struct sockaddr_in6) +	\
54				sizeof (struct sockaddr_dl)
55
56static int	rtmseq;				/* rtm_seq sequence number */
57static int	rtsock;				/* Routing socket */
58static struct	rt_msghdr	*rt_msg;	/* Routing socket message */
59static struct	sockaddr_in6	*rta_dst;	/* RTA_DST sockaddr */
60static struct	sockaddr_in6	*rta_gateway;	/* RTA_GATEWAY sockaddr */
61static struct	sockaddr_in6	*rta_netmask;	/* RTA_NETMASK sockaddr */
62static struct	sockaddr_dl	*rta_ifp;	/* RTA_IFP sockaddr */
63
64/* simulate vax insque and remque instructions. */
65
66typedef struct vq {
67	caddr_t	 fwd, back;
68} vq_t;
69
70#define	insque(e, p)	((vq_t *)(e))->back = (caddr_t)(p); \
71			((vq_t *)(e))->fwd = \
72				(caddr_t)((vq_t *)((vq_t *)(p))->fwd); \
73			((vq_t *)((vq_t *)(p))->fwd)->back = (caddr_t)(e); \
74			((vq_t *)(p))->fwd = (caddr_t)(e);
75
76#define	remque(e)	((vq_t *)((vq_t *)(e))->back)->fwd =  \
77				(caddr_t)((vq_t *)(e))->fwd; \
78			((vq_t *)((vq_t *)(e))->fwd)->back = \
79				(caddr_t)((vq_t *)(e))->back; \
80			((vq_t *)(e))->fwd = NULL; \
81			((vq_t *)(e))->back = NULL;
82
83static void
84log_change(int level, struct rt_entry *orig, struct rt_entry *new)
85{
86	char buf1[INET6_ADDRSTRLEN];
87	char buf2[INET6_ADDRSTRLEN];
88	char buf3[INET6_ADDRSTRLEN];
89
90	(void) inet_ntop(AF_INET6, (void *) &new->rt_dst, buf1, sizeof (buf1));
91	(void) inet_ntop(AF_INET6, (void *) &orig->rt_router, buf2,
92	    sizeof (buf2));
93	(void) inet_ntop(AF_INET6, (void *) &new->rt_router, buf3,
94	    sizeof (buf3));
95
96	syslog(level, "\tdst %s from gw %s if %s to gw %s if %s metric %d",
97	    buf1, buf2,
98	    (orig->rt_ifp != NULL && orig->rt_ifp->int_name != NULL) ?
99		orig->rt_ifp->int_name : "(noname)",
100	    buf3,
101	    (new->rt_ifp != NULL && new->rt_ifp->int_name != NULL) ?
102		new->rt_ifp->int_name : "(noname)", new->rt_metric);
103}
104
105static void
106log_single(int level, struct rt_entry *rt)
107{
108	char buf1[INET6_ADDRSTRLEN];
109	char buf2[INET6_ADDRSTRLEN];
110
111	(void) inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1));
112	(void) inet_ntop(AF_INET6, (void *)&rt->rt_router, buf2, sizeof (buf2));
113
114	syslog(level, "\tdst %s gw %s if %s metric %d",
115	    buf1, buf2,
116	    (rt->rt_ifp != NULL && rt->rt_ifp->int_name != NULL) ?
117		rt->rt_ifp->int_name : "(noname)",
118	    rt->rt_metric);
119}
120
121/*
122 * Computes a hash by XOR-ing the (up to sixteen) octets that make up an IPv6
123 * address.  This function assumes that that there are no one-bits in the
124 * address beyond the prefix length.
125 */
126static uint8_t
127rthash(struct in6_addr *dst, int prefix_length)
128{
129	uint8_t val = 0;
130	int i;
131
132	for (i = 0; prefix_length > 0; prefix_length -= 8, i++)
133		val ^= dst->s6_addr[i];
134	return (val);
135}
136
137/*
138 * Given a prefix length, fill in the struct in6_addr representing an IPv6
139 * netmask.
140 */
141static void
142rtmask_to_bits(uint_t prefix_length, struct in6_addr *prefix)
143{
144	uint_t mask = 0xff;
145	int i;
146
147	bzero((caddr_t)prefix, sizeof (struct in6_addr));
148	for (i = 0; prefix_length >= 8; prefix_length -= 8, i++)
149		prefix->s6_addr[i] = 0xff;
150	mask = (mask << (8 - prefix_length));
151	if (mask != 0)
152		prefix->s6_addr[i] = mask;
153}
154
155void
156rtcreate_prefix(struct in6_addr *p1, struct in6_addr *dst, int bits)
157{
158	uchar_t mask;
159	int j;
160
161	for (j = 0; bits >= 8; bits -= 8, j++)
162		dst->s6_addr[j] = p1->s6_addr[j];
163
164	if (bits != 0) {
165		mask = 0xff << (8 - bits);
166		dst->s6_addr[j] = p1->s6_addr[j] & mask;
167		j++;
168	}
169
170	for (; j < 16; j++)
171		dst->s6_addr[j] = 0;
172}
173
174/*
175 * Lookup dst in the tables for an exact match.
176 */
177struct rt_entry *
178rtlookup(struct in6_addr *dst, int prefix_length)
179{
180	struct rt_entry *rt;
181	struct rthash *rh;
182	uint_t	hash;
183
184	if (net_hashes[prefix_length] == NULL)
185		return (NULL);
186
187	hash = rthash(dst, prefix_length);
188
189	rh = &net_hashes[prefix_length][hash & ROUTEHASHMASK];
190
191	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
192		if (rt->rt_hash != hash)
193			continue;
194		if (IN6_ARE_ADDR_EQUAL(&rt->rt_dst, dst) &&
195		    rt->rt_prefix_length == prefix_length)
196			return (rt);
197	}
198	return (NULL);
199}
200
201/*
202 * Given an IPv6 prefix (destination and prefix length), a gateway, an
203 * interface name and route flags, send down the requested command returning
204 * the return value and errno (in the case of error) from the write() on the
205 * routing socket.
206 */
207static int
208rtcmd(uchar_t type, struct in6_addr *dst, struct in6_addr *gateway,
209    uint_t prefix_length, char *name, int flags)
210{
211	int rlen;
212
213	rta_ifp->sdl_index = if_nametoindex(name);
214	if (rta_ifp->sdl_index == 0)
215		return (-1);
216
217	rta_dst->sin6_addr = *dst;
218	rta_gateway->sin6_addr = *gateway;
219	rtmask_to_bits(prefix_length, &rta_netmask->sin6_addr);
220
221	rt_msg->rtm_type = type;
222	rt_msg->rtm_flags = flags;
223	rt_msg->rtm_seq = ++rtmseq;
224	rlen = write(rtsock, rt_msg, RIPNG_RTM_MSGLEN);
225	if (rlen >= 0 && rlen < RIPNG_RTM_MSGLEN) {
226		syslog(LOG_ERR,
227		    "rtcmd: write to routing socket got only %d for rlen\n",
228		    rlen);
229	}
230	return (rlen);
231}
232
233void
234rtadd(struct in6_addr *dst, struct in6_addr *gate, int prefix_length,
235    int metric, int tag, boolean_t ifroute, struct interface *ifp)
236{
237	struct rt_entry *rt;
238	struct rthash *rh;
239	uint_t hash;
240	struct in6_addr pdst;
241	int rlen;
242
243	if (metric >= HOPCNT_INFINITY)
244		return;
245
246	if (net_hashes[prefix_length] == NULL) {
247		struct rthash *trh;
248
249		rh = (struct rthash *)
250		    calloc(ROUTEHASHSIZ, sizeof (struct rt_entry));
251		if (rh == NULL)
252			return;
253		for (trh = rh; trh < &rh[ROUTEHASHSIZ]; trh++)
254			trh->rt_forw = trh->rt_back = (struct rt_entry *)trh;
255		net_hashes[prefix_length] = rh;
256	}
257	rtcreate_prefix(dst, &pdst, prefix_length);
258
259	hash = rthash(&pdst, prefix_length);
260	rh = &net_hashes[prefix_length][hash & ROUTEHASHMASK];
261	rt = (struct rt_entry *)malloc(sizeof (*rt));
262	if (rt == NULL) {
263		/*
264		 * In the event of an allocation failure, log the error and
265		 * continue since on the next update another attempt will be
266		 * made.
267		 */
268		syslog(LOG_ERR, "rtadd: malloc: %m");
269		return;
270	}
271	rt->rt_hash = hash;
272	rt->rt_dst = pdst;
273	rt->rt_prefix_length = prefix_length;
274	rt->rt_router = *gate;
275	rt->rt_metric = metric;
276	rt->rt_tag = tag;
277	rt->rt_timer = 0;
278	rt->rt_flags = RTF_UP;
279	if (prefix_length == IPV6_ABITS)
280		rt->rt_flags |= RTF_HOST;
281	rt->rt_state = RTS_CHANGED;
282	if (ifroute) {
283		rt->rt_state |= RTS_INTERFACE;
284		if (ifp->int_flags & RIP6_IFF_PRIVATE)
285			rt->rt_state |= RTS_PRIVATE;
286	} else {
287		rt->rt_flags |= RTF_GATEWAY;
288	}
289	rt->rt_ifp = ifp;
290
291	insque(rt, rh);
292	TRACE_ACTION("ADD", rt);
293	/*
294	 * If the RTM_ADD fails because the gateway is unreachable
295	 * from this host, discard the entry.  This should never
296	 * happen.
297	 */
298	if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
299		rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
300		    prefix_length, ifp->int_name, rt->rt_flags);
301		if (rlen < 0) {
302			if (errno != EEXIST) {
303				syslog(LOG_ERR, "rtadd: RTM_ADD: %m");
304				log_single(LOG_ERR, rt);
305			}
306			if (errno == ENETUNREACH) {
307				TRACE_ACTION("DELETE", rt);
308				remque(rt);
309				free((char *)rt);
310			}
311		} else if (rlen < RIPNG_RTM_MSGLEN) {
312			log_single(LOG_ERR, rt);
313		}
314	}
315}
316
317/*
318 * Handle the case when the metric changes but the gateway is the same (or the
319 * interface index associated with the gateway changes), or when both gateway
320 * and metric changes, or when only the gateway changes but the existing route
321 * is more than one-half of EXPIRE_TIME in age. Note that routes with metric >=
322 * HOPCNT_INFINITY are not in the kernel.
323 */
324void
325rtchange(struct rt_entry *rt, struct in6_addr *gate, short metric,
326    struct interface *ifp)
327{
328	boolean_t dokern = _B_FALSE;
329	boolean_t dokerndelete;
330	boolean_t metricchanged = _B_FALSE;
331	int oldmetric;
332	struct rt_entry oldroute;
333	int rlen;
334
335	if (metric >= HOPCNT_INFINITY) {
336		rtdown(rt);
337		return;
338	}
339
340	if (!IN6_ARE_ADDR_EQUAL(&rt->rt_router, gate) || rt->rt_ifp != ifp)
341		dokern = _B_TRUE;
342	oldmetric = rt->rt_metric;
343	if (oldmetric >= HOPCNT_INFINITY)
344		dokerndelete = _B_FALSE;
345	else
346		dokerndelete = dokern;
347	if (metric != rt->rt_metric)
348		metricchanged = _B_TRUE;
349	rt->rt_timer = 0;
350	if (dokern || metricchanged) {
351		TRACE_ACTION("CHANGE FROM", rt);
352		if ((rt->rt_state & RTS_INTERFACE) && metric != 0) {
353			rt->rt_state &= ~RTS_INTERFACE;
354			if (rt->rt_ifp != NULL) {
355				syslog(LOG_ERR,
356				    "rtchange: changing route from "
357				    "interface %s (timed out)",
358				    (rt->rt_ifp->int_name != NULL) ?
359					rt->rt_ifp->int_name : "(noname)");
360			} else {
361				syslog(LOG_ERR,
362				    "rtchange: "
363				    "changing route no interface for route");
364			}
365		}
366		if (dokern) {
367			oldroute = *rt;
368			rt->rt_router = *gate;
369			rt->rt_ifp = ifp;
370		}
371		rt->rt_metric = metric;
372		if (!(rt->rt_state & RTS_INTERFACE))
373			rt->rt_flags |= RTF_GATEWAY;
374		else
375			rt->rt_flags &= ~RTF_GATEWAY;
376		rt->rt_state |= RTS_CHANGED;
377		TRACE_ACTION("CHANGE TO", rt);
378	}
379	if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
380		if (dokerndelete) {
381			rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
382			    rt->rt_prefix_length, rt->rt_ifp->int_name,
383			    rt->rt_flags);
384			if (rlen < 0) {
385				if (errno != EEXIST) {
386					syslog(LOG_ERR,
387					    "rtchange: RTM_ADD: %m");
388					log_change(LOG_ERR, rt,
389					    (struct rt_entry *)&oldroute);
390				}
391			} else if (rlen < RIPNG_RTM_MSGLEN) {
392				log_change(LOG_ERR, rt,
393				    (struct rt_entry *)&oldroute);
394			}
395
396			rlen = rtcmd(RTM_DELETE, &oldroute.rt_dst,
397			    &oldroute.rt_router, oldroute.rt_prefix_length,
398			    oldroute.rt_ifp->int_name, oldroute.rt_flags);
399			if (rlen < 0) {
400				syslog(LOG_ERR, "rtchange: RTM_DELETE: %m");
401				log_change(LOG_ERR, rt,
402				    (struct rt_entry *)&oldroute);
403			} else if (rlen < RIPNG_RTM_MSGLEN) {
404				log_change(LOG_ERR, rt,
405				    (struct rt_entry *)&oldroute);
406			}
407		} else if (dokern || oldmetric >= HOPCNT_INFINITY) {
408			rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
409			    rt->rt_prefix_length, ifp->int_name, rt->rt_flags);
410			if (rlen < 0 && errno != EEXIST) {
411				syslog(LOG_ERR, "rtchange: RTM_ADD: %m");
412				log_change(LOG_ERR, rt,
413				    (struct rt_entry *)&oldroute);
414			} else if (rlen < RIPNG_RTM_MSGLEN) {
415				log_change(LOG_ERR, rt,
416				    (struct rt_entry *)&oldroute);
417			}
418		}
419	}
420}
421
422void
423rtdown(struct rt_entry *rt)
424{
425	int rlen;
426
427	if (rt->rt_metric != HOPCNT_INFINITY) {
428		TRACE_ACTION("DELETE", rt);
429		if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
430			rlen = rtcmd(RTM_DELETE, &rt->rt_dst,
431			    &rt->rt_router, rt->rt_prefix_length,
432			    rt->rt_ifp->int_name, rt->rt_flags);
433			if (rlen < 0) {
434				syslog(LOG_ERR, "rtdown: RTM_DELETE: %m");
435				log_single(LOG_ERR, rt);
436			} else if (rlen < RIPNG_RTM_MSGLEN) {
437				log_single(LOG_ERR, rt);
438			}
439		}
440		rt->rt_metric = HOPCNT_INFINITY;
441		rt->rt_state |= RTS_CHANGED;
442	}
443	if (rt->rt_timer < EXPIRE_TIME)
444		rt->rt_timer = EXPIRE_TIME;
445}
446
447void
448rtdelete(struct rt_entry *rt)
449{
450
451	if (rt->rt_state & RTS_INTERFACE) {
452		if (rt->rt_ifp != NULL) {
453			syslog(LOG_ERR,
454			    "rtdelete: "
455			    "deleting route to interface %s (timed out)",
456			    (rt->rt_ifp->int_name != NULL) ?
457				rt->rt_ifp->int_name : "(noname)");
458			log_single(LOG_ERR, rt);
459		}
460	}
461	rtdown(rt);
462	remque(rt);
463	free((char *)rt);
464}
465
466/*
467 * Mark all the routes heard off a particular interface "down".  Unlike the
468 * routes managed by in.routed, all of these routes have an interface associated
469 * with them.
470 */
471void
472rtpurgeif(struct interface *ifp)
473{
474	struct rthash *rh;
475	struct rt_entry *rt;
476	int i;
477
478	for (i = IPV6_ABITS; i >= 0; i--) {
479		if (net_hashes[i] == NULL)
480			continue;
481
482		for (rh = net_hashes[i];
483		    rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
484			for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
485			    rt = rt->rt_forw) {
486				if (rt->rt_ifp == ifp) {
487					rtdown(rt);
488					rt->rt_ifp = NULL;
489					rt->rt_state &= ~RTS_INTERFACE;
490				}
491			}
492		}
493	}
494}
495
496/*
497 * Called when the subnetmask has changed on one or more interfaces.
498 * Re-evaluates all non-interface routes by doing a rtchange so that
499 * routes that were believed to be host routes before the netmask change
500 * can be converted to network routes and vice versa.
501 */
502void
503rtchangeall(void)
504{
505	struct rthash *rh;
506	struct rt_entry *rt;
507	int i;
508
509	for (i = IPV6_ABITS; i >= 0; i--) {
510		if (net_hashes[i] == NULL)
511			continue;
512
513		for (rh = net_hashes[i];
514		    rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
515			for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
516			    rt = rt->rt_forw) {
517				if ((rt->rt_state & RTS_INTERFACE) == 0) {
518					rtchange(rt, &rt->rt_router,
519					    rt->rt_metric, rt->rt_ifp);
520				}
521			}
522		}
523	}
524}
525
526static void
527rtdumpentry(FILE *fp, struct rt_entry *rt)
528{
529	char buf1[INET6_ADDRSTRLEN];
530	static struct bits {
531		ulong_t	t_bits;
532		char	*t_name;
533	} flagbits[] = {
534		/* BEGIN CSTYLED */
535		{ RTF_UP,		"UP" },
536		{ RTF_GATEWAY,		"GATEWAY" },
537		{ RTF_HOST,		"HOST" },
538		{ 0,			NULL }
539		/* END CSTYLED */
540	}, statebits[] = {
541		/* BEGIN CSTYLED */
542		{ RTS_INTERFACE,	"INTERFACE" },
543		{ RTS_CHANGED,		"CHANGED" },
544		{ RTS_PRIVATE,		"PRIVATE" },
545		{ 0,			NULL }
546		/* END CSTYLED */
547	};
548	struct bits *p;
549	boolean_t first;
550	char c;
551
552	(void) fprintf(fp, "prefix %s/%d ",
553	    inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1)),
554	    rt->rt_prefix_length);
555	(void) fprintf(fp, "via %s metric %d timer %d",
556	    inet_ntop(AF_INET6, (void *)&rt->rt_router, buf1, sizeof (buf1)),
557	    rt->rt_metric, rt->rt_timer);
558	if (rt->rt_ifp != NULL) {
559		(void) fprintf(fp, " if %s",
560		    (rt->rt_ifp->int_name != NULL) ?
561			rt->rt_ifp->int_name : "(noname)");
562	}
563	(void) fprintf(fp, " state");
564	c = ' ';
565	for (first = _B_TRUE, p = statebits; p->t_bits > 0; p++) {
566		if ((rt->rt_state & p->t_bits) == 0)
567			continue;
568		(void) fprintf(fp, "%c%s", c, p->t_name);
569		if (first) {
570			c = '|';
571			first = _B_FALSE;
572		}
573	}
574	if (first)
575		(void) fprintf(fp, " 0");
576	if (rt->rt_flags & (RTF_UP | RTF_GATEWAY)) {
577		c = ' ';
578		for (first = _B_TRUE, p = flagbits; p->t_bits > 0; p++) {
579			if ((rt->rt_flags & p->t_bits) == 0)
580				continue;
581			(void) fprintf(fp, "%c%s", c, p->t_name);
582			if (first) {
583				c = '|';
584				first = _B_FALSE;
585			}
586		}
587	}
588	(void) putc('\n', fp);
589	(void) fflush(fp);
590}
591
592static void
593rtdump2(FILE *fp)
594{
595	struct rthash *rh;
596	struct rt_entry *rt;
597	int i;
598
599	for (i = IPV6_ABITS; i >= 0; i--) {
600		if (net_hashes[i] == NULL)
601			continue;
602
603		for (rh = net_hashes[i];
604		    rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
605			for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
606			    rt = rt->rt_forw) {
607				rtdumpentry(fp, rt);
608			}
609		}
610	}
611}
612
613void
614rtdump(void)
615{
616	if (ftrace != NULL)
617		rtdump2(ftrace);
618	else
619		rtdump2(stderr);
620}
621
622/*
623 * Create a routing socket for sending RTM_ADD and RTM_DELETE messages and
624 * initialize the routing socket message header and as much of the sockaddrs
625 * as possible.
626 */
627void
628setup_rtsock(void)
629{
630	char *cp;
631	int off = 0;
632
633	rtsock = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
634	if (rtsock < 0) {
635		syslog(LOG_ERR, "setup_rtsock: socket: %m");
636		exit(EXIT_FAILURE);
637	}
638
639	/* We don't want to listen to our own messages */
640	if (setsockopt(rtsock, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
641	    sizeof (off)) < 0) {
642		syslog(LOG_ERR, "setup_rtsock: setsockopt: SO_USELOOPBACK: %m");
643		exit(EXIT_FAILURE);
644	}
645
646	/*
647	 * Allocate storage for the routing socket message.
648	 */
649	rt_msg = (struct rt_msghdr *)malloc(RIPNG_RTM_MSGLEN);
650	if (rt_msg == NULL) {
651		syslog(LOG_ERR, "setup_rtsock: malloc: %m");
652		exit(EXIT_FAILURE);
653	}
654
655	/*
656	 * Initialize the routing socket message by zero-filling it and then
657	 * setting the fields where are constant through the lifetime of the
658	 * process.
659	 */
660	bzero(rt_msg, RIPNG_RTM_MSGLEN);
661	rt_msg->rtm_msglen = RIPNG_RTM_MSGLEN;
662	rt_msg->rtm_version = RTM_VERSION;
663	rt_msg->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP;
664	rt_msg->rtm_pid = getpid();
665	if (rt_msg->rtm_pid < 0) {
666		syslog(LOG_ERR, "setup_rtsock: getpid: %m");
667		exit(EXIT_FAILURE);
668	}
669
670	/*
671	 * Initialize the constant portion of the RTA_DST sockaddr.
672	 */
673	cp = (char *)rt_msg + sizeof (struct rt_msghdr);
674	rta_dst = (struct sockaddr_in6 *)cp;
675	rta_dst->sin6_family = AF_INET6;
676
677	/*
678	 * Initialize the constant portion of the RTA_GATEWAY sockaddr.
679	 */
680	cp += sizeof (struct sockaddr_in6);
681	rta_gateway = (struct sockaddr_in6 *)cp;
682	rta_gateway->sin6_family = AF_INET6;
683
684	/*
685	 * Initialize the constant portion of the RTA_NETMASK sockaddr.
686	 */
687	cp += sizeof (struct sockaddr_in6);
688	rta_netmask = (struct sockaddr_in6 *)cp;
689	rta_netmask->sin6_family = AF_INET6;
690
691	/*
692	 * Initialize the constant portion of the RTA_IFP sockaddr.
693	 */
694	cp += sizeof (struct sockaddr_in6);
695	rta_ifp = (struct sockaddr_dl *)cp;
696	rta_ifp->sdl_family = AF_LINK;
697}
698