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