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