| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| * |
| * Copyright (c) 1983, 1988, 1993 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgment: |
| * This product includes software developed by the University of |
| * California, Berkeley and its contributors. |
| * 4. Neither the name of the University nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * $FreeBSD: src/sbin/routed/trace.c,v 1.6 2000/08/11 08:24:38 sheldonh Exp $ |
| */ |
| |
| #include "defs.h" |
| #include "pathnames.h" |
| #include <signal.h> |
| #include <sys/stat.h> |
| #include <sys/signal.h> |
| #include <strings.h> |
| #include <fcntl.h> |
| #include <protocols/routed.h> |
| |
| #define NRECORDS 50 /* size of circular trace buffer */ |
| |
| int tracelevel, new_tracelevel; |
| FILE *ftrace = stdout; /* output trace file */ |
| static const char *sigtrace_pat = "%s"; |
| static char savetracename[MAXPATHLEN+1]; |
| static char *ripcmds[RIPCMD_MAX] = |
| {"#0", "REQUEST", "RESPONSE", "TRACEON", "TRACEOFF", "POLL", |
| "POLLENTRY"}; |
| char inittracename[MAXPATHLEN+1]; |
| static boolean_t file_trace; /* 1=tracing to file, not stdout */ |
| |
| static void tmsg(const char *, ...); |
| |
| const char * |
| rip_strerror(int err) |
| { |
| const char *cp = strerror(err); |
| static char msgbuf[64]; |
| |
| if (cp == NULL) { |
| if (err == 0) { |
| cp = "success"; |
| } else { |
| (void) snprintf(msgbuf, sizeof (msgbuf), |
| "unknown error %d", err); |
| cp = msgbuf; |
| } |
| } |
| return (cp); |
| } |
| |
| /* convert IP address to a string, but not into a single buffer */ |
| char * |
| naddr_ntoa(in_addr_t a) |
| { |
| #define NUM_BUFS 4 |
| static int bufno; |
| static struct { |
| char str[INET_ADDRSTRLEN]; /* xxx.xxx.xxx.xxx\0 */ |
| } bufs[NUM_BUFS]; |
| char *s; |
| struct in_addr addr; |
| |
| addr.s_addr = a; |
| s = strcpy(bufs[bufno].str, inet_ntoa(addr)); |
| bufno = (bufno+1) % NUM_BUFS; |
| return (s); |
| #undef NUM_BUFS |
| } |
| |
| |
| const char * |
| saddr_ntoa(struct sockaddr_storage *ss) |
| { |
| return (ss == NULL) ? "?" : naddr_ntoa(S_ADDR(ss)); |
| } |
| |
| |
| static char * |
| ts(time_t secs) |
| { |
| static char s[20]; |
| |
| secs += epoch.tv_sec; |
| (void) strftime(s, sizeof (s), "%T", localtime(&secs)); |
| return (s); |
| } |
| |
| static char * |
| ts_full(struct timeval *tv) |
| { |
| static char s[32]; |
| time_t secs; |
| int len; |
| |
| secs = tv->tv_sec + epoch.tv_sec; |
| (void) strftime(s, sizeof (s), "%Y/%m/%d %T", localtime(&secs)); |
| len = strlen(s); |
| (void) snprintf(s + len, sizeof (s) - len, ".%06ld", tv->tv_usec); |
| return (s); |
| } |
| |
| /* |
| * On each event, display a time stamp. |
| * This assumes that 'now' is update once for each event, and |
| * that at least now.tv_usec changes. |
| */ |
| static struct timeval lastlog_time; |
| |
| void |
| lastlog(void) |
| { |
| if (lastlog_time.tv_sec != now.tv_sec || |
| lastlog_time.tv_usec != now.tv_usec) { |
| (void) fprintf(ftrace, "-- %s --\n", ts_full(&now)); |
| lastlog_time = now; |
| } |
| } |
| |
| |
| static void |
| tmsg(const char *p, ...) |
| { |
| va_list args; |
| |
| if (ftrace != NULL) { |
| lastlog(); |
| va_start(args, p); |
| (void) vfprintf(ftrace, p, args); |
| (void) fputc('\n', ftrace); |
| (void) fflush(ftrace); |
| (void) va_end(args); |
| } |
| } |
| |
| |
| void |
| trace_close(int zap_stdio) |
| { |
| int fd; |
| |
| |
| (void) fflush(stdout); |
| (void) fflush(stderr); |
| |
| if (ftrace != NULL && zap_stdio) { |
| if (ftrace != stdout) |
| (void) fclose(ftrace); |
| ftrace = NULL; |
| fd = open("/dev/null", O_RDWR); |
| if (isatty(STDIN_FILENO)) |
| (void) dup2(fd, STDIN_FILENO); |
| if (isatty(STDOUT_FILENO)) |
| (void) dup2(fd, STDOUT_FILENO); |
| if (isatty(STDERR_FILENO)) |
| (void) dup2(fd, STDERR_FILENO); |
| (void) close(fd); |
| } |
| lastlog_time.tv_sec = 0; |
| } |
| |
| |
| void |
| trace_flush(void) |
| { |
| if (ftrace != NULL) { |
| (void) fflush(ftrace); |
| if (ferror(ftrace)) |
| trace_off("tracing off: %s", |
| rip_strerror(ferror(ftrace))); |
| } |
| } |
| |
| |
| void |
| trace_off(const char *p, ...) |
| { |
| va_list args; |
| |
| |
| if (ftrace != NULL) { |
| lastlog(); |
| va_start(args, p); |
| (void) vfprintf(ftrace, p, args); |
| (void) fputc('\n', ftrace); |
| (void) va_end(args); |
| } |
| trace_close(file_trace); |
| |
| new_tracelevel = tracelevel = 0; |
| } |
| |
| |
| /* log a change in tracing */ |
| void |
| tracelevel_msg(const char *pat, |
| int dump) /* -1=no dump, 0=default, 1=force */ |
| { |
| static const char *off_msgs[MAX_TRACELEVEL] = { |
| "Tracing actions stopped", |
| "Tracing packets stopped", |
| "Tracing packet contents stopped", |
| "Tracing kernel changes stopped", |
| "Tracing routing socket messages stopped", |
| }; |
| static const char *on_msgs[MAX_TRACELEVEL] = { |
| "Tracing actions started", |
| "Tracing packets started", |
| "Tracing packet contents started", |
| "Tracing kernel changes started", |
| "Tracing routing socket messages started", |
| }; |
| uint_t old_tracelevel = tracelevel; |
| |
| |
| if (new_tracelevel < 0) |
| new_tracelevel = 0; |
| else if (new_tracelevel > MAX_TRACELEVEL) |
| new_tracelevel = MAX_TRACELEVEL; |
| |
| if (new_tracelevel < tracelevel) { |
| if (new_tracelevel <= 0) { |
| trace_off(pat, off_msgs[0]); |
| } else { |
| do { |
| tmsg(pat, off_msgs[tracelevel]); |
| } while (--tracelevel != new_tracelevel); |
| } |
| |
| } else if (new_tracelevel > tracelevel) { |
| do { |
| tmsg(pat, on_msgs[tracelevel++]); |
| } while (tracelevel != new_tracelevel); |
| } |
| |
| if (dump > 0 || |
| (dump == 0 && old_tracelevel == 0 && tracelevel != 0)) |
| trace_dump(); |
| } |
| |
| void |
| set_tracefile(const char *filename, |
| const char *pat, |
| int dump) /* -1=no dump, 0=default, 1=force */ |
| { |
| struct stat stbuf; |
| struct stat stbuf2; |
| FILE *n_ftrace; |
| const char *fn; |
| int nfd; |
| boolean_t allow_create; |
| |
| /* |
| * main() calls this routine with "dump == -1". All others |
| * call it with 0, so we take dump == -1 to mean "can create |
| * the file." |
| */ |
| allow_create = (dump == -1); |
| |
| /* |
| * Allow a null filename to increase the level if the trace file |
| * is already open or if coming from a trusted source, such as |
| * a signal or the command line. |
| */ |
| if (filename == NULL || filename[0] == '\0') { |
| filename = NULL; |
| if (ftrace == NULL) { |
| if (inittracename[0] == '\0') { |
| msglog("missing trace file name"); |
| return; |
| } |
| fn = inittracename; |
| } else { |
| goto set_tracelevel; |
| } |
| |
| } else if (strcmp(filename, "dump/../table") == 0) { |
| trace_dump(); |
| return; |
| |
| } else { |
| /* |
| * Allow the file specified with "-T file" to be reopened, |
| * but require all other names specified over the net to |
| * match the official path. The path can specify a directory |
| * in which the file is to be created. |
| */ |
| |
| if (strcmp(filename, inittracename) != 0) { |
| if (strncmp(filename, PATH_TRACE, |
| sizeof (PATH_TRACE)-1) != 0 || |
| (strstr(filename, "../") != NULL)) { |
| msglog("wrong trace file \"%s\"", filename); |
| return; |
| } |
| if (stat(PATH_TRACE, &stbuf) == -1) { |
| fn = PATH_TRACE; |
| goto missing_file; |
| } |
| if (filename[sizeof (PATH_TRACE) - 1] != '\0' && |
| (filename[sizeof (PATH_TRACE) - 1] != '/' || |
| !S_ISDIR(stbuf.st_mode))) { |
| goto bad_file_type; |
| } |
| if (S_ISDIR(stbuf.st_mode)) |
| allow_create = _B_TRUE; |
| } |
| |
| fn = filename; |
| } |
| /* fn cannot be null here */ |
| |
| /* If the new tracefile exists, it must be a regular file. */ |
| if (lstat(fn, &stbuf) == -1) { |
| if (!allow_create) |
| goto missing_file; |
| nfd = open(fn, O_CREAT|O_EXCL|O_WRONLY, 0644); |
| if (nfd != -1 && fstat(nfd, &stbuf) == -1) { |
| (void) close(nfd); |
| goto missing_file; |
| } |
| } else if (S_ISREG(stbuf.st_mode)) { |
| nfd = open(fn, O_APPEND|O_WRONLY, 0644); |
| } else { |
| goto bad_file_type; |
| } |
| |
| if (nfd == -1 || (n_ftrace = fdopen(nfd, "a")) == NULL) { |
| msglog("failed to open trace file \"%s\" %s", fn, |
| rip_strerror(errno)); |
| if (fn == inittracename) |
| inittracename[0] = '\0'; |
| if (nfd != -1) |
| (void) close(nfd); |
| return; |
| } |
| |
| if (fstat(nfd, &stbuf2) == -1 || !S_ISREG(stbuf2.st_mode) || |
| stbuf2.st_dev != stbuf.st_dev || stbuf2.st_ino != stbuf.st_ino) { |
| msglog("trace file \"%s\" moved", fn); |
| (void) fclose(n_ftrace); |
| return; |
| } |
| |
| tmsg("switch to trace file %s", fn); |
| trace_close(file_trace = _B_TRUE); |
| (void) dup2(nfd, STDOUT_FILENO); |
| (void) dup2(nfd, STDERR_FILENO); |
| |
| if (fn != savetracename) |
| (void) strlcpy(savetracename, fn, sizeof (savetracename) - 1); |
| ftrace = n_ftrace; |
| |
| set_tracelevel: |
| if (new_tracelevel == 0 || filename == NULL) |
| new_tracelevel++; |
| tracelevel_msg(pat, dump != 0 ? dump : (filename != NULL)); |
| return; |
| |
| missing_file: |
| msglog("trace \"%s\" missing", fn); |
| return; |
| |
| bad_file_type: |
| msglog("wrong type (%#x) of trace file \"%s\"", stbuf.st_mode, fn); |
| } |
| |
| |
| /* ARGSUSED */ |
| void |
| sigtrace_more(int s) |
| { |
| new_tracelevel++; |
| sigtrace_pat = "SIGUSR1: %s"; |
| if (signal(s, sigtrace_more) == SIG_ERR) |
| msglog("signal: %s", rip_strerror(errno)); |
| } |
| |
| |
| /* ARGSUSED */ |
| void |
| sigtrace_less(int s) |
| { |
| new_tracelevel--; |
| sigtrace_pat = "SIGUSR2: %s"; |
| if (signal(s, sigtrace_less) == SIG_ERR) |
| msglog("signal: %s", rip_strerror(errno)); |
| } |
| |
| /* ARGSUSED */ |
| void |
| sigtrace_dump(int s) |
| { |
| trace_dump(); |
| if (signal(s, sigtrace_dump) == SIG_ERR) |
| msglog("signal: %s", rip_strerror(errno)); |
| } |
| |
| /* Set tracing after a signal. */ |
| void |
| set_tracelevel(void) |
| { |
| if (new_tracelevel == tracelevel) |
| return; |
| |
| /* |
| * If tracing entirely off, and there was no tracefile specified |
| * on the command line, then leave it off. |
| */ |
| if (new_tracelevel > tracelevel && ftrace == NULL) { |
| if (savetracename[0] != '\0') { |
| set_tracefile(savetracename, sigtrace_pat, 0); |
| } else if (inittracename[0] != '\0') { |
| set_tracefile(inittracename, sigtrace_pat, 0); |
| } else { |
| new_tracelevel = 0; |
| return; |
| } |
| } else { |
| tracelevel_msg(sigtrace_pat, 0); |
| } |
| } |
| |
| |
| /* display an address */ |
| char * |
| addrname(in_addr_t addr, /* in network byte order */ |
| in_addr_t mask, |
| int force) /* 0=show mask if nonstandard, */ |
| { /* 1=always show mask, 2=never */ |
| #define NUM_BUFS 4 |
| static int bufno; |
| static struct { |
| /* |
| * this array can hold either of the following strings terminated |
| * by a null character: |
| * "xxx.xxx.xxx.xxx/xx" |
| * "xxx.xxx.xxx.xxx (mask xxx.xxx.xxx.xxx)" |
| * |
| */ |
| char str[2*INET_ADDRSTRLEN + sizeof (" (mask )")]; |
| } bufs[NUM_BUFS]; |
| char *s, *sp; |
| in_addr_t dmask; |
| int i, len; |
| struct in_addr tmp_addr; |
| |
| tmp_addr.s_addr = addr; |
| len = strlcpy(bufs[bufno].str, inet_ntoa(tmp_addr), |
| sizeof (bufs[bufno].str)); |
| s = bufs[bufno].str; |
| bufno = (bufno+1) % NUM_BUFS; |
| |
| if (force == 1 || (force == 0 && mask != std_mask(addr))) { |
| sp = &s[strlen(s)]; |
| |
| dmask = mask & -mask; |
| if (mask + dmask == 0) { |
| i = ffs(mask); |
| (void) snprintf(sp, |
| (sizeof (bufs[bufno].str) - len), "/%d", |
| (NBBY * sizeof (in_addr_t) + 1) - i); |
| |
| } else { |
| (void) snprintf(sp, |
| (sizeof (bufs[bufno].str) - len), " (mask %s)", |
| naddr_ntoa(htonl(mask))); |
| } |
| } |
| |
| return (s); |
| #undef NUM_BUFS |
| } |
| |
| |
| /* display a bit-field */ |
| struct or_bits { |
| uint8_t origin; |
| const char *origin_name; |
| }; |
| |
| static struct or_bits origin_bits[] = { |
| { RO_RIP, "RIP" }, |
| { RO_RDISC, "RDISC" }, |
| { RO_STATIC, "STATIC" }, |
| { RO_LOOPBCK, "LOOPBCK" }, |
| { RO_PTOPT, "PTOPT" }, |
| { RO_NET_SYN, "NET_SYN" }, |
| { RO_IF, "IF" }, |
| { RO_FILE, "FILE" }, |
| { RO_NONE, " " }, |
| { 0, NULL} |
| }; |
| |
| /* display a bit-field */ |
| struct bits { |
| uint64_t bits_mask; |
| uint64_t bits_clear; |
| const char *bits_name; |
| }; |
| |
| static struct bits if_bits[] = { |
| { IFF_BROADCAST, 0, "BROADCAST" }, |
| { IFF_DEBUG, 0, "DEBUG" }, |
| { IFF_LOOPBACK, 0, "LOOPBACK" }, |
| { IFF_POINTOPOINT, 0, "POINTOPOINT" }, |
| { IFF_NOTRAILERS, 0, "NOTRAILERS" }, |
| { IFF_RUNNING, 0, "RUNNING" }, |
| { IFF_NOARP, 0, "NOARP" }, |
| { IFF_PROMISC, 0, "PROMISC" }, |
| { IFF_ALLMULTI, 0, "ALLMULTI" }, |
| { IFF_INTELLIGENT, 0, "INTELLIGENT" }, |
| { IFF_MULTICAST, 0, "MULTICAST" }, |
| { IFF_MULTI_BCAST, 0, "MULTI_BCAST" }, |
| { IFF_UNNUMBERED, 0, "UNNUMBERED" }, |
| { IFF_DHCPRUNNING, 0, "DHCP" }, |
| { IFF_PRIVATE, 0, "PRIVATE" }, |
| { IFF_NOXMIT, 0, "NOXMIT" }, |
| { IFF_NOLOCAL, 0, "NOLOCAL" }, |
| { IFF_DEPRECATED, 0, "DEPRECATED" }, |
| { IFF_ADDRCONF, 0, "ADDRCONF" }, |
| { IFF_ROUTER, 0, "ROUTER" }, |
| { IFF_NONUD, 0, "NONUD" }, |
| { IFF_ANYCAST, 0, "ANYCAST" }, |
| { IFF_NORTEXCH, 0, "NORTEXCH" }, |
| { IFF_IPV4, 0, "IPv4" }, |
| { IFF_IPV6, 0, "IPv6" }, |
| { IFF_NOFAILOVER, 0, "NOFAILOVER" }, |
| { IFF_FAILED, 0, "FAILED" }, |
| { IFF_STANDBY, 0, "STANDBY" }, |
| { IFF_INACTIVE, 0, "INACTIVE" }, |
| { IFF_OFFLINE, 0, "OFFLINE" }, |
| { IFF_XRESOLV, 0, "XRESOLV" }, |
| { IFF_COS_ENABLED, 0, "CoS" }, |
| { IFF_PREFERRED, 0, "PREFERRED" }, |
| { IFF_TEMPORARY, 0, "TEMPORARY" }, |
| { IFF_FIXEDMTU, 0, "FIXEDMTU" }, |
| { IFF_VIRTUAL, 0, "VIRTUAL"}, |
| { IFF_IPMP, 0, "IPMP"}, |
| { 0, 0, NULL} |
| }; |
| |
| static struct bits is_bits[] = { |
| { IS_ALIAS, 0, "ALIAS" }, |
| { IS_SUBNET, 0, "" }, |
| { IS_REMOTE, (IS_NO_RDISC | |
| IS_BCAST_RDISC), "REMOTE" }, |
| { IS_PASSIVE, (IS_NO_RDISC | |
| IS_NO_RIP | |
| IS_NO_SUPER_AG | |
| IS_PM_RDISC | |
| IS_NO_AG), "PASSIVE" }, |
| { IS_EXTERNAL, 0, "EXTERNAL" }, |
| { IS_CHECKED, 0, "" }, |
| { IS_ALL_HOSTS, 0, "" }, |
| { IS_ALL_ROUTERS, 0, "" }, |
| { IS_DISTRUST, 0, "DISTRUST" }, |
| { IS_BROKE, IS_SICK, "BROKEN" }, |
| { IS_SICK, 0, "SICK" }, |
| { IS_DUP, 0, "DUPLICATE" }, |
| { IS_REDIRECT_OK, 0, "REDIRECT_OK" }, |
| { IS_NEED_NET_SYN, 0, "" }, |
| { IS_NO_AG, IS_NO_SUPER_AG, "NO_AG" }, |
| { IS_NO_SUPER_AG, 0, "NO_SUPER_AG" }, |
| { (IS_NO_RIPV1_IN | |
| IS_NO_RIPV2_IN | |
| IS_NO_RIPV1_OUT | |
| IS_NO_RIPV2_OUT), 0, "NO_RIP" }, |
| { (IS_NO_RIPV1_IN | |
| IS_NO_RIPV1_OUT), 0, "RIPV2" }, |
| { IS_NO_RIPV1_IN, 0, "NO_RIPV1_IN" }, |
| { IS_NO_RIPV2_IN, 0, "NO_RIPV2_IN" }, |
| { IS_NO_RIPV1_OUT, 0, "NO_RIPV1_OUT" }, |
| { IS_NO_RIPV2_OUT, 0, "NO_RIPV2_OUT" }, |
| { IS_NO_RIP_MCAST, 0, "NO_RIP_MCAST" }, |
| { (IS_NO_ADV_IN | |
| IS_NO_SOL_OUT | |
| IS_NO_ADV_OUT), IS_BCAST_RDISC, "NO_RDISC" }, |
| { IS_NO_SOL_OUT, 0, "NO_SOLICIT" }, |
| { IS_SOL_OUT, 0, "SEND_SOLICIT" }, |
| { IS_NO_ADV_OUT, IS_BCAST_RDISC, "NO_RDISC_ADV" }, |
| { IS_ADV_OUT, 0, "RDISC_ADV" }, |
| { IS_BCAST_RDISC, 0, "BCAST_RDISC" }, |
| { IS_PM_RDISC, 0, "" }, |
| { IS_NO_HOST, 0, "NO_HOST" }, |
| { IS_SUPPRESS_RDISC, 0, "SUPPRESS_RDISC" }, |
| { IS_FLUSH_RDISC, 0, "FLUSH_RDISC" }, |
| { 0, 0, NULL} |
| }; |
| |
| static struct bits rs_bits[] = { |
| { RS_IF, 0, "IF" }, |
| { RS_NET_INT, RS_NET_SYN, "NET_INT" }, |
| { RS_NET_SYN, 0, "NET_SYN" }, |
| { RS_SUBNET, 0, "" }, |
| { RS_LOCAL, 0, "LOCAL" }, |
| { RS_MHOME, 0, "MHOME" }, |
| { RS_STATIC, 0, "STATIC" }, |
| { RS_NOPROPAGATE, 0, "NOPROP" }, |
| { RS_BADIF, 0, "BADIF" }, |
| { 0, 0, NULL} |
| }; |
| |
| static struct bits ks_bits[] = { |
| { KS_NEW, 0, "NEW" }, |
| { KS_DELETE, 0, "DELETE" }, |
| { KS_ADD, 0, "ADD" }, |
| { KS_CHANGE, 0, "CHANGE" }, |
| { KS_DEL_ADD, 0, "DEL_ADD" }, |
| { KS_STATIC, 0, "STATIC" }, |
| { KS_GATEWAY, 0, "GATEWAY" }, |
| { KS_DYNAMIC, 0, "DYNAMIC" }, |
| { KS_DELETED, 0, "DELETED" }, |
| { KS_PRIVATE, 0, "PRIVATE" }, |
| { KS_CHECK, 0, "CHECK" }, |
| { KS_IF, 0, "IF" }, |
| { KS_PASSIVE, 0, "PASSIVE" }, |
| { KS_DEPRE_IF, 0, "DEPRE_IF" }, |
| { KS_FILE, 0, "FILE" }, |
| { 0, 0, NULL} |
| }; |
| |
| static void |
| trace_bits(const struct bits *tbl, |
| uint64_t field, |
| boolean_t force) |
| { |
| uint64_t b; |
| char c; |
| |
| if (force) { |
| (void) putc('<', ftrace); |
| c = '\0'; |
| } else { |
| c = '<'; |
| } |
| |
| while (field != 0 && |
| (b = tbl->bits_mask) != 0) { |
| if ((b & field) == b) { |
| if (tbl->bits_name[0] != '\0') { |
| if (c != '\0') |
| (void) putc(c, ftrace); |
| (void) fprintf(ftrace, "%s", tbl->bits_name); |
| c = '|'; |
| } |
| field &= ~(b | tbl->bits_clear); |
| } |
| tbl++; |
| } |
| if (field != 0) { |
| if (c != '\0') |
| (void) putc(c, ftrace); |
| (void) fprintf(ftrace, "%#llx", field); |
| c = '|'; |
| } |
| |
| if (c != '<' || force) |
| (void) fputs("> ", ftrace); |
| } |
| |
| static char * |
| trace_string(const struct bits *tbl, uint_t field, boolean_t force) |
| { |
| const struct bits *tbp; |
| char *sbuf, *cp, chr; |
| size_t slen; |
| |
| /* minimum default string */ |
| slen = sizeof ("<0x12345678>"); |
| for (tbp = tbl; tbp->bits_mask != 0; tbp++) |
| if (tbp->bits_name[0] != '\0') |
| slen += strlen(tbp->bits_name) + 1; |
| if ((sbuf = malloc(slen)) == NULL) |
| return (NULL); |
| cp = sbuf; |
| |
| if (force) { |
| *cp++ = '<'; |
| chr = '\0'; |
| } else { |
| chr = '<'; |
| } |
| |
| while (field != 0 && tbl->bits_mask != 0) { |
| if ((tbl->bits_mask & field) == tbl->bits_mask) { |
| if (tbl->bits_name[0] != '\0') { |
| if (chr != '\0') |
| *cp++ = chr; |
| (void) strcpy(cp, tbl->bits_name); |
| cp += strlen(tbl->bits_name); |
| chr = '|'; |
| } |
| field &= ~(tbl->bits_mask | tbl->bits_clear); |
| } |
| tbl++; |
| } |
| if (field != 0) { |
| if (chr != '\0') |
| *cp++ = chr; |
| cp += sprintf(cp, "%#x", field); |
| chr = '|'; |
| } |
| |
| if (chr != '<' || force) |
| *cp++ = '>'; |
| *cp = '\0'; |
| return (sbuf); |
| } |
| |
| char * |
| if_bit_string(uint_t field, boolean_t force) |
| { |
| return (trace_string(if_bits, field, force)); |
| } |
| |
| char * |
| rtname(in_addr_t dst, |
| in_addr_t mask, |
| in_addr_t gate) |
| { |
| static char buf[sizeof ("xxx.xxx.xxx.xxx/xx-->xxx.xxx.xxx.xxx")]; |
| int i; |
| |
| (void) snprintf(buf, sizeof (buf), "%-16s-->", addrname(dst, mask, 0)); |
| i = strlen(buf); |
| (void) snprintf(&buf[i], (sizeof (buf) -i), "%-*s", 15+24-MAX(24, i), |
| naddr_ntoa(gate)); |
| return (buf); |
| } |
| |
| |
| static void |
| print_rts(struct rt_spare *rts, |
| int force_metric, /* -1=suppress, 0=default */ |
| int force_ifp, /* -1=suppress, 0=default */ |
| int force_router, /* -1=suppress, 0=default, 1=display */ |
| int force_tag, /* -1=suppress, 0=default, 1=display */ |
| int force_time) /* 0=suppress, 1=display */ |
| { |
| int i; |
| |
| if (force_metric >= 0) |
| (void) fprintf(ftrace, "metric=%-2d ", rts->rts_metric); |
| if (force_ifp >= 0) |
| (void) fprintf(ftrace, "%s ", (rts->rts_ifp == 0 ? |
| "if?" : rts->rts_ifp->int_name)); |
| if (force_router > 0 || |
| (force_router == 0 && rts->rts_router != rts->rts_gate)) |
| (void) fprintf(ftrace, "router=%s ", |
| naddr_ntoa(rts->rts_router)); |
| if (force_time > 0) |
| (void) fprintf(ftrace, "%s ", ts(rts->rts_time)); |
| if (force_tag > 0 || |
| (force_tag == 0 && rts->rts_tag != 0)) |
| (void) fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag)); |
| if (rts->rts_de_ag != 0) { |
| for (i = 1; (uint_t)(1 << i) <= rts->rts_de_ag; i++) |
| continue; |
| (void) fprintf(ftrace, "de_ag=%d ", i); |
| } |
| (void) fprintf(ftrace, "flags 0x%x ", rts->rts_flags); |
| |
| } |
| |
| |
| static void |
| print_rtsorigin(const struct or_bits *tbl, uint8_t route_origin) |
| { |
| |
| uint8_t tblentry; |
| while ((tblentry = tbl->origin) != 0) { |
| if (tblentry == route_origin) { |
| (void) fprintf(ftrace, "origin=%s ", tbl->origin_name); |
| } |
| tbl++; |
| } |
| } |
| |
| |
| void |
| trace_if(const char *act, struct interface *ifp) |
| { |
| if (!TRACEACTIONS || ftrace == NULL) |
| return; |
| |
| lastlog(); |
| (void) fprintf(ftrace, "%-3s interface %-4s #%-3d ", act, |
| ifp->int_name, |
| ifp->int_phys != NULL ? ifp->int_phys->phyi_index : 0); |
| (void) fprintf(ftrace, "%-15s-->%-15s", |
| naddr_ntoa(ifp->int_addr), |
| addrname(((ifp->int_if_flags & IFF_POINTOPOINT) ? |
| ifp->int_dstaddr : htonl(ifp->int_net)), |
| ifp->int_mask, 1)); |
| if (ifp->int_metric != 0) |
| (void) fprintf(ftrace, " metric=%d", ifp->int_metric); |
| if (!IS_RIP_OUT_OFF(ifp->int_state) && |
| ifp->int_d_metric != 0) |
| (void) fprintf(ftrace, " fake_default=%d", ifp->int_d_metric); |
| (void) fputs("\n ", ftrace); |
| trace_bits(if_bits, ifp->int_if_flags, _B_FALSE); |
| trace_bits(is_bits, ifp->int_state, _B_FALSE); |
| (void) fputc('\n', ftrace); |
| } |
| |
| void |
| trace_khash(const struct khash *krt) |
| { |
| if (ftrace == NULL) |
| return; |
| |
| lastlog(); |
| (void) fprintf(ftrace, " %-15s-->%-15s metric=%d ", |
| addrname(krt->k_dst, krt->k_mask, 0), |
| naddr_ntoa(krt->k_gate), krt->k_metric); |
| if (krt->k_ifp != NULL) |
| (void) fprintf(ftrace, "ifp %s ", krt->k_ifp->int_name); |
| else |
| (void) fprintf(ftrace, "ifp NULL "); |
| (void) fprintf(ftrace, "%s ", ts(krt->k_keep)); |
| (void) fprintf(ftrace, "%s ", ts(krt->k_redirect_time)); |
| trace_bits(ks_bits, krt->k_state, _B_TRUE); |
| (void) fputc('\n', ftrace); |
| } |
| |
| void |
| trace_dr(const struct dr *drp) |
| { |
| if (ftrace == NULL) |
| return; |
| |
| lastlog(); |
| (void) fprintf(ftrace, " %-4s %-15s %s ", |
| drp->dr_ifp != NULL ? drp->dr_ifp->int_name : "?", |
| naddr_ntoa(drp->dr_gate), ts(drp->dr_ts)); |
| (void) fprintf(ftrace, "%s %d %u\n", ts(drp->dr_life), |
| SIGN_PREF(drp->dr_recv_pref), drp->dr_pref); |
| } |
| |
| void |
| trace_upslot(struct rt_entry *rt, |
| struct rt_spare *rts, |
| struct rt_spare *new) |
| { |
| if (!TRACEACTIONS || ftrace == NULL) |
| return; |
| |
| if (rts->rts_gate == new->rts_gate && |
| rts->rts_router == new->rts_router && |
| rts->rts_metric == new->rts_metric && |
| rts->rts_tag == new->rts_tag && |
| rts->rts_de_ag == new->rts_de_ag) |
| return; |
| |
| lastlog(); |
| if (new->rts_gate == 0) { |
| (void) fprintf(ftrace, "Del #%d %-35s ", |
| (int)(rts - rt->rt_spares), |
| rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate)); |
| print_rts(rts, 0, 0, 0, 0, |
| (rts != rt->rt_spares || |
| AGE_RT(rt->rt_state, rts->rts_origin, new->rts_ifp))); |
| |
| } else if (rts->rts_gate != RIP_DEFAULT) { |
| (void) fprintf(ftrace, "Chg #%d %-35s ", |
| (int)(rts - rt->rt_spares), |
| rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate)); |
| print_rts(rts, 0, 0, |
| rts->rts_gate != new->rts_gate, |
| rts->rts_tag != new->rts_tag, |
| rts != rt->rt_spares || |
| AGE_RT(rt->rt_state, rts->rts_origin, rt->rt_ifp)); |
| |
| (void) fprintf(ftrace, "\n %19s%-16s ", "", |
| (new->rts_gate != rts->rts_gate ? |
| naddr_ntoa(new->rts_gate) : "")); |
| print_rts(new, |
| ((new->rts_metric == rts->rts_metric) ? -1 : 0), |
| ((new->rts_ifp == rts->rts_ifp) ? -1 : 0), |
| 0, |
| rts->rts_tag != new->rts_tag, |
| (new->rts_time != rts->rts_time && |
| (rts != rt->rt_spares || |
| AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp)))); |
| |
| } else { |
| (void) fprintf(ftrace, "Add #%d %-35s ", |
| (int)(rts - rt->rt_spares), |
| rtname(rt->rt_dst, rt->rt_mask, new->rts_gate)); |
| print_rts(new, 0, 0, 0, 0, |
| (rts != rt->rt_spares || |
| AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp))); |
| } |
| (void) fputc('\n', ftrace); |
| } |
| |
| |
| /* miscellaneous message checked by the caller */ |
| void |
| trace_misc(const char *p, ...) |
| { |
| va_list args; |
| |
| if (ftrace == NULL) |
| return; |
| |
| lastlog(); |
| va_start(args, p); |
| (void) vfprintf(ftrace, p, args); |
| (void) fputc('\n', ftrace); |
| (void) va_end(args); |
| } |
| |
| |
| /* display a message if tracing actions */ |
| void |
| trace_act(const char *p, ...) |
| { |
| va_list args; |
| |
| if (!TRACEACTIONS || ftrace == NULL) |
| return; |
| |
| lastlog(); |
| va_start(args, p); |
| (void) vfprintf(ftrace, p, args); |
| (void) fputc('\n', ftrace); |
| (void) va_end(args); |
| } |
| |
| |
| /* display a message if tracing packets */ |
| void |
| trace_pkt(const char *p, ...) |
| { |
| va_list args; |
| |
| if (!TRACEPACKETS || ftrace == NULL) |
| return; |
| |
| lastlog(); |
| va_start(args, p); |
| (void) vfprintf(ftrace, p, args); |
| (void) fputc('\n', ftrace); |
| (void) va_end(args); |
| } |
| |
| |
| void |
| trace_change(struct rt_entry *rt, |
| uint16_t state, |
| struct rt_spare *new, |
| const char *label) |
| { |
| if (ftrace == NULL) |
| return; |
| |
| if (rt->rt_metric == new->rts_metric && |
| rt->rt_gate == new->rts_gate && |
| rt->rt_router == new->rts_router && |
| rt->rt_state == state && |
| rt->rt_tag == new->rts_tag && |
| rt->rt_de_ag == new->rts_de_ag) |
| return; |
| |
| lastlog(); |
| (void) fprintf(ftrace, "%s %-35s ", |
| label, |
| rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate)); |
| print_rts(rt->rt_spares, |
| 0, 0, 0, 0, AGE_RT(rt->rt_state, rt->rt_spares->rts_origin, |
| rt->rt_ifp)); |
| print_rtsorigin(origin_bits, rt->rt_spares->rts_origin); |
| trace_bits(rs_bits, rt->rt_state, rt->rt_state != state); |
| |
| (void) fprintf(ftrace, "\n%*s %19s%-16s ", |
| strlen(label), "", "", |
| (rt->rt_gate != new->rts_gate ? |
| naddr_ntoa(new->rts_gate) : "")); |
| print_rts(new, |
| ((new->rts_metric == rt->rt_metric) ? -1 : 0), |
| ((new->rts_ifp == rt->rt_ifp) ? -1 : 0), |
| 0, |
| rt->rt_tag != new->rts_tag, |
| (rt->rt_time != new->rts_time && |
| AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp))); |
| if (rt->rt_state != state) { |
| print_rtsorigin(origin_bits, new->rts_origin); |
| trace_bits(rs_bits, state, _B_TRUE); |
| } |
| (void) fputc('\n', ftrace); |
| } |
| |
| |
| void |
| trace_add_del(const char *action, struct rt_entry *rt) |
| { |
| if (ftrace == NULL) |
| return; |
| |
| lastlog(); |
| (void) fprintf(ftrace, "%s %-35s ", |
| action, |
| rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate)); |
| print_rts(rt->rt_spares, 0, 0, 0, 0, AGE_RT(rt->rt_state, |
| rt->rt_spares->rts_origin, rt->rt_ifp)); |
| print_rtsorigin(origin_bits, rt->rt_spares->rts_origin); |
| trace_bits(rs_bits, rt->rt_state, _B_FALSE); |
| (void) fputc('\n', ftrace); |
| } |
| |
| |
| /* ARGSUSED */ |
| static int |
| walk_trace(struct radix_node *rn, |
| void *w) |
| { |
| #define RT ((struct rt_entry *)rn) |
| struct rt_spare *rts; |
| int i; |
| |
| (void) fprintf(ftrace, " %-35s ", |
| rtname(RT->rt_dst, RT->rt_mask, RT->rt_gate)); |
| print_rts(&RT->rt_spares[0], 0, 0, 0, 0, |
| AGE_RT(RT->rt_state, RT->rt_spares[0].rts_origin, RT->rt_ifp)); |
| print_rtsorigin(origin_bits, RT->rt_spares[0].rts_origin); |
| trace_bits(rs_bits, RT->rt_state, _B_FALSE); |
| if (RT->rt_poison_time >= now_garbage && |
| RT->rt_poison_metric < RT->rt_metric) |
| (void) fprintf(ftrace, "pm=%d@%s", |
| RT->rt_poison_metric, ts(RT->rt_poison_time)); |
| (void) fprintf(ftrace, "%d spare slots", RT->rt_num_spares); |
| |
| rts = &RT->rt_spares[1]; |
| for (i = 1; i < RT->rt_num_spares; i++, rts++) { |
| if (rts->rts_gate != RIP_DEFAULT) { |
| (void) fprintf(ftrace, "\n #%d%15s%-16s ", |
| i, "", naddr_ntoa(rts->rts_gate)); |
| print_rts(rts, 0, 0, 0, 0, 1); |
| print_rtsorigin(origin_bits, rts->rts_origin); |
| } |
| } |
| (void) fputc('\n', ftrace); |
| |
| return (0); |
| } |
| |
| |
| void |
| trace_dump(void) |
| { |
| struct interface *ifp; |
| |
| if (ftrace == NULL) |
| return; |
| lastlog(); |
| |
| /* |
| * Warning: the rtquery.trace.* family of STC tests depend on |
| * the log file format here. If you need to change this next |
| * message, make sure that you change the TRACE_DUMP variable |
| * as well. |
| */ |
| (void) fputs("current daemon state:\n", ftrace); |
| for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) |
| trace_if("", ifp); |
| (void) fputs("Routes:\n", ftrace); |
| (void) rn_walktree(rhead, walk_trace, NULL); |
| (void) fputs("Kernel routes:\n", ftrace); |
| kern_dump(); |
| (void) fputs("Discovered routers:\n", ftrace); |
| rdisc_dump(); |
| } |
| |
| |
| void |
| trace_rip(const char *dir1, const char *dir2, |
| struct sockaddr_in *who, |
| struct interface *ifp, |
| struct rip *msg, |
| int size) /* total size of message */ |
| { |
| struct netinfo *n, *lim; |
| #define NA ((struct netauth *)n) |
| int i, seen_route; |
| struct in_addr tmp_mask; |
| |
| if (!TRACEPACKETS || ftrace == NULL) |
| return; |
| |
| lastlog(); |
| if (msg->rip_cmd >= RIPCMD_MAX || msg->rip_vers == 0) { |
| (void) fprintf(ftrace, "%s bad RIPv%d cmd=%d %s" |
| " %s.%d size=%d\n", |
| dir1, msg->rip_vers, msg->rip_cmd, dir2, |
| naddr_ntoa(who->sin_addr.s_addr), |
| ntohs(who->sin_port), |
| size); |
| return; |
| } |
| |
| (void) fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n", |
| dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2, |
| naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port), |
| ifp ? " via " : "", ifp ? ifp->int_name : ""); |
| if (!TRACECONTENTS) |
| return; |
| |
| seen_route = 0; |
| switch (msg->rip_cmd) { |
| case RIPCMD_REQUEST: |
| case RIPCMD_RESPONSE: |
| |
| n = msg->rip_nets; |
| tmp_mask.s_addr = n->n_mask; |
| lim = n + (size - 4) / sizeof (struct netinfo); |
| for (; n < lim; n++) { |
| if (!seen_route && |
| n->n_family == RIP_AF_UNSPEC && |
| ntohl(n->n_metric) == HOPCNT_INFINITY && |
| msg->rip_cmd == RIPCMD_REQUEST && |
| (n+1 == lim || |
| (n+2 == lim && |
| (n+1)->n_family == RIP_AF_AUTH))) { |
| (void) fputs("\tQUERY ", ftrace); |
| if (n->n_dst != 0) |
| (void) fprintf(ftrace, "%s ", |
| naddr_ntoa(n->n_dst)); |
| if (n->n_mask != 0) |
| (void) fprintf(ftrace, "mask=%s ", |
| inet_ntoa(tmp_mask)); |
| if (n->n_nhop != 0) |
| (void) fprintf(ftrace, "nhop=%s ", |
| naddr_ntoa(n->n_nhop)); |
| if (n->n_tag != 0) |
| (void) fprintf(ftrace, "tag=%#x ", |
| ntohs(n->n_tag)); |
| (void) fputc('\n', ftrace); |
| continue; |
| } |
| |
| if (n->n_family == RIP_AF_AUTH) { |
| if (NA->a_type == RIP_AUTH_PW && |
| n == msg->rip_nets) { |
| (void) fprintf(ftrace, "\tPassword" |
| " Authentication: \"%s\"\n", |
| qstring(NA->au.au_pw, |
| RIP_AUTH_PW_LEN)); |
| continue; |
| } |
| |
| if (NA->a_type == RIP_AUTH_MD5 && |
| n == msg->rip_nets) { |
| (void) fprintf(ftrace, |
| "\tMD5 Auth" |
| " pkt_len=%d KeyID=%u" |
| " auth_len=%d" |
| " seqno=%#x" |
| " rsvd=%#hx,%#hx\n", |
| ntohs(NA->au.a_md5.md5_pkt_len), |
| NA->au.a_md5.md5_keyid, |
| NA->au.a_md5.md5_auth_len, |
| ntohl(NA->au.a_md5.md5_seqno), |
| ntohs(NA->au.a_md5.rsvd[0]), |
| ntohs(NA->au.a_md5.rsvd[1])); |
| continue; |
| } |
| (void) fprintf(ftrace, |
| "\tAuthentication type %d: ", |
| ntohs(NA->a_type)); |
| for (i = 0; i < (int)sizeof (NA->au.au_pw); |
| i++) |
| (void) fprintf(ftrace, "%02x ", |
| NA->au.au_pw[i]); |
| (void) fputc('\n', ftrace); |
| continue; |
| } |
| |
| seen_route = 1; |
| if (n->n_family != RIP_AF_INET) { |
| (void) fprintf(ftrace, |
| "\t(af %d) %-18s mask=%s ", |
| ntohs(n->n_family), |
| naddr_ntoa(n->n_dst), |
| inet_ntoa(tmp_mask)); |
| } else if (msg->rip_vers == RIPv1) { |
| (void) fprintf(ftrace, "\t%-18s ", |
| addrname(n->n_dst, ntohl(n->n_mask), |
| n->n_mask == 0 ? 2 : 1)); |
| } else { |
| (void) fprintf(ftrace, "\t%-18s ", |
| addrname(n->n_dst, ntohl(n->n_mask), |
| n->n_mask == 0 ? 2 : 0)); |
| } |
| (void) fprintf(ftrace, "metric=%-2lu ", |
| (unsigned long)ntohl(n->n_metric)); |
| if (n->n_nhop != 0) |
| (void) fprintf(ftrace, " nhop=%s ", |
| naddr_ntoa(n->n_nhop)); |
| if (n->n_tag != 0) |
| (void) fprintf(ftrace, "tag=%#x", |
| ntohs(n->n_tag)); |
| (void) fputc('\n', ftrace); |
| } |
| if (size != (char *)n - (char *)msg) |
| (void) fprintf(ftrace, "truncated record, len %d\n", |
| size); |
| break; |
| |
| case RIPCMD_TRACEON: |
| (void) fprintf(ftrace, "\tfile=\"%.*s\"\n", size - 4, |
| msg->rip_tracefile); |
| break; |
| |
| case RIPCMD_TRACEOFF: |
| break; |
| } |
| } |