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