xref: /freebsd/sbin/routed/trace.c (revision 8ce574de3b680e0a08b4c58c2a949f26963828af)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 #define	RIPCMDS
35 #include "defs.h"
36 #include "pathnames.h"
37 #include <sys/stat.h>
38 #include <sys/signal.h>
39 #include <fcntl.h>
40 
41 #ifdef __NetBSD__
42 __RCSID("$NetBSD$");
43 #elif defined(__FreeBSD__)
44 __RCSID("$FreeBSD$");
45 #else
46 __RCSID("$Revision: 2.27 $");
47 #ident "$Revision: 2.27 $"
48 #endif
49 
50 
51 #ifdef sgi
52 /* use *stat64 for files on large file systems */
53 #define stat	stat64
54 #endif
55 
56 int	tracelevel, new_tracelevel;
57 FILE	*ftrace;			/* output trace file */
58 static const char *sigtrace_pat = "%s";
59 static char savetracename[PATH_MAX];
60 char	inittracename[PATH_MAX];
61 static int file_trace;			/* 1=tracing to file, not stdout */
62 
63 static void trace_dump(void);
64 static void tmsg(const char *, ...) PATTRIB(1,2);
65 
66 
67 /* convert string to printable characters
68  */
69 static char *
70 qstring(u_char *s, int len)
71 {
72 	static char buf[8*20+1];
73 	char *p;
74 	u_char *s2, c;
75 
76 
77 	for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) {
78 		c = *s++;
79 		if (c == '\0') {
80 			for (s2 = s+1; s2 < &s[len]; s2++) {
81 				if (*s2 != '\0')
82 					break;
83 			}
84 			if (s2 >= &s[len])
85 			    goto exit;
86 		}
87 
88 		if (c >= ' ' && c < 0x7f && c != '\\') {
89 			*p++ = c;
90 			continue;
91 		}
92 		*p++ = '\\';
93 		switch (c) {
94 		case '\\':
95 			*p++ = '\\';
96 			break;
97 		case '\n':
98 			*p++= 'n';
99 			break;
100 		case '\r':
101 			*p++= 'r';
102 			break;
103 		case '\t':
104 			*p++ = 't';
105 			break;
106 		case '\b':
107 			*p++ = 'b';
108 			break;
109 		default:
110 			p += sprintf(p,"%o",c);
111 			break;
112 		}
113 	}
114 exit:
115 	*p = '\0';
116 	return buf;
117 }
118 
119 
120 /* convert IP address to a string, but not into a single buffer
121  */
122 char *
123 naddr_ntoa(naddr a)
124 {
125 #define NUM_BUFS 4
126 	static int bufno;
127 	static struct {
128 	    char    str[16];		/* xxx.xxx.xxx.xxx\0 */
129 	} bufs[NUM_BUFS];
130 	char *s;
131 	struct in_addr addr;
132 
133 	addr.s_addr = a;
134 	s = strcpy(bufs[bufno].str, inet_ntoa(addr));
135 	bufno = (bufno+1) % NUM_BUFS;
136 	return s;
137 #undef NUM_BUFS
138 }
139 
140 
141 const char *
142 saddr_ntoa(struct sockaddr *sa)
143 {
144 	return (sa == NULL) ? "?" : naddr_ntoa(S_ADDR(sa));
145 }
146 
147 
148 static char *
149 ts(time_t secs) {
150 	static char s[20];
151 
152 	secs += epoch.tv_sec;
153 #ifdef sgi
154 	(void)cftime(s, "%T", &secs);
155 #else
156 	memcpy(s, ctime(&secs)+11, 8);
157 	s[8] = '\0';
158 #endif
159 	return s;
160 }
161 
162 
163 /* On each event, display a time stamp.
164  * This assumes that 'now' is update once for each event, and
165  * that at least now.tv_usec changes.
166  */
167 static struct timeval lastlog_time;
168 
169 void
170 lastlog(void)
171 {
172 	if (lastlog_time.tv_sec != now.tv_sec
173 	    || lastlog_time.tv_usec != now.tv_usec) {
174 		(void)fprintf(ftrace, "-- %s --\n", ts(now.tv_sec));
175 		lastlog_time = now;
176 	}
177 }
178 
179 
180 static void
181 tmsg(const char *p, ...)
182 {
183 	va_list args;
184 
185 	if (ftrace != NULL) {
186 		lastlog();
187 		va_start(args, p);
188 		vfprintf(ftrace, p, args);
189 		va_end(args);
190 		(void)fputc('\n',ftrace);
191 		fflush(ftrace);
192 	}
193 }
194 
195 
196 void
197 trace_close(int zap_stdio)
198 {
199 	int fd;
200 
201 
202 	fflush(stdout);
203 	fflush(stderr);
204 
205 	if (ftrace != NULL && zap_stdio) {
206 		if (ftrace != stdout)
207 			fclose(ftrace);
208 		ftrace = NULL;
209 		fd = open(_PATH_DEVNULL, O_RDWR);
210 		if (fd < 0)
211 			return;
212 		if (isatty(STDIN_FILENO))
213 			(void)dup2(fd, STDIN_FILENO);
214 		if (isatty(STDOUT_FILENO))
215 			(void)dup2(fd, STDOUT_FILENO);
216 		if (isatty(STDERR_FILENO))
217 			(void)dup2(fd, STDERR_FILENO);
218 		(void)close(fd);
219 	}
220 	lastlog_time.tv_sec = 0;
221 }
222 
223 
224 void
225 trace_flush(void)
226 {
227 	if (ftrace != NULL) {
228 		fflush(ftrace);
229 		if (ferror(ftrace))
230 			trace_off("tracing off: %s", strerror(ferror(ftrace)));
231 	}
232 }
233 
234 
235 void
236 trace_off(const char *p, ...)
237 {
238 	va_list args;
239 
240 
241 	if (ftrace != NULL) {
242 		lastlog();
243 		va_start(args, p);
244 		vfprintf(ftrace, p, args);
245 		va_end(args);
246 		(void)fputc('\n',ftrace);
247 	}
248 	trace_close(file_trace);
249 
250 	new_tracelevel = tracelevel = 0;
251 }
252 
253 
254 /* log a change in tracing
255  */
256 void
257 tracelevel_msg(const char *pat,
258 	       int dump)		/* -1=no dump, 0=default, 1=force */
259 {
260 	static const char * const off_msgs[MAX_TRACELEVEL] = {
261 		"Tracing actions stopped",
262 		"Tracing packets stopped",
263 		"Tracing packet contents stopped",
264 		"Tracing kernel changes stopped",
265 	};
266 	static const char * const on_msgs[MAX_TRACELEVEL] = {
267 		"Tracing actions started",
268 		"Tracing packets started",
269 		"Tracing packet contents started",
270 		"Tracing kernel changes started",
271 	};
272 	u_int old_tracelevel = tracelevel;
273 
274 
275 	if (new_tracelevel < 0)
276 		new_tracelevel = 0;
277 	else if (new_tracelevel > MAX_TRACELEVEL)
278 		new_tracelevel = MAX_TRACELEVEL;
279 
280 	if (new_tracelevel < tracelevel) {
281 		if (new_tracelevel <= 0) {
282 			trace_off(pat, off_msgs[0]);
283 		} else do {
284 			tmsg(pat, off_msgs[tracelevel]);
285 		}
286 		while (--tracelevel != new_tracelevel);
287 
288 	} else if (new_tracelevel > tracelevel) {
289 		do {
290 			tmsg(pat, on_msgs[tracelevel++]);
291 		} while (tracelevel != new_tracelevel);
292 	}
293 
294 	if (dump > 0
295 	    || (dump == 0 && old_tracelevel == 0 && tracelevel != 0))
296 		trace_dump();
297 }
298 
299 
300 void
301 set_tracefile(const char *filename,
302 	      const char *pat,
303 	      int dump)			/* -1=no dump, 0=default, 1=force */
304 {
305 	struct stat stbuf;
306 	FILE *n_ftrace;
307 	const char *fn;
308 
309 
310 	/* Allow a null filename to increase the level if the trace file
311 	 * is already open or if coming from a trusted source, such as
312 	 * a signal or the command line.
313 	 */
314 	if (filename == NULL || filename[0] == '\0') {
315 		filename = NULL;
316 		if (ftrace == NULL) {
317 			if (inittracename[0] == '\0') {
318 				msglog("missing trace file name");
319 				return;
320 			}
321 			fn = inittracename;
322 		} else {
323 			fn = NULL;
324 		}
325 
326 	} else if (!strcmp(filename,"dump/../table")) {
327 		trace_dump();
328 		return;
329 
330 	} else {
331 		/* Allow the file specified with "-T file" to be reopened,
332 		 * but require all other names specified over the net to
333 		 * match the official path.  The path can specify a directory
334 		 * in which the file is to be created.
335 		 */
336 		if (strcmp(filename, inittracename)
337 #ifdef _PATH_TRACE
338 		    && (strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1)
339 			|| strstr(filename,"../")
340 			|| 0 > stat(_PATH_TRACE, &stbuf))
341 #endif
342 		    ) {
343 			msglog("wrong trace file \"%s\"", filename);
344 			return;
345 		}
346 
347 		/* If the new tracefile exists, it must be a regular file.
348 		 */
349 		if (stat(filename, &stbuf) >= 0 && !S_ISREG(stbuf.st_mode)) {
350 			msglog("wrong type (%#x) of trace file \"%s\"",
351 			       stbuf.st_mode, filename);
352 			return;
353 		}
354 
355 		fn = filename;
356 	}
357 
358 	if (fn != NULL) {
359 		n_ftrace = fopen(fn, "a");
360 		if (n_ftrace == NULL) {
361 			msglog("failed to open trace file \"%s\" %s",
362 			       fn, strerror(errno));
363 			if (fn == inittracename)
364 				inittracename[0] = '\0';
365 			return;
366 		}
367 
368 		tmsg("switch to trace file %s", fn);
369 
370 		trace_close(file_trace = 1);
371 
372 		if (fn != savetracename)
373 			strncpy(savetracename, fn, sizeof(savetracename)-1);
374 		ftrace = n_ftrace;
375 
376 		fflush(stdout);
377 		fflush(stderr);
378 		dup2(fileno(ftrace), STDOUT_FILENO);
379 		dup2(fileno(ftrace), STDERR_FILENO);
380 	}
381 
382 	if (new_tracelevel == 0 || filename == NULL)
383 		new_tracelevel++;
384 	tracelevel_msg(pat, dump != 0 ? dump : (filename != NULL));
385 }
386 
387 
388 /* ARGSUSED */
389 void
390 sigtrace_on(int s UNUSED)
391 {
392 	new_tracelevel++;
393 	sigtrace_pat = "SIGUSR1: %s";
394 }
395 
396 
397 /* ARGSUSED */
398 void
399 sigtrace_off(int s UNUSED)
400 {
401 	new_tracelevel--;
402 	sigtrace_pat = "SIGUSR2: %s";
403 }
404 
405 
406 /* Set tracing after a signal.
407  */
408 void
409 set_tracelevel(void)
410 {
411 	if (new_tracelevel == tracelevel)
412 		return;
413 
414 	/* If tracing entirely off, and there was no tracefile specified
415 	 * on the command line, then leave it off.
416 	 */
417 	if (new_tracelevel > tracelevel && ftrace == NULL) {
418 		if (savetracename[0] != '\0') {
419 			set_tracefile(savetracename,sigtrace_pat,0);
420 		} else if (inittracename[0] != '\0') {
421 				set_tracefile(inittracename,sigtrace_pat,0);
422 		} else {
423 			new_tracelevel = 0;
424 			return;
425 		}
426 	} else {
427 		tracelevel_msg(sigtrace_pat, 0);
428 	}
429 }
430 
431 
432 /* display an address
433  */
434 char *
435 addrname(naddr	addr,			/* in network byte order */
436 	 naddr	mask,
437 	 int	force)			/* 0=show mask if nonstandard, */
438 {					/*	1=always show mask, 2=never */
439 #define NUM_BUFS 4
440 	static int bufno;
441 	static struct {
442 	    char    str[15+20];
443 	} bufs[NUM_BUFS];
444 	char *s, *sp;
445 	naddr dmask;
446 	size_t l;
447 	int i;
448 
449 	strlcpy(bufs[bufno].str, naddr_ntoa(addr), sizeof(bufs[bufno].str));
450 	s = bufs[bufno].str;
451 	l = sizeof(bufs[bufno].str);
452 	bufno = (bufno+1) % NUM_BUFS;
453 
454 	if (force == 1 || (force == 0 && mask != std_mask(addr))) {
455 		sp = &s[strlen(s)];
456 
457 		dmask = mask & -mask;
458 		if (mask + dmask == 0) {
459 			for (i = 0; i != 32 && ((1<<i) & mask) == 0; i++)
460 				continue;
461 			(void)snprintf(sp, s + l - sp, "/%d", 32-i);
462 
463 		} else {
464 			(void)snprintf(sp, s + l - sp, " (mask %#x)",
465 			    (u_int)mask);
466 		}
467 	}
468 
469 	return s;
470 #undef NUM_BUFS
471 }
472 
473 
474 /* display a bit-field
475  */
476 struct bits {
477 	u_int	bits_mask;
478 	u_int	bits_clear;
479 	const char *bits_name;
480 };
481 
482 static const struct bits if_bits[] = {
483 	{ IFF_LOOPBACK,		0,		"LOOPBACK" },
484 	{ IFF_POINTOPOINT,	0,		"PT-TO-PT" },
485 	{ 0,			0,		0}
486 };
487 
488 static const struct bits is_bits[] = {
489 	{ IS_ALIAS,		0,		"ALIAS" },
490 	{ IS_SUBNET,		0,		"" },
491 	{ IS_REMOTE,		(IS_NO_RDISC
492 				 | IS_BCAST_RDISC), "REMOTE" },
493 	{ IS_PASSIVE,		(IS_NO_RDISC
494 				 | IS_NO_RIP
495 				 | IS_NO_SUPER_AG
496 				 | IS_PM_RDISC
497 				 | IS_NO_AG),	"PASSIVE" },
498 	{ IS_EXTERNAL,		0,		"EXTERNAL" },
499 	{ IS_CHECKED,		0,		"" },
500 	{ IS_ALL_HOSTS,		0,		"" },
501 	{ IS_ALL_ROUTERS,	0,		"" },
502 	{ IS_DISTRUST,		0,		"DISTRUST" },
503 	{ IS_BROKE,		IS_SICK,	"BROKEN" },
504 	{ IS_SICK,		0,		"SICK" },
505 	{ IS_DUP,		0,		"DUPLICATE" },
506 	{ IS_REDIRECT_OK,	0,		"REDIRECT_OK" },
507 	{ IS_NEED_NET_SYN,	0,		"" },
508 	{ IS_NO_AG,		IS_NO_SUPER_AG,	"NO_AG" },
509 	{ IS_NO_SUPER_AG,	0,		"NO_SUPER_AG" },
510 	{ (IS_NO_RIPV1_IN
511 	   | IS_NO_RIPV2_IN
512 	   | IS_NO_RIPV1_OUT
513 	   | IS_NO_RIPV2_OUT),	0,		"NO_RIP" },
514 	{ (IS_NO_RIPV1_IN
515 	   | IS_NO_RIPV1_OUT),	0,		"RIPV2" },
516 	{ IS_NO_RIPV1_IN,	0,		"NO_RIPV1_IN" },
517 	{ IS_NO_RIPV2_IN,	0,		"NO_RIPV2_IN" },
518 	{ IS_NO_RIPV1_OUT,	0,		"NO_RIPV1_OUT" },
519 	{ IS_NO_RIPV2_OUT,	0,		"NO_RIPV2_OUT" },
520 	{ (IS_NO_ADV_IN
521 	   | IS_NO_SOL_OUT
522 	   | IS_NO_ADV_OUT),	IS_BCAST_RDISC,	"NO_RDISC" },
523 	{ IS_NO_SOL_OUT,	0,		"NO_SOLICIT" },
524 	{ IS_SOL_OUT,		0,		"SEND_SOLICIT" },
525 	{ IS_NO_ADV_OUT,	IS_BCAST_RDISC,	"NO_RDISC_ADV" },
526 	{ IS_ADV_OUT,		0,		"RDISC_ADV" },
527 	{ IS_BCAST_RDISC,	0,		"BCAST_RDISC" },
528 	{ IS_PM_RDISC,		0,		"" },
529 	{ 0,			0,		"%#x"}
530 };
531 
532 static const struct bits rs_bits[] = {
533 	{ RS_IF,		0,		"IF" },
534 	{ RS_NET_INT,		RS_NET_SYN,	"NET_INT" },
535 	{ RS_NET_SYN,		0,		"NET_SYN" },
536 	{ RS_SUBNET,		0,		"" },
537 	{ RS_LOCAL,		0,		"LOCAL" },
538 	{ RS_MHOME,		0,		"MHOME" },
539 	{ RS_STATIC,		0,		"STATIC" },
540 	{ RS_RDISC,		0,		"RDISC" },
541 	{ 0,			0,		"%#x"}
542 };
543 
544 
545 static void
546 trace_bits(const struct bits *tbl,
547 	   u_int field,
548 	   int force)
549 {
550 	u_int b;
551 	char c;
552 
553 	if (force) {
554 		(void)putc('<', ftrace);
555 		c = 0;
556 	} else {
557 		c = '<';
558 	}
559 
560 	while (field != 0
561 	       && (b = tbl->bits_mask) != 0) {
562 		if ((b & field) == b) {
563 			if (tbl->bits_name[0] != '\0') {
564 				if (c)
565 					(void)putc(c, ftrace);
566 				(void)fprintf(ftrace, "%s", tbl->bits_name);
567 				c = '|';
568 			}
569 			if (0 == (field &= ~(b | tbl->bits_clear)))
570 				break;
571 		}
572 		tbl++;
573 	}
574 	if (field != 0 && tbl->bits_name != NULL) {
575 		if (c)
576 			(void)putc(c, ftrace);
577 		(void)fprintf(ftrace, tbl->bits_name, field);
578 		c = '|';
579 	}
580 
581 	if (c != '<' || force)
582 		(void)fputs("> ", ftrace);
583 }
584 
585 
586 char *
587 rtname(naddr dst,
588        naddr mask,
589        naddr gate)
590 {
591 	static char buf[3*4+3+1+2+3	/* "xxx.xxx.xxx.xxx/xx-->" */
592 			+3*4+3+1];	/* "xxx.xxx.xxx.xxx" */
593 	int i;
594 
595 	i = sprintf(buf, "%-16s-->", addrname(dst, mask, 0));
596 	(void)sprintf(&buf[i], "%-*s", 15+20-MAX(20,i), naddr_ntoa(gate));
597 	return buf;
598 }
599 
600 
601 static void
602 print_rts(struct rt_spare *rts,
603 	  int force_metric,		/* -1=suppress, 0=default */
604 	  int force_ifp,		/* -1=suppress, 0=default */
605 	  int force_router,		/* -1=suppress, 0=default, 1=display */
606 	  int force_tag,		/* -1=suppress, 0=default, 1=display */
607 	  int force_time)		/* 0=suppress, 1=display */
608 {
609 	int i;
610 
611 
612 	if (force_metric >= 0)
613 		(void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric);
614 	if (force_ifp >= 0)
615 		(void)fprintf(ftrace, "%s ", (rts->rts_ifp == NULL ?
616 					      "if?" : rts->rts_ifp->int_name));
617 	if (force_router > 0
618 	    || (force_router == 0 && rts->rts_router != rts->rts_gate))
619 		(void)fprintf(ftrace, "router=%s ",
620 			      naddr_ntoa(rts->rts_router));
621 	if (force_time > 0)
622 		(void)fprintf(ftrace, "%s ", ts(rts->rts_time));
623 	if (force_tag > 0
624 	    || (force_tag == 0 && rts->rts_tag != 0))
625 		(void)fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag));
626 	if (rts->rts_de_ag != 0) {
627 		for (i = 1; (u_int)(1 << i) <= rts->rts_de_ag; i++)
628 			continue;
629 		(void)fprintf(ftrace, "de_ag=%d ", i);
630 	}
631 
632 }
633 
634 
635 void
636 trace_if(const char *act,
637 	 struct interface *ifp)
638 {
639 	if (!TRACEACTIONS || ftrace == NULL)
640 		return;
641 
642 	lastlog();
643 	(void)fprintf(ftrace, "%-3s interface %-4s ", act, ifp->int_name);
644 	(void)fprintf(ftrace, "%-15s-->%-15s ",
645 		      naddr_ntoa(ifp->int_addr),
646 		      addrname(((ifp->int_if_flags & IFF_POINTOPOINT)
647 				? ifp->int_dstaddr
648 				: htonl(ifp->int_net)),
649 			       ifp->int_mask, 1));
650 	if (ifp->int_metric != 0)
651 		(void)fprintf(ftrace, "metric=%d ", ifp->int_metric);
652 	if (ifp->int_adj_inmetric != 0)
653 		(void)fprintf(ftrace, "adj_inmetric=%u ",
654 			      ifp->int_adj_inmetric);
655 	if (ifp->int_adj_outmetric != 0)
656 		(void)fprintf(ftrace, "adj_outmetric=%u ",
657 			      ifp->int_adj_outmetric);
658 	if (!IS_RIP_OUT_OFF(ifp->int_state)
659 	    && ifp->int_d_metric != 0)
660 		(void)fprintf(ftrace, "fake_default=%u ", ifp->int_d_metric);
661 	trace_bits(if_bits, ifp->int_if_flags, 0);
662 	trace_bits(is_bits, ifp->int_state, 0);
663 	(void)fputc('\n',ftrace);
664 }
665 
666 
667 void
668 trace_upslot(struct rt_entry *rt,
669 	     struct rt_spare *rts,
670 	     struct rt_spare *new)
671 {
672 	if (!TRACEACTIONS || ftrace == NULL)
673 		return;
674 
675 	if (rts->rts_gate == new->rts_gate
676 	    && rts->rts_router == new->rts_router
677 	    && rts->rts_metric == new->rts_metric
678 	    && rts->rts_tag == new->rts_tag
679 	    && rts->rts_de_ag == new->rts_de_ag)
680 		return;
681 
682 	lastlog();
683 	if (new->rts_gate == 0) {
684 		(void)fprintf(ftrace, "Del #%d %-35s ",
685 			      (int)(rts - rt->rt_spares),
686 			      rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate));
687 		print_rts(rts, 0,0,0,0,
688 			  (rts != rt->rt_spares
689 			   || AGE_RT(rt->rt_state,new->rts_ifp)));
690 
691 	} else if (rts->rts_gate != RIP_DEFAULT) {
692 		(void)fprintf(ftrace, "Chg #%d %-35s ",
693 			      (int)(rts - rt->rt_spares),
694 			      rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate));
695 		print_rts(rts, 0,0,
696 			  rts->rts_gate != new->rts_gate,
697 			  rts->rts_tag != new->rts_tag,
698 			  rts != rt->rt_spares || AGE_RT(rt->rt_state,
699 							rt->rt_ifp));
700 
701 		(void)fprintf(ftrace, "\n       %19s%-16s ", "",
702 			      (new->rts_gate != rts->rts_gate
703 			       ? naddr_ntoa(new->rts_gate) : ""));
704 		print_rts(new,
705 			  -(new->rts_metric == rts->rts_metric),
706 			  -(new->rts_ifp == rts->rts_ifp),
707 			  0,
708 			  rts->rts_tag != new->rts_tag,
709 			  (new->rts_time != rts->rts_time
710 			   && (rts != rt->rt_spares
711 			       || AGE_RT(rt->rt_state, new->rts_ifp))));
712 
713 	} else {
714 		(void)fprintf(ftrace, "Add #%d %-35s ",
715 			      (int)(rts - rt->rt_spares),
716 			      rtname(rt->rt_dst, rt->rt_mask, new->rts_gate));
717 		print_rts(new, 0,0,0,0,
718 			  (rts != rt->rt_spares
719 			   || AGE_RT(rt->rt_state,new->rts_ifp)));
720 	}
721 	(void)fputc('\n',ftrace);
722 }
723 
724 
725 /* miscellaneous message checked by the caller
726  */
727 void
728 trace_misc(const char *p, ...)
729 {
730 	va_list args;
731 
732 	if (ftrace == NULL)
733 		return;
734 
735 	lastlog();
736 	va_start(args, p);
737 	vfprintf(ftrace, p, args);
738 	va_end(args);
739 	(void)fputc('\n',ftrace);
740 }
741 
742 
743 /* display a message if tracing actions
744  */
745 void
746 trace_act(const char *p, ...)
747 {
748 	va_list args;
749 
750 	if (!TRACEACTIONS || ftrace == NULL)
751 		return;
752 
753 	lastlog();
754 	va_start(args, p);
755 	vfprintf(ftrace, p, args);
756 	va_end(args);
757 	(void)fputc('\n',ftrace);
758 }
759 
760 
761 /* display a message if tracing packets
762  */
763 void
764 trace_pkt(const char *p, ...)
765 {
766 	va_list args;
767 
768 	if (!TRACEPACKETS || ftrace == NULL)
769 		return;
770 
771 	lastlog();
772 	va_start(args, p);
773 	vfprintf(ftrace, p, args);
774 	va_end(args);
775 	(void)fputc('\n',ftrace);
776 }
777 
778 
779 void
780 trace_change(struct rt_entry *rt,
781 	     u_int	state,
782 	     struct	rt_spare *new,
783 	     const char	*label)
784 {
785 	if (ftrace == NULL)
786 		return;
787 
788 	if (rt->rt_metric == new->rts_metric
789 	    && rt->rt_gate == new->rts_gate
790 	    && rt->rt_router == new->rts_router
791 	    && rt->rt_state == state
792 	    && rt->rt_tag == new->rts_tag
793 	    && rt->rt_de_ag == new->rts_de_ag)
794 		return;
795 
796 	lastlog();
797 	(void)fprintf(ftrace, "%s %-35s ",
798 		      label,
799 		      rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate));
800 	print_rts(rt->rt_spares,
801 		  0,0,0,0, AGE_RT(rt->rt_state, rt->rt_ifp));
802 	trace_bits(rs_bits, rt->rt_state, rt->rt_state != state);
803 
804 	(void)fprintf(ftrace, "\n%*s %19s%-16s ",
805 		      (int)strlen(label), "", "",
806 		      (rt->rt_gate != new->rts_gate
807 		       ? naddr_ntoa(new->rts_gate) : ""));
808 	print_rts(new,
809 		  -(new->rts_metric == rt->rt_metric),
810 		  -(new->rts_ifp == rt->rt_ifp),
811 		  0,
812 		  rt->rt_tag != new->rts_tag,
813 		  (rt->rt_time != new->rts_time
814 		   && AGE_RT(rt->rt_state,new->rts_ifp)));
815 	if (rt->rt_state != state)
816 		trace_bits(rs_bits, state, 1);
817 	(void)fputc('\n',ftrace);
818 }
819 
820 
821 void
822 trace_add_del(const char * action, struct rt_entry *rt)
823 {
824 	if (ftrace == NULL)
825 		return;
826 
827 	lastlog();
828 	(void)fprintf(ftrace, "%s    %-35s ",
829 		      action,
830 		      rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate));
831 	print_rts(rt->rt_spares, 0,0,0,0,AGE_RT(rt->rt_state,rt->rt_ifp));
832 	trace_bits(rs_bits, rt->rt_state, 0);
833 	(void)fputc('\n',ftrace);
834 }
835 
836 
837 /* ARGSUSED */
838 static int
839 walk_trace(struct radix_node *rn,
840 	   struct walkarg *w UNUSED)
841 {
842 #define RT ((struct rt_entry *)rn)
843 	struct rt_spare *rts;
844 	int i;
845 
846 	(void)fprintf(ftrace, "  %-35s ",
847 		      rtname(RT->rt_dst, RT->rt_mask, RT->rt_gate));
848 	print_rts(&RT->rt_spares[0], 0,0,0,0, AGE_RT(RT->rt_state, RT->rt_ifp));
849 	trace_bits(rs_bits, RT->rt_state, 0);
850 	if (RT->rt_poison_time >= now_garbage
851 	    && RT->rt_poison_metric < RT->rt_metric)
852 		(void)fprintf(ftrace, "pm=%d@%s",
853 			      RT->rt_poison_metric, ts(RT->rt_poison_time));
854 
855 	rts = &RT->rt_spares[1];
856 	for (i = 1; i < NUM_SPARES; i++, rts++) {
857 		if (rts->rts_gate != RIP_DEFAULT) {
858 			(void)fprintf(ftrace,"\n    #%d%15s%-16s ",
859 				      i, "", naddr_ntoa(rts->rts_gate));
860 			print_rts(rts, 0,0,0,0,1);
861 		}
862 	}
863 	(void)fputc('\n',ftrace);
864 
865 	return 0;
866 }
867 
868 
869 static void
870 trace_dump(void)
871 {
872 	struct interface *ifp;
873 
874 	if (ftrace == NULL)
875 		return;
876 	lastlog();
877 
878 	(void)fputs("current daemon state:\n", ftrace);
879 	LIST_FOREACH(ifp, &ifnet, int_list)
880 		trace_if("", ifp);
881 	(void)rn_walktree(rhead, walk_trace, 0);
882 }
883 
884 
885 void
886 trace_rip(const char *dir1, const char *dir2,
887 	  struct sockaddr_in *who,
888 	  struct interface *ifp,
889 	  struct rip *msg,
890 	  int size)			/* total size of message */
891 {
892 	struct netinfo *n, *lim;
893 #	define NA ((struct netauth*)n)
894 	int i, seen_route;
895 
896 	if (!TRACEPACKETS || ftrace == NULL)
897 		return;
898 
899 	lastlog();
900 	if (msg->rip_cmd >= RIPCMD_MAX
901 	    || msg->rip_vers == 0) {
902 		(void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s"
903 			      " %s.%d size=%d\n",
904 			      dir1, msg->rip_vers, msg->rip_cmd, dir2,
905 			      naddr_ntoa(who->sin_addr.s_addr),
906 			      ntohs(who->sin_port),
907 			      size);
908 		return;
909 	}
910 
911 	(void)fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n",
912 		      dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2,
913 		      naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port),
914 		      ifp ? " via " : "", ifp ? ifp->int_name : "");
915 	if (!TRACECONTENTS)
916 		return;
917 
918 	seen_route = 0;
919 	switch (msg->rip_cmd) {
920 	case RIPCMD_REQUEST:
921 	case RIPCMD_RESPONSE:
922 		n = msg->rip_nets;
923 		lim = (struct netinfo *)((char*)msg + size);
924 		for (; n < lim; n++) {
925 			if (!seen_route
926 			    && n->n_family == RIP_AF_UNSPEC
927 			    && ntohl(n->n_metric) == HOPCNT_INFINITY
928 			    && msg->rip_cmd == RIPCMD_REQUEST
929 			    && (n+1 == lim
930 				|| (n+2 == lim
931 				    && (n+1)->n_family == RIP_AF_AUTH))) {
932 				(void)fputs("\tQUERY ", ftrace);
933 				if (n->n_dst != 0)
934 					(void)fprintf(ftrace, "%s ",
935 						      naddr_ntoa(n->n_dst));
936 				if (n->n_mask != 0)
937 					(void)fprintf(ftrace, "mask=%#x ",
938 						      (u_int)ntohl(n->n_mask));
939 				if (n->n_nhop != 0)
940 					(void)fprintf(ftrace, "nhop=%s ",
941 						      naddr_ntoa(n->n_nhop));
942 				if (n->n_tag != 0)
943 					(void)fprintf(ftrace, "tag=%#x ",
944 						      ntohs(n->n_tag));
945 				(void)fputc('\n',ftrace);
946 				continue;
947 			}
948 
949 			if (n->n_family == RIP_AF_AUTH) {
950 				if (NA->a_type == RIP_AUTH_PW
951 				    && n == msg->rip_nets) {
952 					(void)fprintf(ftrace, "\tPassword"
953 						      " Authentication:"
954 						      " \"%s\"\n",
955 						      qstring(NA->au.au_pw,
956 							  RIP_AUTH_PW_LEN));
957 					continue;
958 				}
959 
960 				if (NA->a_type == RIP_AUTH_MD5
961 				    && n == msg->rip_nets) {
962 					(void)fprintf(ftrace,
963 						      "\tMD5 Auth"
964 						      " pkt_len=%d KeyID=%u"
965 						      " auth_len=%d"
966 						      " seqno=%#x"
967 						      " rsvd=%#x,%#x\n",
968 					    ntohs(NA->au.a_md5.md5_pkt_len),
969 					    NA->au.a_md5.md5_keyid,
970 					    NA->au.a_md5.md5_auth_len,
971 					    (int)ntohl(NA->au.a_md5.md5_seqno),
972 					    (int)ntohs(NA->au.a_md5.rsvd[0]),
973 					    (int)ntohs(NA->au.a_md5.rsvd[1]));
974 					continue;
975 				}
976 				(void)fprintf(ftrace,
977 					      "\tAuthentication type %d: ",
978 					      ntohs(NA->a_type));
979 				for (i = 0;
980 				     i < (int)sizeof(NA->au.au_pw);
981 				     i++)
982 					(void)fprintf(ftrace, "%02x ",
983 						      NA->au.au_pw[i]);
984 				(void)fputc('\n',ftrace);
985 				continue;
986 			}
987 
988 			seen_route = 1;
989 			if (n->n_family != RIP_AF_INET) {
990 				(void)fprintf(ftrace,
991 					      "\t(af %d) %-18s mask=%#x ",
992 					      ntohs(n->n_family),
993 					      naddr_ntoa(n->n_dst),
994 					      (u_int)ntohl(n->n_mask));
995 			} else if (msg->rip_vers == RIPv1) {
996 				(void)fprintf(ftrace, "\t%-18s ",
997 					      addrname(n->n_dst,
998 						       ntohl(n->n_mask),
999 						       n->n_mask==0 ? 2 : 1));
1000 			} else {
1001 				(void)fprintf(ftrace, "\t%-18s ",
1002 					      addrname(n->n_dst,
1003 						       ntohl(n->n_mask),
1004 						       n->n_mask==0 ? 2 : 0));
1005 			}
1006 			(void)fprintf(ftrace, "metric=%-2d ",
1007 				      (u_int)ntohl(n->n_metric));
1008 			if (n->n_nhop != 0)
1009 				(void)fprintf(ftrace, " nhop=%s ",
1010 					      naddr_ntoa(n->n_nhop));
1011 			if (n->n_tag != 0)
1012 				(void)fprintf(ftrace, "tag=%#x",
1013 					      ntohs(n->n_tag));
1014 			(void)fputc('\n',ftrace);
1015 		}
1016 		if (size != (char *)n - (char *)msg)
1017 			(void)fprintf(ftrace, "truncated record, len %d\n",
1018 				size);
1019 		break;
1020 
1021 	case RIPCMD_TRACEON:
1022 		fprintf(ftrace, "\tfile=\"%.*s\"\n", size-4,
1023 			msg->rip_tracefile);
1024 		break;
1025 
1026 	case RIPCMD_TRACEOFF:
1027 		break;
1028 	}
1029 }
1030