xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/in.routed/trace.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  *
5  * Copyright (c) 1983, 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgment:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * $FreeBSD: src/sbin/routed/trace.c,v 1.6 2000/08/11 08:24:38 sheldonh Exp $
37  */
38 
39 #include "defs.h"
40 #include "pathnames.h"
41 #include <signal.h>
42 #include <sys/stat.h>
43 #include <sys/signal.h>
44 #include <strings.h>
45 #include <fcntl.h>
46 #include <protocols/routed.h>
47 
48 #define	NRECORDS	50		/* size of circular trace buffer */
49 
50 int	tracelevel, new_tracelevel;
51 FILE	*ftrace = stdout;		/* output trace file */
52 static const char *sigtrace_pat = "%s";
53 static char savetracename[MAXPATHLEN+1];
54 static char *ripcmds[RIPCMD_MAX] =
55 	{"#0", "REQUEST", "RESPONSE", "TRACEON", "TRACEOFF", "POLL",
56 	"POLLENTRY"};
57 char	inittracename[MAXPATHLEN+1];
58 static boolean_t file_trace;	/* 1=tracing to file, not stdout */
59 
60 static void tmsg(const char *, ...);
61 
62 const char *
63 rip_strerror(int err)
64 {
65 	const char *cp = strerror(err);
66 	static char msgbuf[64];
67 
68 	if (cp == NULL) {
69 		if (err == 0) {
70 			cp = "success";
71 		} else {
72 			(void) snprintf(msgbuf, sizeof (msgbuf),
73 			    "unknown error %d", err);
74 			cp = msgbuf;
75 		}
76 	}
77 	return (cp);
78 }
79 
80 /* convert IP address to a string, but not into a single buffer */
81 char *
82 naddr_ntoa(in_addr_t a)
83 {
84 #define	NUM_BUFS 4
85 	static int bufno;
86 	static struct {
87 	    char    str[INET_ADDRSTRLEN];	/* xxx.xxx.xxx.xxx\0 */
88 	} bufs[NUM_BUFS];
89 	char *s;
90 	struct in_addr addr;
91 
92 	addr.s_addr = a;
93 	s = strcpy(bufs[bufno].str, inet_ntoa(addr));
94 	bufno = (bufno+1) % NUM_BUFS;
95 	return (s);
96 #undef NUM_BUFS
97 }
98 
99 
100 const char *
101 saddr_ntoa(struct sockaddr_storage *ss)
102 {
103 	return (ss == NULL) ? "?" : naddr_ntoa(S_ADDR(ss));
104 }
105 
106 
107 static char *
108 ts(time_t secs)
109 {
110 	static char s[20];
111 
112 	secs += epoch.tv_sec;
113 	(void) strftime(s, sizeof (s), "%T", localtime(&secs));
114 	return (s);
115 }
116 
117 static char *
118 ts_full(struct timeval *tv)
119 {
120 	static char s[32];
121 	time_t secs;
122 	int len;
123 
124 	secs = tv->tv_sec + epoch.tv_sec;
125 	(void) strftime(s, sizeof (s), "%Y/%m/%d %T", localtime(&secs));
126 	len = strlen(s);
127 	(void) snprintf(s + len, sizeof (s) - len, ".%06ld", tv->tv_usec);
128 	return (s);
129 }
130 
131 /*
132  * On each event, display a time stamp.
133  * This assumes that 'now' is update once for each event, and
134  * that at least now.tv_usec changes.
135  */
136 static struct timeval lastlog_time;
137 
138 void
139 lastlog(void)
140 {
141 	if (lastlog_time.tv_sec != now.tv_sec ||
142 	    lastlog_time.tv_usec != now.tv_usec) {
143 		(void) fprintf(ftrace, "-- %s --\n", ts_full(&now));
144 		lastlog_time = now;
145 	}
146 }
147 
148 
149 static void
150 tmsg(const char *p, ...)
151 {
152 	va_list args;
153 
154 	if (ftrace != NULL) {
155 		lastlog();
156 		va_start(args, p);
157 		(void) vfprintf(ftrace, p, args);
158 		(void) fputc('\n', ftrace);
159 		(void) fflush(ftrace);
160 		(void) va_end(args);
161 	}
162 }
163 
164 
165 void
166 trace_close(int zap_stdio)
167 {
168 	int fd;
169 
170 
171 	(void) fflush(stdout);
172 	(void) fflush(stderr);
173 
174 	if (ftrace != NULL && zap_stdio) {
175 		if (ftrace != stdout)
176 			(void) fclose(ftrace);
177 		ftrace = NULL;
178 		fd = open("/dev/null", O_RDWR);
179 		if (isatty(STDIN_FILENO))
180 			(void) dup2(fd, STDIN_FILENO);
181 		if (isatty(STDOUT_FILENO))
182 			(void) dup2(fd, STDOUT_FILENO);
183 		if (isatty(STDERR_FILENO))
184 			(void) dup2(fd, STDERR_FILENO);
185 		(void) close(fd);
186 	}
187 	lastlog_time.tv_sec = 0;
188 }
189 
190 
191 void
192 trace_flush(void)
193 {
194 	if (ftrace != NULL) {
195 		(void) fflush(ftrace);
196 		if (ferror(ftrace))
197 			trace_off("tracing off: %s",
198 			    rip_strerror(ferror(ftrace)));
199 	}
200 }
201 
202 
203 void
204 trace_off(const char *p, ...)
205 {
206 	va_list args;
207 
208 
209 	if (ftrace != NULL) {
210 		lastlog();
211 		va_start(args, p);
212 		(void) vfprintf(ftrace, p, args);
213 		(void) fputc('\n', ftrace);
214 		(void) va_end(args);
215 	}
216 	trace_close(file_trace);
217 
218 	new_tracelevel = tracelevel = 0;
219 }
220 
221 
222 /* log a change in tracing */
223 void
224 tracelevel_msg(const char *pat,
225     int dump)		/* -1=no dump, 0=default, 1=force */
226 {
227 	static const char *off_msgs[MAX_TRACELEVEL] = {
228 		"Tracing actions stopped",
229 		"Tracing packets stopped",
230 		"Tracing packet contents stopped",
231 		"Tracing kernel changes stopped",
232 		"Tracing routing socket messages stopped",
233 	};
234 	static const char *on_msgs[MAX_TRACELEVEL] = {
235 		"Tracing actions started",
236 		"Tracing packets started",
237 		"Tracing packet contents started",
238 		"Tracing kernel changes started",
239 		"Tracing routing socket messages started",
240 	};
241 	uint_t old_tracelevel = tracelevel;
242 
243 
244 	if (new_tracelevel < 0)
245 		new_tracelevel = 0;
246 	else if (new_tracelevel > MAX_TRACELEVEL)
247 		new_tracelevel = MAX_TRACELEVEL;
248 
249 	if (new_tracelevel < tracelevel) {
250 		if (new_tracelevel <= 0) {
251 			trace_off(pat, off_msgs[0]);
252 		} else {
253 			do {
254 				tmsg(pat, off_msgs[tracelevel]);
255 			} while (--tracelevel != new_tracelevel);
256 		}
257 
258 	} else if (new_tracelevel > tracelevel) {
259 		do {
260 			tmsg(pat, on_msgs[tracelevel++]);
261 		} while (tracelevel != new_tracelevel);
262 	}
263 
264 	if (dump > 0 ||
265 	    (dump == 0 && old_tracelevel == 0 && tracelevel != 0))
266 		trace_dump();
267 }
268 
269 void
270 set_tracefile(const char *filename,
271     const char *pat,
272     int dump)			/* -1=no dump, 0=default, 1=force */
273 {
274 	struct stat stbuf;
275 	struct stat stbuf2;
276 	FILE *n_ftrace;
277 	const char *fn;
278 	int nfd;
279 	boolean_t allow_create;
280 
281 	/*
282 	 * main() calls this routine with "dump == -1".  All others
283 	 * call it with 0, so we take dump == -1 to mean "can create
284 	 * the file."
285 	 */
286 	allow_create = (dump == -1);
287 
288 	/*
289 	 * Allow a null filename to increase the level if the trace file
290 	 * is already open or if coming from a trusted source, such as
291 	 * a signal or the command line.
292 	 */
293 	if (filename == NULL || filename[0] == '\0') {
294 		filename = NULL;
295 		if (ftrace == NULL) {
296 			if (inittracename[0] == '\0') {
297 				msglog("missing trace file name");
298 				return;
299 			}
300 			fn = inittracename;
301 		} else {
302 			goto set_tracelevel;
303 		}
304 
305 	} else if (strcmp(filename, "dump/../table") == 0) {
306 		trace_dump();
307 		return;
308 
309 	} else {
310 		/*
311 		 * Allow the file specified with "-T file" to be reopened,
312 		 * but require all other names specified over the net to
313 		 * match the official path.  The path can specify a directory
314 		 * in which the file is to be created.
315 		 */
316 
317 		if (strcmp(filename, inittracename) != 0) {
318 			if (strncmp(filename, PATH_TRACE,
319 			    sizeof (PATH_TRACE)-1) != 0 ||
320 			    (strstr(filename, "../") != NULL)) {
321 				msglog("wrong trace file \"%s\"", filename);
322 				return;
323 			}
324 			if (stat(PATH_TRACE, &stbuf) == -1) {
325 				fn = PATH_TRACE;
326 				goto missing_file;
327 			}
328 			if (filename[sizeof (PATH_TRACE) - 1] != '\0' &&
329 			    (filename[sizeof (PATH_TRACE) - 1] != '/' ||
330 			    !S_ISDIR(stbuf.st_mode))) {
331 				goto bad_file_type;
332 			}
333 			if (S_ISDIR(stbuf.st_mode))
334 				allow_create = _B_TRUE;
335 		}
336 
337 		fn = filename;
338 	}
339 	/* fn cannot be null here */
340 
341 	/* If the new tracefile exists, it must be a regular file. */
342 	if (lstat(fn, &stbuf) == -1) {
343 		if (!allow_create)
344 			goto missing_file;
345 		nfd = open(fn, O_CREAT|O_EXCL|O_WRONLY, 0644);
346 		if (nfd != -1 && fstat(nfd, &stbuf) == -1) {
347 			(void) close(nfd);
348 			goto missing_file;
349 		}
350 	} else if (S_ISREG(stbuf.st_mode)) {
351 		nfd = open(fn, O_APPEND|O_WRONLY, 0644);
352 	} else {
353 		goto bad_file_type;
354 	}
355 
356 	if (nfd == -1 || (n_ftrace = fdopen(nfd, "a")) == NULL) {
357 		msglog("failed to open trace file \"%s\" %s", fn,
358 		    rip_strerror(errno));
359 		if (fn == inittracename)
360 			inittracename[0] = '\0';
361 		if (nfd != -1)
362 			(void) close(nfd);
363 		return;
364 	}
365 
366 	if (fstat(nfd, &stbuf2) == -1 || !S_ISREG(stbuf2.st_mode) ||
367 	    stbuf2.st_dev != stbuf.st_dev || stbuf2.st_ino != stbuf.st_ino) {
368 		msglog("trace file \"%s\" moved", fn);
369 		(void) fclose(n_ftrace);
370 		return;
371 	}
372 
373 	tmsg("switch to trace file %s", fn);
374 	trace_close(file_trace = _B_TRUE);
375 	(void) dup2(nfd, STDOUT_FILENO);
376 	(void) dup2(nfd, STDERR_FILENO);
377 
378 	if (fn != savetracename)
379 		(void) strlcpy(savetracename, fn, sizeof (savetracename) - 1);
380 	ftrace = n_ftrace;
381 
382 set_tracelevel:
383 	if (new_tracelevel == 0 || filename == NULL)
384 		new_tracelevel++;
385 	tracelevel_msg(pat, dump != 0 ? dump : (filename != NULL));
386 	return;
387 
388 missing_file:
389 	msglog("trace \"%s\" missing", fn);
390 	return;
391 
392 bad_file_type:
393 	msglog("wrong type (%#x) of trace file \"%s\"", stbuf.st_mode, fn);
394 }
395 
396 
397 /* ARGSUSED */
398 void
399 sigtrace_more(int s)
400 {
401 	new_tracelevel++;
402 	sigtrace_pat = "SIGUSR1: %s";
403 	if (signal(s, sigtrace_more) == SIG_ERR)
404 		msglog("signal: %s", rip_strerror(errno));
405 }
406 
407 
408 /* ARGSUSED */
409 void
410 sigtrace_less(int s)
411 {
412 	new_tracelevel--;
413 	sigtrace_pat = "SIGUSR2: %s";
414 	if (signal(s, sigtrace_less) == SIG_ERR)
415 		msglog("signal: %s", rip_strerror(errno));
416 }
417 
418 /* ARGSUSED */
419 void
420 sigtrace_dump(int s)
421 {
422 	trace_dump();
423 	if (signal(s, sigtrace_dump) == SIG_ERR)
424 		msglog("signal: %s", rip_strerror(errno));
425 }
426 
427 /* Set tracing after a signal. */
428 void
429 set_tracelevel(void)
430 {
431 	if (new_tracelevel == tracelevel)
432 		return;
433 
434 	/*
435 	 * If tracing entirely off, and there was no tracefile specified
436 	 * on the command line, then leave it off.
437 	 */
438 	if (new_tracelevel > tracelevel && ftrace == NULL) {
439 		if (savetracename[0] != '\0') {
440 			set_tracefile(savetracename, sigtrace_pat, 0);
441 		} else if (inittracename[0] != '\0') {
442 			set_tracefile(inittracename, sigtrace_pat, 0);
443 		} else {
444 			new_tracelevel = 0;
445 			return;
446 		}
447 	} else {
448 		tracelevel_msg(sigtrace_pat, 0);
449 	}
450 }
451 
452 
453 /* display an address */
454 char *
455 addrname(in_addr_t addr,	/* in network byte order */
456     in_addr_t	mask,
457     int	force)			/* 0=show mask if nonstandard, */
458 {					/*	1=always show mask, 2=never */
459 #define	NUM_BUFS 4
460 	static int bufno;
461 	static struct {
462 	/*
463 	 * this array can hold either of the following strings terminated
464 	 * by a null character:
465 	 * "xxx.xxx.xxx.xxx/xx"
466 	 * "xxx.xxx.xxx.xxx (mask xxx.xxx.xxx.xxx)"
467 	 *
468 	 */
469 	    char    str[2*INET_ADDRSTRLEN + sizeof (" (mask )")];
470 	} bufs[NUM_BUFS];
471 	char *s, *sp;
472 	in_addr_t dmask;
473 	int i, len;
474 	struct in_addr tmp_addr;
475 
476 	tmp_addr.s_addr = addr;
477 	len = strlcpy(bufs[bufno].str, inet_ntoa(tmp_addr),
478 	    sizeof (bufs[bufno].str));
479 	s = bufs[bufno].str;
480 	bufno = (bufno+1) % NUM_BUFS;
481 
482 	if (force == 1 || (force == 0 && mask != std_mask(addr))) {
483 		sp = &s[strlen(s)];
484 
485 		dmask = mask & -mask;
486 		if (mask + dmask == 0) {
487 			i = ffs(mask);
488 			(void) snprintf(sp,
489 			    (sizeof (bufs[bufno].str) - len), "/%d",
490 			    (NBBY * sizeof (in_addr_t) + 1) - i);
491 
492 		} else {
493 			(void) snprintf(sp,
494 			    (sizeof (bufs[bufno].str) - len), " (mask %s)",
495 			    naddr_ntoa(htonl(mask)));
496 		}
497 	}
498 
499 	return (s);
500 #undef NUM_BUFS
501 }
502 
503 
504 /* display a bit-field */
505 struct or_bits {
506 	uint8_t	origin;
507 	const char *origin_name;
508 };
509 
510 static struct or_bits origin_bits[] = {
511 	{ RO_RIP,		"RIP" },
512 	{ RO_RDISC,		"RDISC" },
513 	{ RO_STATIC,		"STATIC" },
514 	{ RO_LOOPBCK,		"LOOPBCK" },
515 	{ RO_PTOPT,		"PTOPT" },
516 	{ RO_NET_SYN,		"NET_SYN" },
517 	{ RO_IF,		"IF" },
518 	{ RO_FILE,		"FILE" },
519 	{ RO_NONE,		"     " },
520 	{ 0,			NULL}
521 };
522 
523 /* display a bit-field */
524 struct bits {
525 	uint64_t	bits_mask;
526 	uint64_t	bits_clear;
527 	const char	*bits_name;
528 };
529 
530 static struct bits if_bits[] = {
531 	{ IFF_BROADCAST,	0,		"BROADCAST" },
532 	{ IFF_DEBUG,		0,		"DEBUG" },
533 	{ IFF_LOOPBACK,		0,		"LOOPBACK" },
534 	{ IFF_POINTOPOINT,	0,		"POINTOPOINT" },
535 	{ IFF_NOTRAILERS,	0,		"NOTRAILERS" },
536 	{ IFF_RUNNING,		0,		"RUNNING" },
537 	{ IFF_NOARP,		0,		"NOARP" },
538 	{ IFF_PROMISC,		0,		"PROMISC" },
539 	{ IFF_ALLMULTI,		0,		"ALLMULTI" },
540 	{ IFF_INTELLIGENT,	0,		"INTELLIGENT" },
541 	{ IFF_MULTICAST,	0,		"MULTICAST" },
542 	{ IFF_MULTI_BCAST,	0,		"MULTI_BCAST" },
543 	{ IFF_UNNUMBERED,	0,		"UNNUMBERED" },
544 	{ IFF_DHCPRUNNING,	0,		"DHCP" },
545 	{ IFF_PRIVATE,		0,		"PRIVATE" },
546 	{ IFF_NOXMIT,		0,		"NOXMIT" },
547 	{ IFF_NOLOCAL,		0,		"NOLOCAL" },
548 	{ IFF_DEPRECATED,	0,		"DEPRECATED" },
549 	{ IFF_ADDRCONF,		0,		"ADDRCONF" },
550 	{ IFF_ROUTER,		0,		"ROUTER" },
551 	{ IFF_NONUD,		0,		"NONUD" },
552 	{ IFF_ANYCAST,		0,		"ANYCAST" },
553 	{ IFF_NORTEXCH,		0,		"NORTEXCH" },
554 	{ IFF_IPV4,		0,		"IPv4" },
555 	{ IFF_IPV6,		0,		"IPv6" },
556 	{ IFF_NOFAILOVER,	0,		"NOFAILOVER" },
557 	{ IFF_FAILED,		0,		"FAILED" },
558 	{ IFF_STANDBY,		0,		"STANDBY" },
559 	{ IFF_INACTIVE,		0,		"INACTIVE" },
560 	{ IFF_OFFLINE,		0,		"OFFLINE" },
561 	{ IFF_XRESOLV,		0,		"XRESOLV" },
562 	{ IFF_COS_ENABLED,	0,		"CoS" },
563 	{ IFF_PREFERRED,	0,		"PREFERRED" },
564 	{ IFF_TEMPORARY,	0,		"TEMPORARY" },
565 	{ IFF_FIXEDMTU,		0,		"FIXEDMTU" },
566 	{ IFF_VIRTUAL,		0,		"VIRTUAL"},
567 	{ IFF_IPMP,		0,		"IPMP"},
568 	{ 0,			0,		NULL}
569 };
570 
571 static struct bits is_bits[] = {
572 	{ IS_ALIAS,		0,		"ALIAS" },
573 	{ IS_SUBNET,		0,		"" },
574 	{ IS_REMOTE,		(IS_NO_RDISC |
575 				IS_BCAST_RDISC), "REMOTE" },
576 	{ IS_PASSIVE,		(IS_NO_RDISC |
577 				IS_NO_RIP |
578 				IS_NO_SUPER_AG |
579 				IS_PM_RDISC |
580 				IS_NO_AG),	"PASSIVE" },
581 	{ IS_EXTERNAL,		0,		"EXTERNAL" },
582 	{ IS_CHECKED,		0,		"" },
583 	{ IS_ALL_HOSTS,		0,		"" },
584 	{ IS_ALL_ROUTERS,	0,		"" },
585 	{ IS_DISTRUST,		0,		"DISTRUST" },
586 	{ IS_BROKE,		IS_SICK,	"BROKEN" },
587 	{ IS_SICK,		0,		"SICK" },
588 	{ IS_DUP,		0,		"DUPLICATE" },
589 	{ IS_REDIRECT_OK,	0,		"REDIRECT_OK" },
590 	{ IS_NEED_NET_SYN,	0,		"" },
591 	{ IS_NO_AG,		IS_NO_SUPER_AG,	"NO_AG" },
592 	{ IS_NO_SUPER_AG,	0,		"NO_SUPER_AG" },
593 	{ (IS_NO_RIPV1_IN |
594 	    IS_NO_RIPV2_IN |
595 	    IS_NO_RIPV1_OUT |
596 	    IS_NO_RIPV2_OUT),	0,		"NO_RIP" },
597 	{ (IS_NO_RIPV1_IN |
598 	    IS_NO_RIPV1_OUT),	0,		"RIPV2" },
599 	{ IS_NO_RIPV1_IN,	0,		"NO_RIPV1_IN" },
600 	{ IS_NO_RIPV2_IN,	0,		"NO_RIPV2_IN" },
601 	{ IS_NO_RIPV1_OUT,	0,		"NO_RIPV1_OUT" },
602 	{ IS_NO_RIPV2_OUT,	0,		"NO_RIPV2_OUT" },
603 	{ IS_NO_RIP_MCAST,	0,		"NO_RIP_MCAST" },
604 	{ (IS_NO_ADV_IN |
605 	    IS_NO_SOL_OUT |
606 	    IS_NO_ADV_OUT),	IS_BCAST_RDISC,	"NO_RDISC" },
607 	{ IS_NO_SOL_OUT,	0,		"NO_SOLICIT" },
608 	{ IS_SOL_OUT,		0,		"SEND_SOLICIT" },
609 	{ IS_NO_ADV_OUT,	IS_BCAST_RDISC,	"NO_RDISC_ADV" },
610 	{ IS_ADV_OUT,		0,		"RDISC_ADV" },
611 	{ IS_BCAST_RDISC,	0,		"BCAST_RDISC" },
612 	{ IS_PM_RDISC,		0,		"" },
613 	{ IS_NO_HOST,		0,		"NO_HOST" },
614 	{ IS_SUPPRESS_RDISC,	0,		"SUPPRESS_RDISC" },
615 	{ IS_FLUSH_RDISC,	0,		"FLUSH_RDISC" },
616 	{ 0,			0,		NULL}
617 };
618 
619 static struct bits rs_bits[] = {
620 	{ RS_IF,		0,		"IF" },
621 	{ RS_NET_INT,		RS_NET_SYN,	"NET_INT" },
622 	{ RS_NET_SYN,		0,		"NET_SYN" },
623 	{ RS_SUBNET,		0,		"" },
624 	{ RS_LOCAL,		0,		"LOCAL" },
625 	{ RS_MHOME,		0,		"MHOME" },
626 	{ RS_STATIC,		0,		"STATIC" },
627 	{ RS_NOPROPAGATE,	0,		"NOPROP" },
628 	{ RS_BADIF,		0,		"BADIF" },
629 	{ 0,			0,		NULL}
630 };
631 
632 static struct bits ks_bits[] = {
633 	{ KS_NEW,	0,		"NEW" },
634 	{ KS_DELETE,	0,		"DELETE" },
635 	{ KS_ADD,	0,		"ADD" },
636 	{ KS_CHANGE,	0,		"CHANGE" },
637 	{ KS_DEL_ADD,	0,		"DEL_ADD" },
638 	{ KS_STATIC,	0,		"STATIC" },
639 	{ KS_GATEWAY,	0,		"GATEWAY" },
640 	{ KS_DYNAMIC,	0,		"DYNAMIC" },
641 	{ KS_DELETED,	0,		"DELETED" },
642 	{ KS_PRIVATE,	0,		"PRIVATE" },
643 	{ KS_CHECK,	0,		"CHECK" },
644 	{ KS_IF,	0,		"IF" },
645 	{ KS_PASSIVE,	0,		"PASSIVE" },
646 	{ KS_DEPRE_IF,	0,		"DEPRE_IF" },
647 	{ KS_FILE,	0,		"FILE" },
648 	{ 0,		0,		NULL}
649 };
650 
651 static void
652 trace_bits(const struct bits *tbl,
653     uint64_t field,
654     boolean_t force)
655 {
656 	uint64_t b;
657 	char c;
658 
659 	if (force) {
660 		(void) putc('<', ftrace);
661 		c = '\0';
662 	} else {
663 		c = '<';
664 	}
665 
666 	while (field != 0 &&
667 	    (b = tbl->bits_mask) != 0) {
668 		if ((b & field) == b) {
669 			if (tbl->bits_name[0] != '\0') {
670 				if (c != '\0')
671 					(void) putc(c, ftrace);
672 				(void) fprintf(ftrace, "%s", tbl->bits_name);
673 				c = '|';
674 			}
675 			field &= ~(b | tbl->bits_clear);
676 		}
677 		tbl++;
678 	}
679 	if (field != 0) {
680 		if (c != '\0')
681 			(void) putc(c, ftrace);
682 		(void) fprintf(ftrace, "%#llx", field);
683 		c = '|';
684 	}
685 
686 	if (c != '<' || force)
687 		(void) fputs("> ", ftrace);
688 }
689 
690 static char *
691 trace_string(const struct bits *tbl, uint_t field, boolean_t force)
692 {
693 	const struct bits *tbp;
694 	char *sbuf, *cp, chr;
695 	size_t slen;
696 
697 	/* minimum default string */
698 	slen = sizeof ("<0x12345678>");
699 	for (tbp = tbl; tbp->bits_mask != 0; tbp++)
700 		if (tbp->bits_name[0] != '\0')
701 			slen += strlen(tbp->bits_name) + 1;
702 	if ((sbuf = malloc(slen)) == NULL)
703 		return (NULL);
704 	cp = sbuf;
705 
706 	if (force) {
707 		*cp++ = '<';
708 		chr = '\0';
709 	} else {
710 		chr = '<';
711 	}
712 
713 	while (field != 0 && tbl->bits_mask != 0) {
714 		if ((tbl->bits_mask & field) == tbl->bits_mask) {
715 			if (tbl->bits_name[0] != '\0') {
716 				if (chr != '\0')
717 					*cp++ = chr;
718 				(void) strcpy(cp, tbl->bits_name);
719 				cp += strlen(tbl->bits_name);
720 				chr = '|';
721 			}
722 			field &= ~(tbl->bits_mask | tbl->bits_clear);
723 		}
724 		tbl++;
725 	}
726 	if (field != 0) {
727 		if (chr != '\0')
728 			*cp++ = chr;
729 		cp += sprintf(cp, "%#x", field);
730 		chr = '|';
731 	}
732 
733 	if (chr != '<' || force)
734 		*cp++ = '>';
735 	*cp = '\0';
736 	return (sbuf);
737 }
738 
739 char *
740 if_bit_string(uint_t field, boolean_t force)
741 {
742 	return (trace_string(if_bits, field, force));
743 }
744 
745 char *
746 rtname(in_addr_t dst,
747     in_addr_t mask,
748     in_addr_t gate)
749 {
750 	static char buf[sizeof ("xxx.xxx.xxx.xxx/xx-->xxx.xxx.xxx.xxx")];
751 	int i;
752 
753 	(void) snprintf(buf, sizeof (buf), "%-16s-->", addrname(dst, mask, 0));
754 	i = strlen(buf);
755 	(void) snprintf(&buf[i], (sizeof (buf) -i), "%-*s", 15+24-MAX(24, i),
756 	    naddr_ntoa(gate));
757 	return (buf);
758 }
759 
760 
761 static void
762 print_rts(struct rt_spare *rts,
763     int force_metric,		/* -1=suppress, 0=default */
764     int force_ifp,		/* -1=suppress, 0=default */
765     int force_router,		/* -1=suppress, 0=default, 1=display */
766     int force_tag,		/* -1=suppress, 0=default, 1=display */
767     int force_time)		/* 0=suppress, 1=display */
768 {
769 	int i;
770 
771 	if (force_metric >= 0)
772 		(void) fprintf(ftrace, "metric=%-2d ", rts->rts_metric);
773 	if (force_ifp >= 0)
774 		(void) fprintf(ftrace, "%s ", (rts->rts_ifp == 0 ?
775 		    "if?" : rts->rts_ifp->int_name));
776 	if (force_router > 0 ||
777 	    (force_router == 0 && rts->rts_router != rts->rts_gate))
778 		(void) fprintf(ftrace, "router=%s ",
779 		    naddr_ntoa(rts->rts_router));
780 	if (force_time > 0)
781 		(void) fprintf(ftrace, "%s ", ts(rts->rts_time));
782 	if (force_tag > 0 ||
783 	    (force_tag == 0 && rts->rts_tag != 0))
784 		(void) fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag));
785 	if (rts->rts_de_ag != 0) {
786 		for (i = 1; (uint_t)(1 << i) <= rts->rts_de_ag; i++)
787 			continue;
788 		(void) fprintf(ftrace, "de_ag=%d ", i);
789 	}
790 	(void) fprintf(ftrace, "flags 0x%x ", rts->rts_flags);
791 
792 }
793 
794 
795 static void
796 print_rtsorigin(const struct or_bits *tbl, uint8_t route_origin)
797 {
798 
799 	uint8_t tblentry;
800 	while ((tblentry = tbl->origin) != 0) {
801 		if (tblentry == route_origin) {
802 			(void) fprintf(ftrace, "origin=%s ", tbl->origin_name);
803 		}
804 		tbl++;
805 	}
806 }
807 
808 
809 void
810 trace_if(const char *act, struct interface *ifp)
811 {
812 	if (!TRACEACTIONS || ftrace == NULL)
813 		return;
814 
815 	lastlog();
816 	(void) fprintf(ftrace, "%-3s interface %-4s #%-3d ", act,
817 	    ifp->int_name,
818 	    ifp->int_phys != NULL ? ifp->int_phys->phyi_index : 0);
819 	(void) fprintf(ftrace, "%-15s-->%-15s",
820 	    naddr_ntoa(ifp->int_addr),
821 	    addrname(((ifp->int_if_flags & IFF_POINTOPOINT) ?
822 	    ifp->int_dstaddr : htonl(ifp->int_net)),
823 	    ifp->int_mask, 1));
824 	if (ifp->int_metric != 0)
825 		(void) fprintf(ftrace, " metric=%d", ifp->int_metric);
826 	if (!IS_RIP_OUT_OFF(ifp->int_state) &&
827 	    ifp->int_d_metric != 0)
828 		(void) fprintf(ftrace, " fake_default=%d", ifp->int_d_metric);
829 	(void) fputs("\n    ", ftrace);
830 	trace_bits(if_bits, ifp->int_if_flags, _B_FALSE);
831 	trace_bits(is_bits, ifp->int_state, _B_FALSE);
832 	(void) fputc('\n', ftrace);
833 }
834 
835 void
836 trace_khash(const struct khash *krt)
837 {
838 	if (ftrace == NULL)
839 		return;
840 
841 	lastlog();
842 	(void) fprintf(ftrace, "  %-15s-->%-15s metric=%d ",
843 	    addrname(krt->k_dst, krt->k_mask, 0),
844 	    naddr_ntoa(krt->k_gate), krt->k_metric);
845 	if (krt->k_ifp != NULL)
846 		(void) fprintf(ftrace, "ifp %s ", krt->k_ifp->int_name);
847 	else
848 		(void) fprintf(ftrace, "ifp NULL ");
849 	(void) fprintf(ftrace, "%s ", ts(krt->k_keep));
850 	(void) fprintf(ftrace, "%s ", ts(krt->k_redirect_time));
851 	trace_bits(ks_bits, krt->k_state, _B_TRUE);
852 	(void) fputc('\n', ftrace);
853 }
854 
855 void
856 trace_dr(const struct dr *drp)
857 {
858 	if (ftrace == NULL)
859 		return;
860 
861 	lastlog();
862 	(void) fprintf(ftrace, "  %-4s %-15s %s ",
863 	    drp->dr_ifp != NULL ? drp->dr_ifp->int_name : "?",
864 	    naddr_ntoa(drp->dr_gate), ts(drp->dr_ts));
865 	(void) fprintf(ftrace, "%s %d %u\n", ts(drp->dr_life),
866 	    SIGN_PREF(drp->dr_recv_pref), drp->dr_pref);
867 }
868 
869 void
870 trace_upslot(struct rt_entry *rt,
871     struct rt_spare *rts,
872     struct rt_spare *new)
873 {
874 	if (!TRACEACTIONS || ftrace == NULL)
875 		return;
876 
877 	if (rts->rts_gate == new->rts_gate &&
878 	    rts->rts_router == new->rts_router &&
879 	    rts->rts_metric == new->rts_metric &&
880 	    rts->rts_tag == new->rts_tag &&
881 	    rts->rts_de_ag == new->rts_de_ag)
882 		return;
883 
884 	lastlog();
885 	if (new->rts_gate == 0) {
886 		(void) fprintf(ftrace, "Del #%d %-35s ",
887 		    (int)(rts - rt->rt_spares),
888 		    rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate));
889 		print_rts(rts, 0, 0, 0, 0,
890 		    (rts != rt->rt_spares ||
891 		    AGE_RT(rt->rt_state, rts->rts_origin, new->rts_ifp)));
892 
893 	} else if (rts->rts_gate != RIP_DEFAULT) {
894 		(void) fprintf(ftrace, "Chg #%d %-35s ",
895 		    (int)(rts - rt->rt_spares),
896 		    rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate));
897 		print_rts(rts, 0, 0,
898 		    rts->rts_gate != new->rts_gate,
899 		    rts->rts_tag != new->rts_tag,
900 		    rts != rt->rt_spares ||
901 		    AGE_RT(rt->rt_state, rts->rts_origin, rt->rt_ifp));
902 
903 		(void) fprintf(ftrace, "\n       %19s%-16s ", "",
904 		    (new->rts_gate != rts->rts_gate ?
905 		    naddr_ntoa(new->rts_gate) : ""));
906 		print_rts(new,
907 		    ((new->rts_metric == rts->rts_metric) ? -1 : 0),
908 		    ((new->rts_ifp == rts->rts_ifp) ? -1 : 0),
909 		    0,
910 		    rts->rts_tag != new->rts_tag,
911 		    (new->rts_time != rts->rts_time &&
912 		    (rts != rt->rt_spares ||
913 		    AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp))));
914 
915 	} else {
916 		(void) fprintf(ftrace, "Add #%d %-35s ",
917 		    (int)(rts - rt->rt_spares),
918 		    rtname(rt->rt_dst, rt->rt_mask, new->rts_gate));
919 		print_rts(new, 0, 0, 0, 0,
920 		    (rts != rt->rt_spares ||
921 		    AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp)));
922 	}
923 	(void) fputc('\n', ftrace);
924 }
925 
926 
927 /* miscellaneous message checked by the caller */
928 void
929 trace_misc(const char *p, ...)
930 {
931 	va_list args;
932 
933 	if (ftrace == NULL)
934 		return;
935 
936 	lastlog();
937 	va_start(args, p);
938 	(void) vfprintf(ftrace, p, args);
939 	(void) fputc('\n', ftrace);
940 	(void) va_end(args);
941 }
942 
943 
944 /* display a message if tracing actions */
945 void
946 trace_act(const char *p, ...)
947 {
948 	va_list args;
949 
950 	if (!TRACEACTIONS || ftrace == NULL)
951 		return;
952 
953 	lastlog();
954 	va_start(args, p);
955 	(void) vfprintf(ftrace, p, args);
956 	(void) fputc('\n', ftrace);
957 	(void) va_end(args);
958 }
959 
960 
961 /* display a message if tracing packets */
962 void
963 trace_pkt(const char *p, ...)
964 {
965 	va_list args;
966 
967 	if (!TRACEPACKETS || ftrace == NULL)
968 		return;
969 
970 	lastlog();
971 	va_start(args, p);
972 	(void) vfprintf(ftrace, p, args);
973 	(void) fputc('\n', ftrace);
974 	(void) va_end(args);
975 }
976 
977 
978 void
979 trace_change(struct rt_entry *rt,
980     uint16_t	state,
981     struct	rt_spare *new,
982     const char	*label)
983 {
984 	if (ftrace == NULL)
985 		return;
986 
987 	if (rt->rt_metric == new->rts_metric &&
988 	    rt->rt_gate == new->rts_gate &&
989 	    rt->rt_router == new->rts_router &&
990 	    rt->rt_state == state &&
991 	    rt->rt_tag == new->rts_tag &&
992 	    rt->rt_de_ag == new->rts_de_ag)
993 		return;
994 
995 	lastlog();
996 	(void) fprintf(ftrace, "%s %-35s ",
997 	    label,
998 	    rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate));
999 	print_rts(rt->rt_spares,
1000 	    0, 0, 0, 0, AGE_RT(rt->rt_state, rt->rt_spares->rts_origin,
1001 	    rt->rt_ifp));
1002 	print_rtsorigin(origin_bits, rt->rt_spares->rts_origin);
1003 	trace_bits(rs_bits, rt->rt_state, rt->rt_state != state);
1004 
1005 	(void) fprintf(ftrace, "\n%*s %19s%-16s ",
1006 	    strlen(label), "", "",
1007 	    (rt->rt_gate != new->rts_gate ?
1008 	    naddr_ntoa(new->rts_gate) : ""));
1009 	print_rts(new,
1010 	    ((new->rts_metric == rt->rt_metric) ? -1 : 0),
1011 	    ((new->rts_ifp == rt->rt_ifp) ? -1 : 0),
1012 	    0,
1013 	    rt->rt_tag != new->rts_tag,
1014 	    (rt->rt_time != new->rts_time &&
1015 	    AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp)));
1016 	if (rt->rt_state != state) {
1017 		print_rtsorigin(origin_bits, new->rts_origin);
1018 		trace_bits(rs_bits, state, _B_TRUE);
1019 	}
1020 	(void) fputc('\n', ftrace);
1021 }
1022 
1023 
1024 void
1025 trace_add_del(const char *action, struct rt_entry *rt)
1026 {
1027 	if (ftrace == NULL)
1028 		return;
1029 
1030 	lastlog();
1031 	(void) fprintf(ftrace, "%s    %-35s ",
1032 	    action,
1033 	    rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate));
1034 	print_rts(rt->rt_spares, 0, 0, 0, 0, AGE_RT(rt->rt_state,
1035 	    rt->rt_spares->rts_origin, rt->rt_ifp));
1036 	print_rtsorigin(origin_bits, rt->rt_spares->rts_origin);
1037 	trace_bits(rs_bits, rt->rt_state, _B_FALSE);
1038 	(void) fputc('\n', ftrace);
1039 }
1040 
1041 
1042 /* ARGSUSED */
1043 static int
1044 walk_trace(struct radix_node *rn,
1045     void *w)
1046 {
1047 #define	RT ((struct rt_entry *)rn)
1048 	struct rt_spare *rts;
1049 	int i;
1050 
1051 	(void) fprintf(ftrace, "  %-35s ",
1052 	    rtname(RT->rt_dst, RT->rt_mask, RT->rt_gate));
1053 	print_rts(&RT->rt_spares[0], 0, 0, 0, 0,
1054 	    AGE_RT(RT->rt_state, RT->rt_spares[0].rts_origin, RT->rt_ifp));
1055 	print_rtsorigin(origin_bits, RT->rt_spares[0].rts_origin);
1056 	trace_bits(rs_bits, RT->rt_state, _B_FALSE);
1057 	if (RT->rt_poison_time >= now_garbage &&
1058 	    RT->rt_poison_metric < RT->rt_metric)
1059 		(void) fprintf(ftrace, "pm=%d@%s",
1060 		    RT->rt_poison_metric, ts(RT->rt_poison_time));
1061 	(void) fprintf(ftrace, "%d spare slots", RT->rt_num_spares);
1062 
1063 	rts = &RT->rt_spares[1];
1064 	for (i = 1; i < RT->rt_num_spares; i++, rts++) {
1065 		if (rts->rts_gate != RIP_DEFAULT) {
1066 			(void) fprintf(ftrace, "\n    #%d%15s%-16s ",
1067 			    i, "", naddr_ntoa(rts->rts_gate));
1068 			print_rts(rts, 0, 0, 0, 0, 1);
1069 			print_rtsorigin(origin_bits, rts->rts_origin);
1070 		}
1071 	}
1072 	(void) fputc('\n', ftrace);
1073 
1074 	return (0);
1075 }
1076 
1077 
1078 void
1079 trace_dump(void)
1080 {
1081 	struct interface *ifp;
1082 
1083 	if (ftrace == NULL)
1084 		return;
1085 	lastlog();
1086 
1087 	/*
1088 	 * Warning: the rtquery.trace.* family of STC tests depend on
1089 	 * the log file format here.  If you need to change this next
1090 	 * message, make sure that you change the TRACE_DUMP variable
1091 	 * as well.
1092 	 */
1093 	(void) fputs("current daemon state:\n", ftrace);
1094 	for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next)
1095 		trace_if("", ifp);
1096 	(void) fputs("Routes:\n", ftrace);
1097 	(void) rn_walktree(rhead, walk_trace, NULL);
1098 	(void) fputs("Kernel routes:\n", ftrace);
1099 	kern_dump();
1100 	(void) fputs("Discovered routers:\n", ftrace);
1101 	rdisc_dump();
1102 }
1103 
1104 
1105 void
1106 trace_rip(const char *dir1, const char *dir2,
1107     struct sockaddr_in *who,
1108     struct interface *ifp,
1109     struct rip *msg,
1110     int size)			/* total size of message */
1111 {
1112 	struct netinfo *n, *lim;
1113 #define	NA ((struct netauth *)n)
1114 	int i, seen_route;
1115 	struct in_addr tmp_mask;
1116 
1117 	if (!TRACEPACKETS || ftrace == NULL)
1118 		return;
1119 
1120 	lastlog();
1121 	if (msg->rip_cmd >= RIPCMD_MAX || msg->rip_vers == 0) {
1122 		(void) fprintf(ftrace, "%s bad RIPv%d cmd=%d %s"
1123 		    " %s.%d size=%d\n",
1124 		    dir1, msg->rip_vers, msg->rip_cmd, dir2,
1125 		    naddr_ntoa(who->sin_addr.s_addr),
1126 		    ntohs(who->sin_port),
1127 		    size);
1128 		return;
1129 	}
1130 
1131 	(void) fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n",
1132 	    dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2,
1133 	    naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port),
1134 	    ifp ? " via " : "", ifp ? ifp->int_name : "");
1135 	if (!TRACECONTENTS)
1136 		return;
1137 
1138 	seen_route = 0;
1139 	switch (msg->rip_cmd) {
1140 	case RIPCMD_REQUEST:
1141 	case RIPCMD_RESPONSE:
1142 
1143 		n = msg->rip_nets;
1144 		tmp_mask.s_addr = n->n_mask;
1145 		lim = n + (size - 4) / sizeof (struct netinfo);
1146 		for (; n < lim; n++) {
1147 			if (!seen_route &&
1148 			    n->n_family == RIP_AF_UNSPEC &&
1149 			    ntohl(n->n_metric) == HOPCNT_INFINITY &&
1150 			    msg->rip_cmd == RIPCMD_REQUEST &&
1151 			    (n+1 == lim ||
1152 			    (n+2 == lim &&
1153 			    (n+1)->n_family == RIP_AF_AUTH))) {
1154 				(void) fputs("\tQUERY ", ftrace);
1155 				if (n->n_dst != 0)
1156 					(void) fprintf(ftrace, "%s ",
1157 					    naddr_ntoa(n->n_dst));
1158 				if (n->n_mask != 0)
1159 					(void) fprintf(ftrace, "mask=%s ",
1160 					    inet_ntoa(tmp_mask));
1161 				if (n->n_nhop != 0)
1162 					(void) fprintf(ftrace, "nhop=%s ",
1163 					    naddr_ntoa(n->n_nhop));
1164 				if (n->n_tag != 0)
1165 					(void) fprintf(ftrace, "tag=%#x ",
1166 					    ntohs(n->n_tag));
1167 				(void) fputc('\n', ftrace);
1168 				continue;
1169 			}
1170 
1171 			if (n->n_family == RIP_AF_AUTH) {
1172 				if (NA->a_type == RIP_AUTH_PW &&
1173 				    n == msg->rip_nets) {
1174 					(void) fprintf(ftrace, "\tPassword"
1175 					    " Authentication: \"%s\"\n",
1176 					    qstring(NA->au.au_pw,
1177 					    RIP_AUTH_PW_LEN));
1178 					continue;
1179 				}
1180 
1181 				if (NA->a_type == RIP_AUTH_MD5 &&
1182 				    n == msg->rip_nets) {
1183 					(void) fprintf(ftrace,
1184 					    "\tMD5 Auth"
1185 					    " pkt_len=%d KeyID=%u"
1186 					    " auth_len=%d"
1187 					    " seqno=%#x"
1188 					    " rsvd=%#hx,%#hx\n",
1189 					    ntohs(NA->au.a_md5.md5_pkt_len),
1190 					    NA->au.a_md5.md5_keyid,
1191 					    NA->au.a_md5.md5_auth_len,
1192 					    ntohl(NA->au.a_md5.md5_seqno),
1193 					    ntohs(NA->au.a_md5.rsvd[0]),
1194 					    ntohs(NA->au.a_md5.rsvd[1]));
1195 					continue;
1196 				}
1197 				(void) fprintf(ftrace,
1198 				    "\tAuthentication type %d: ",
1199 				    ntohs(NA->a_type));
1200 				for (i = 0; i < (int)sizeof (NA->au.au_pw);
1201 				    i++)
1202 					(void) fprintf(ftrace, "%02x ",
1203 					    NA->au.au_pw[i]);
1204 				(void) fputc('\n', ftrace);
1205 				continue;
1206 			}
1207 
1208 			seen_route = 1;
1209 			if (n->n_family != RIP_AF_INET) {
1210 				(void) fprintf(ftrace,
1211 				    "\t(af %d) %-18s mask=%s ",
1212 				    ntohs(n->n_family),
1213 				    naddr_ntoa(n->n_dst),
1214 				    inet_ntoa(tmp_mask));
1215 			} else if (msg->rip_vers == RIPv1) {
1216 				(void) fprintf(ftrace, "\t%-18s ",
1217 				    addrname(n->n_dst, ntohl(n->n_mask),
1218 				    n->n_mask == 0 ? 2 : 1));
1219 			} else {
1220 				(void) fprintf(ftrace, "\t%-18s ",
1221 				    addrname(n->n_dst, ntohl(n->n_mask),
1222 				    n->n_mask == 0 ? 2 : 0));
1223 			}
1224 			(void) fprintf(ftrace, "metric=%-2lu ",
1225 			    (unsigned long)ntohl(n->n_metric));
1226 			if (n->n_nhop != 0)
1227 				(void) fprintf(ftrace, " nhop=%s ",
1228 				    naddr_ntoa(n->n_nhop));
1229 			if (n->n_tag != 0)
1230 				(void) fprintf(ftrace, "tag=%#x",
1231 				    ntohs(n->n_tag));
1232 			(void) fputc('\n', ftrace);
1233 		}
1234 		if (size != (char *)n - (char *)msg)
1235 			(void) fprintf(ftrace, "truncated record, len %d\n",
1236 			    size);
1237 		break;
1238 
1239 	case RIPCMD_TRACEON:
1240 		(void) fprintf(ftrace, "\tfile=\"%.*s\"\n", size - 4,
1241 		    msg->rip_tracefile);
1242 		break;
1243 
1244 	case RIPCMD_TRACEOFF:
1245 		break;
1246 	}
1247 }
1248