Home | History | Annotate | Download | only in in.routed
      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