xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/tables.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 
42 boolean_t	install = _B_TRUE;	/* update kernel routing table */
43 struct 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 
56 static int	rtmseq;				/* rtm_seq sequence number */
57 static int	rtsock;				/* Routing socket */
58 static struct	rt_msghdr	*rt_msg;	/* Routing socket message */
59 static struct	sockaddr_in6	*rta_dst;	/* RTA_DST sockaddr */
60 static struct	sockaddr_in6	*rta_gateway;	/* RTA_GATEWAY sockaddr */
61 static struct	sockaddr_in6	*rta_netmask;	/* RTA_NETMASK sockaddr */
62 static struct	sockaddr_dl	*rta_ifp;	/* RTA_IFP sockaddr */
63 
64 /* simulate vax insque and remque instructions. */
65 
66 typedef 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 
83 static void
84 log_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 
105 static void
106 log_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  */
126 static uint8_t
127 rthash(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  */
141 static void
142 rtmask_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 
155 void
156 rtcreate_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  */
177 struct rt_entry *
178 rtlookup(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  */
207 static int
208 rtcmd(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 
233 void
234 rtadd(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  */
324 void
325 rtchange(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 
422 void
423 rtdown(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 
447 void
448 rtdelete(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  */
471 void
472 rtpurgeif(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  */
502 void
503 rtchangeall(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 
526 static void
527 rtdumpentry(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 
592 static void
593 rtdump2(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 
613 void
614 rtdump(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  */
627 void
628 setup_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