summaryrefslogtreecommitdiff
path: root/usr.sbin/mtrace
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/mtrace')
-rw-r--r--usr.sbin/mtrace/mtrace.834
-rw-r--r--usr.sbin/mtrace/mtrace.c1430
2 files changed, 975 insertions, 489 deletions
diff --git a/usr.sbin/mtrace/mtrace.8 b/usr.sbin/mtrace/mtrace.8
index 37495c5b44e..66ac0fb802c 100644
--- a/usr.sbin/mtrace/mtrace.8
+++ b/usr.sbin/mtrace/mtrace.8
@@ -1,4 +1,4 @@
-.\" $NetBSD: mtrace.8,v 1.3 1995/10/04 03:47:54 thorpej Exp $
+.\" $NetBSD: mtrace.8,v 1.4 1995/12/10 10:57:11 mycroft Exp $
.\"
.\" Copyright (c) 1995 by the University of Southern California
.\" All rights reserved.
@@ -63,9 +63,14 @@ mtrace \- print multicast path from a source to a receiver
] [
.B \-s
] [
+.B \-S
+.I stat_int
+] [
.B \-t
.I ttl
] [
+.B \-v
+] [
.B \-w
.I waittime
]
@@ -105,6 +110,9 @@ detailed below. The two parameters can be distinguished because the
is a unicast address and the
.I group
is a multicast address.
+.PP
+NOTE: For Solaris 2.4/2.5, if the multicast interface is not the default
+interface, the -i option must be used to set the local address.
.SH OPTIONS
.TP 8 8
.BI \-g\ gwy
@@ -118,7 +126,7 @@ to the
.RS 8
.TP 12 12
.I CAUTION!!
-Version 3.3 of
+Versions 3.3 and 3.5 of
.B mrouted
will crash if a trace query is received via a
unicast packet and
@@ -129,7 +137,7 @@ address. Therefore, do not use the
.B \-g
option unless the target
.B mrouted
-has been verified to be newer than 3.3.
+has been verified to be 3.4 or newer than 3.5.
.RE
.TP 8 8
.BI \-i\ addr
@@ -142,7 +150,9 @@ and the response destination.
.TP 8 8
.B \-l
Loop indefinitely printing packet rate and loss statistics for the
-multicast path every 10 seconds.
+multicast path every 10 seconds (see
+.B \-S
+.IR stat_int ).
.TP 8 8
.B \-M
Always send the response using multicast rather than attempting
@@ -169,7 +179,7 @@ The default is 3.
.TP 8 8
.B \-p
Listen passively for multicast responses from traces initiated by
-others (not implemented yet).
+others. This works best when run on a multicast router.
.TP 8 8
.BI \-r\ host
Send the trace response to
@@ -183,6 +193,11 @@ for this purpose (224.0.1.32).
Print a short form output including only the multicast path and not
the packet rate and loss statistics.
.TP 8 8
+.BI \-S\ n
+Change the interval between statistics gathering traces to
+.I n
+seconds (default 10 seconds).
+.TP 8 8
.BI \-t\ ttl
Set the
.I ttl
@@ -190,6 +205,9 @@ Set the
responses. The default is 64, except for local queries to the "all
routers" multicast group which use ttl 1.
.TP 8 8
+.B \-v
+Verbose mode; show hop times on the initial trace and statistics display.
+.TP 8 8
.BI \-w\ n
Set the time to wait for a trace response to
.I n
@@ -492,7 +510,7 @@ are modeled after the unicast
.B traceroute
program written by Van Jacobson.
.SH SEE ALSO
-.BR mrouted (8),
-.BR mrinfo (8),
-.BR map-mbone (8),
+.BR mrouted (8) ,
+.BR mrinfo (8) ,
+.BR map-mbone (8) ,
.BR traceroute (8)
diff --git a/usr.sbin/mtrace/mtrace.c b/usr.sbin/mtrace/mtrace.c
index d7bfe3f1c3a..6f8323ad061 100644
--- a/usr.sbin/mtrace/mtrace.c
+++ b/usr.sbin/mtrace/mtrace.c
@@ -1,4 +1,4 @@
-/* $NetBSD: mtrace.c,v 1.4 1995/10/04 03:47:57 thorpej Exp $ */
+/* $NetBSD: mtrace.c,v 1.5 1995/12/10 10:57:15 mycroft Exp $ */
/*
* mtrace.c
@@ -50,21 +50,27 @@
* license in the accompanying file named "LICENSE".
*/
-#include <sys/filio.h>
+#ifndef lint
+static char rcsid[] =
+ "@(#) $Id: mtrace.c,v 1.2 1995/12/14 01:46:22 deraadt Exp $";
+#endif
+
+#include <netdb.h>
#include <sys/time.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <ctype.h>
#include <memory.h>
-#include <netdb.h>
#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-extern int optind;
-extern char *optarg;
-
+#include <ctype.h>
+#include <sys/ioctl.h>
#include "defs.h"
+#include <arpa/inet.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#ifdef SUNOS5
+#include <sys/systeminfo.h>
+#endif
#define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */
#define DEFAULT_RETRIES 3 /* How many times to try */
@@ -93,6 +99,8 @@ struct resp_buf {
#define ndata u.d
char names[MAXHOPS][40];
+int reset[MAXHOPS]; /* To get around 3.4 bug, ... */
+int swaps[MAXHOPS]; /* To get around 3.6 bug, ... */
int timeout = DEFAULT_TIMEOUT;
int nqueries = DEFAULT_RETRIES;
@@ -100,6 +108,8 @@ int numeric = FALSE;
int debug = 0;
int passive = FALSE;
int multicast = FALSE;
+int statint = 10;
+int verbose = 0;
u_int32_t defgrp; /* Default group if not specified */
u_int32_t query_cast; /* All routers multicast addr */
@@ -112,38 +122,55 @@ u_int32_t dst_netmask; /* netmask to go with qdst */
* Query/response parameters, all initialized to zero and set later
* to default values or from options.
*/
-u_int32_t qsrc = 0;
-u_int32_t qgrp = 0;
-u_int32_t qdst = 0;
-u_char qno = 0;
-u_int32_t raddr = 0;
-int qttl = 0;
-u_char rttl = 0;
-u_int32_t gwy = 0;
+u_int32_t qsrc = 0; /* Source address in the query */
+u_int32_t qgrp = 0; /* Group address in the query */
+u_int32_t qdst = 0; /* Destination (receiver) address in query */
+u_char qno = 0; /* Max number of hops to query */
+u_int32_t raddr = 0; /* Address where response should be sent */
+int qttl = 0; /* TTL for the query packet */
+u_char rttl = 0; /* TTL for the response packet */
+u_int32_t gwy = 0; /* User-supplied last-hop router address */
+u_int32_t tdst = 0; /* Address where trace is sent (last-hop) */
vifi_t numvifs; /* to keep loader happy */
/* (see kern.c) */
-extern void k_join();
-extern void k_leave();
-extern void k_set_ttl();
-extern void exit();
#ifndef SYSV
extern long random();
#endif
extern int errno;
-void
-usage()
-{
-
- printf("\
-Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\
- [-t ttl] [-r resp_dest] [-i if_addr] source [receiver] [group]\n");
- exit(1);
-}
-
-
-char *
+char * inet_name __P((u_int32_t addr));
+u_int32_t host_addr __P((char *name));
+/* u_int is promoted u_char */
+char * proto_type __P((u_int type));
+char * flag_type __P((u_int type));
+
+u_int32_t get_netmask __P((int s, u_int32_t dst));
+int get_ttl __P((struct resp_buf *buf));
+int t_diff __P((u_long a, u_long b));
+u_long fixtime __P((u_long time));
+int send_recv __P((u_int32_t dst, int type, int code,
+ int tries, struct resp_buf *save));
+char * print_host __P((u_int32_t addr));
+char * print_host2 __P((u_int32_t addr1, u_int32_t addr2));
+void print_trace __P((int index, struct resp_buf *buf));
+int what_kind __P((struct resp_buf *buf, char *why));
+char * scale __P((int *hop));
+void stat_line __P((struct tr_resp *r, struct tr_resp *s,
+ int have_next, int *res));
+void fixup_stats __P((struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new));
+int print_stats __P((struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new));
+void check_vif_state __P((void));
+
+int main __P((int argc, char *argv[]));
+
+
+
+char *
inet_name(addr)
u_int32_t addr;
{
@@ -159,46 +186,45 @@ u_int32_t
host_addr(name)
char *name;
{
- struct hostent *e;
- struct in_addr ina;
+ struct hostent *e = (struct hostent *)0;
+ u_int32_t addr;
int i, dots = 3;
char buf[40];
char *ip = name;
char *op = buf;
/*
- * Undo the BSD-ism `127.1' == `127.0.0.1'. We change this to
- * `127.1' == `127.1.0.0'.
+ * Undo BSD's favor -- take fewer than 4 octets as net/subnet address
+ * if the name is all numeric.
*/
-
for (i = sizeof(buf) - 7; i > 0; --i) {
- if (*ip == '.')
- --dots;
- if (*ip == '\0')
- break;
- *op++ = *ip++;
+ if (*ip == '.') --dots;
+ else if (*ip == '\0') break;
+ else if (!isdigit(*ip)) dots = 0; /* Not numeric, don't add zeroes */
+ *op++ = *ip++;
}
for (i = 0; i < dots; ++i) {
- *op++ = '.';
+ *op++ = '.';
*op++ = '0';
}
*op = '\0';
- if (inet_aton(buf, &ina) == 0) {
- if ((e = gethostbyname(name)) == NULL) {
- ina.s_addr = 0;
+ if (dots <= 0) e = gethostbyname(name);
+ if (e) memcpy((char *)&addr, e->h_addr_list[0], e->h_length);
+ else {
+ addr = inet_addr(buf);
+ if (addr == -1) {
+ addr = 0;
printf("Could not parse %s as host name or address\n", name);
- } else
- memcpy((char *)&ina.s_addr, e->h_addr_list[0], e->h_length);
+ }
}
-
- return (ina.s_addr);
+ return addr;
}
char *
proto_type(type)
- u_char type;
+ u_int type;
{
static char buf[80];
@@ -220,7 +246,7 @@ proto_type(type)
char *
flag_type(type)
- u_char type;
+ u_int type;
{
static char buf[80];
@@ -262,21 +288,20 @@ get_netmask(s, dst)
int s;
u_int32_t dst;
{
- char inbuf[8192];
+ unsigned int i;
+ char ifbuf[5000];
struct ifconf ifc;
struct ifreq *ifr;
- int i;
u_int32_t if_addr, if_mask;
u_int32_t retval = 0xFFFFFFFF;
int found = FALSE;
- ifc.ifc_len = sizeof(inbuf);
- ifc.ifc_buf = inbuf;
- if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ ifc.ifc_buf = ifbuf;
+ ifc.ifc_len = sizeof(ifbuf);
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
perror("ioctl (SIOCGIFCONF)");
return (retval);
}
-
for (i = 0; i < ifc.ifc_len; ) {
ifr = (struct ifreq *)((char *)ifc.ifc_req + i);
i += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
@@ -285,12 +310,10 @@ get_netmask(s, dst)
if_mask = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr;
if ((dst & if_mask) == (if_addr & if_mask)) {
retval = if_mask;
- if (lcl_addr == 0)
- lcl_addr = if_addr;
+ if (lcl_addr == 0) lcl_addr = if_addr;
}
}
- if (lcl_addr == if_addr)
- found = TRUE;
+ if (lcl_addr == if_addr) found = TRUE;
}
if (!found && lcl_addr != 0) {
printf("Interface address is not valid\n");
@@ -304,9 +327,9 @@ int
get_ttl(buf)
struct resp_buf *buf;
{
- register rno;
- register struct tr_resp *b;
- register ttl;
+ int rno;
+ struct tr_resp *b;
+ u_int ttl;
if (buf && (rno = buf->len) > 0) {
b = buf->resps + rno - 1;
@@ -314,19 +337,14 @@ get_ttl(buf)
while (--rno > 0) {
--b;
- if (ttl < b->tr_fttl)
- ttl = b->tr_fttl;
- else
- ++ttl;
+ if (ttl < b->tr_fttl) ttl = b->tr_fttl;
+ else ++ttl;
}
ttl += MULTICAST_TTL_INC;
- if (ttl < MULTICAST_TTL1)
- ttl = MULTICAST_TTL1;
- if (ttl > MULTICAST_TTL_MAX)
- ttl = MULTICAST_TTL_MAX;
+ if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1;
+ if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX;
return (ttl);
- } else
- return(MULTICAST_TTL1);
+ } else return(MULTICAST_TTL1);
}
/*
@@ -357,6 +375,17 @@ fixtime(time)
return (time);
}
+/*
+ * Swap bytes for poor little-endian machines that don't byte-swap
+ */
+u_long
+byteswap(v)
+ u_long v;
+{
+ return ((v << 24) | ((v & 0xff00) << 8) |
+ ((v >> 8) & 0xff00) | (v >> 24));
+}
+
int
send_recv(dst, type, code, tries, save)
u_int32_t dst;
@@ -382,10 +411,8 @@ send_recv(dst, type, code, tries, save)
group = htonl(MROUTED_LEVEL);
datalen = 0;
}
- if (IN_MULTICAST(ntohl(dst)))
- local = lcl_addr;
- else
- local = INADDR_ANY;
+ if (IN_MULTICAST(ntohl(dst))) local = lcl_addr;
+ else local = INADDR_ANY;
/*
* If the reply address was not explictly specified, start off
@@ -399,6 +426,8 @@ send_recv(dst, type, code, tries, save)
query->tr_raddr = raddr ? raddr : multicast ? resp_cast : lcl_addr;
query->tr_rttl = rttl ? rttl :
IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL;
+ query->tr_src = qsrc;
+ query->tr_dst = qdst;
for (i = tries ; i > 0; --i) {
if (tries == nqueries && raddr == 0) {
@@ -417,7 +446,11 @@ send_recv(dst, type, code, tries, save)
* Change the qid for each request sent to avoid being confused
* by duplicate responses
*/
+#ifdef SYSV
+ query->tr_qid = ((u_int32_t)lrand48() >> 8);
+#else
query->tr_qid = ((u_int32_t)random() >> 8);
+#endif
/*
* Set timer to calculate delays, then send query
@@ -434,17 +467,14 @@ send_recv(dst, type, code, tries, save)
gettimeofday(&tv, 0);
tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec;
tv.tv_usec = tq.tv_usec - tv.tv_usec;
- if (tv.tv_usec < 0)
- tv.tv_usec += 1000000L, --tv.tv_sec;
- if (tv.tv_sec < 0)
- tv.tv_sec = tv.tv_usec = 0;
+ if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec;
+ if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0;
count = select(igmp_socket + 1, &fds, (fd_set *)0, (fd_set *)0,
&tv);
if (count < 0) {
- if (errno != EINTR)
- perror("select");
+ if (errno != EINTR) perror("select");
continue;
} else if (count == 0) {
printf("* ");
@@ -457,8 +487,7 @@ send_recv(dst, type, code, tries, save)
0, (struct sockaddr *)0, &dummy);
if (recvlen <= 0) {
- if (recvlen && errno != EINTR)
- perror("recvfrom");
+ if (recvlen && errno != EINTR) perror("recvfrom");
continue;
}
@@ -492,17 +521,32 @@ send_recv(dst, type, code, tries, save)
switch (igmp->igmp_type) {
case IGMP_DVMRP:
- if (igmp->igmp_code != DVMRP_NEIGHBORS2)
- continue;
- if (ip->ip_src.s_addr != dst)
- continue;
+ if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue;
len = igmpdatalen;
+ /*
+ * Accept DVMRP_NEIGHBORS2 response if it comes from the
+ * address queried or if that address is one of the local
+ * addresses in the response.
+ */
+ if (ip->ip_src.s_addr != dst) {
+ u_int32_t *p = (u_int32_t *)(igmp + 1);
+ u_int32_t *ep = p + (len >> 2);
+ while (p < ep) {
+ u_int32_t laddr = *p++;
+ int n = ntohl(*p++) & 0xFF;
+ if (laddr == dst) {
+ ep = p + 1; /* ensure p < ep after loop */
+ break;
+ }
+ p += n;
+ }
+ if (p >= ep) continue;
+ }
break;
- case IGMP_MTRACE_QUERY: /* For backward compatibility with 3.3 */
+ case IGMP_MTRACE_QUERY: /* For backward compatibility with 3.3 */
case IGMP_MTRACE_REPLY:
- if (igmpdatalen <= QLEN)
- continue;
+ if (igmpdatalen <= QLEN) continue;
if ((igmpdatalen - QLEN)%RLEN) {
printf("packet with incorrect datalen\n");
continue;
@@ -512,18 +556,15 @@ send_recv(dst, type, code, tries, save)
* Ignore responses that don't match query.
*/
rquery = (struct tr_query *)(igmp + 1);
- if (rquery->tr_qid != query->tr_qid)
- continue;
- if (rquery->tr_src != qsrc)
- continue;
- if (rquery->tr_dst != qdst)
- continue;
+ if (rquery->tr_qid != query->tr_qid) continue;
+ if (rquery->tr_src != qsrc) continue;
+ if (rquery->tr_dst != qdst) continue;
len = (igmpdatalen - QLEN)/RLEN;
/*
* Ignore trace queries passing through this node when
* mtrace is run on an mrouter that is in the path
- * (needed only because IGMP_MTRACE is accepted above
+ * (needed only because IGMP_MTRACE_QUERY is accepted above
* for backward compatibility with multicast release 3.3).
*/
if (igmp->igmp_type == IGMP_MTRACE_QUERY) {
@@ -570,19 +611,155 @@ send_recv(dst, type, code, tries, save)
return (0);
}
+/*
+ * Most of this code is duplicated elsewhere. I'm not sure if
+ * the duplication is absolutely required or not.
+ *
+ * Ideally, this would keep track of ongoing statistics
+ * collection and print out statistics. (& keep track
+ * of h-b-h traces and only print the longest) For now,
+ * it just snoops on what traces it can.
+ */
+void
+passive_mode()
+{
+ struct timeval tr;
+ struct ip *ip;
+ struct igmp *igmp;
+ struct tr_resp *r;
+ int ipdatalen, iphdrlen, igmpdatalen;
+ int len, recvlen, dummy = 0;
+ u_int32_t smask;
+
+ init_igmp();
+
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, INADDR_ANY);
+ } else k_join(htonl(0xE0000120), INADDR_ANY);
+
+ while (1) {
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, (struct sockaddr *)0, &dummy);
+ gettimeofday(&tr,0);
+
+ if (recvlen <= 0) {
+ if (recvlen && errno != EINTR) perror("recvfrom");
+ continue;
+ }
+
+ if (recvlen < sizeof(struct ip)) {
+ fprintf(stderr,
+ "packet too short (%u bytes) for IP header", recvlen);
+ continue;
+ }
+ ip = (struct ip *) recv_buf;
+ if (ip->ip_p == 0) /* ignore cache creation requests */
+ continue;
+
+ iphdrlen = ip->ip_hl << 2;
+ ipdatalen = ip->ip_len;
+ if (iphdrlen + ipdatalen != recvlen) {
+ fprintf(stderr,
+ "packet shorter (%u bytes) than hdr+data len (%u+%u)\n",
+ recvlen, iphdrlen, ipdatalen);
+ continue;
+ }
+
+ igmp = (struct igmp *) (recv_buf + iphdrlen);
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ fprintf(stderr,
+ "IP data field too short (%u bytes) for IGMP from %s\n",
+ ipdatalen, inet_fmt(ip->ip_src.s_addr, s1));
+ continue;
+ }
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_MTRACE_QUERY: /* For backward compatibility with 3.3 */
+ case IGMP_MTRACE_REPLY:
+ if (igmpdatalen < QLEN) continue;
+ if ((igmpdatalen - QLEN)%RLEN) {
+ printf("packet with incorrect datalen\n");
+ continue;
+ }
+
+ len = (igmpdatalen - QLEN)/RLEN;
+
+ break;
+
+ default:
+ continue;
+ }
+
+ base.qtime = ((tr.tv_sec + JAN_1970) << 16) +
+ (tr.tv_usec << 10) / 15625;
+ base.rtime = ((tr.tv_sec + JAN_1970) << 16) +
+ (tr.tv_usec << 10) / 15625;
+ base.len = len;
+ bcopy((char *)igmp, (char *)&base.igmp, ipdatalen);
+ /*
+ * If the user specified which traces to monitor,
+ * only accept traces that correspond to the
+ * request
+ */
+ if ((qsrc != 0 && qsrc != base.qhdr.tr_src) ||
+ (qdst != 0 && qdst != base.qhdr.tr_dst) ||
+ (qgrp != 0 && qgrp != igmp->igmp_group.s_addr))
+ continue;
+
+ printf("Mtrace from %s to %s via group %s (mxhop=%d)\n",
+ inet_fmt(base.qhdr.tr_dst, s1), inet_fmt(base.qhdr.tr_src, s2),
+ inet_fmt(igmp->igmp_group.s_addr, s3), igmp->igmp_code);
+ if (len == 0)
+ continue;
+ printf(" 0 ");
+ print_host(base.qhdr.tr_dst);
+ printf("\n");
+ print_trace(1, &base);
+ r = base.resps + base.len - 1;
+ VAL_TO_MASK(smask, r->tr_smask);
+ if ((r->tr_inaddr & smask) == (base.qhdr.tr_src & smask)) {
+ printf("%3d ", -(base.len+1));
+ print_host(base.qhdr.tr_src);
+ printf("\n");
+ } else if (r->tr_rmtaddr != 0) {
+ printf("%3d ", -(base.len+1));
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace"
+ : "is the next hop");
+ }
+ printf("\n");
+ }
+}
char *
print_host(addr)
u_int32_t addr;
{
+ return print_host2(addr, 0);
+}
+
+/*
+ * On some routers, one interface has a name and the other doesn't.
+ * We always print the address of the outgoing interface, but can
+ * sometimes get the name from the incoming interface. This might be
+ * confusing but should be slightly more helpful than just a "?".
+ */
+char *
+print_host2(addr1, addr2)
+ u_int32_t addr1, addr2;
+{
char *name;
if (numeric) {
- printf("%s", inet_fmt(addr, s1));
+ printf("%s", inet_fmt(addr1, s1));
return ("");
}
- name = inet_name(addr);
- printf("%s (%s)", name, inet_fmt(addr, s1));
+ name = inet_name(addr1);
+ if (*name == '?' && *(name + 1) == '\0' && addr2 != 0)
+ name = inet_name(addr2);
+ printf("%s (%s)", name, inet_fmt(addr1, s1));
return (name);
}
@@ -597,16 +774,22 @@ print_trace(index, buf)
struct tr_resp *r;
char *name;
int i;
+ int hop;
+ char *ms;
i = abs(index);
r = buf->resps + i - 1;
for (; i <= buf->len; ++i, ++r) {
if (index > 0) printf("%3d ", -i);
- name = print_host(r->tr_outaddr);
- printf(" %s thresh^ %d %d ms %s\n", proto_type(r->tr_rproto),
- r->tr_fttl, t_diff(fixtime(ntohl(r->tr_qarr)), buf->qtime),
- flag_type(r->tr_rflags));
+ name = print_host2(r->tr_outaddr, r->tr_inaddr);
+ printf(" %s thresh^ %d", proto_type(r->tr_rproto), r->tr_fttl);
+ if (verbose) {
+ hop = t_diff(fixtime(ntohl(r->tr_qarr)), buf->qtime);
+ ms = scale(&hop);
+ printf(" %d%s", hop, ms);
+ }
+ printf(" %s\n", flag_type(r->tr_rflags));
memcpy(names[i-1], name, sizeof(names[0]) - 1);
names[i-1][sizeof(names[0])-1] = '\0';
}
@@ -615,44 +798,63 @@ print_trace(index, buf)
/*
* See what kind of router is the next hop
*/
-void
-what_kind(buf)
+int
+what_kind(buf, why)
struct resp_buf *buf;
+ char *why;
{
u_int32_t smask;
- int recvlen;
+ int retval;
int hops = buf->len;
struct tr_resp *r = buf->resps + hops - 1;
u_int32_t next = r->tr_rmtaddr;
- recvlen = send_recv(next, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0]);
+ retval = send_recv(next, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0]);
print_host(next);
- if (recvlen) {
+ if (retval) {
u_int32_t version = ntohl(incr[0].igmp.igmp_group.s_addr);
u_int32_t *p = (u_int32_t *)incr[0].ndata;
u_int32_t *ep = p + (incr[0].len >> 2);
- printf(" [%s%d.%d] didn't respond\n",
- (version == 1) ? "proteon/mrouted " :
- ((version & 0xff) == 2) ? "mrouted " :
- ((version & 0xff) == 3) ? "mrouted " :
- ((version & 0xff) == 4) ? "mrouted " :
- ((version & 0xff) == 10) ? "cisco " : "",
- version & 0xff, (version >> 8) & 0xff);
+ char *type = "";
+ retval = 0;
+ switch (version & 0xFF) {
+ case 1:
+ type = "proteon/mrouted ";
+ retval = 1;
+ break;
+
+ case 2:
+ case 3:
+ if (((version >> 8) & 0xFF) < 3) retval = 1;
+ /* Fall through */
+ case 4:
+ type = "mrouted ";
+ break;
+
+ case 10:
+ type = "cisco ";
+ }
+ printf(" [%s%d.%d] %s\n",
+ type, version & 0xFF, (version >> 8) & 0xFF,
+ why);
VAL_TO_MASK(smask, r->tr_smask);
while (p < ep) {
- register u_int32_t laddr = *p++;
- register int n = ntohl(*p++) & 0xFF;
- if ((laddr & smask) == (qsrc & smask)) {
+ u_int32_t laddr = *p++;
+ int flags = (ntohl(*p) & 0xFF00) >> 8;
+ int n = ntohl(*p++) & 0xFF;
+ if (!(flags & (DVMRP_NF_DOWN | DVMRP_NF_DISABLED)) &&
+ (laddr & smask) == (qsrc & smask)) {
printf("%3d ", -(hops+2));
print_host(qsrc);
printf("\n");
- break;
+ return 1;
}
p += n;
}
- return;
+ return retval;
}
- printf(" didn't respond\n");
+ printf(" %s\n", why);
+ return 0;
}
@@ -675,133 +877,199 @@ scale(hop)
#define OUTS 2
#define BOTH 3
void
-stat_line(r, s, have_next)
+stat_line(r, s, have_next, rst)
struct tr_resp *r, *s;
int have_next;
+ int *rst;
{
- register timediff = (fixtime(ntohl(s->tr_qarr)) -
+ int timediff = (fixtime(ntohl(s->tr_qarr)) -
fixtime(ntohl(r->tr_qarr))) >> 16;
- register v_lost, v_pct;
- register g_lost, g_pct;
- register v_out = ntohl(s->tr_vifout) - ntohl(r->tr_vifout);
- register g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt);
- register v_pps, g_pps;
+ int v_lost, v_pct;
+ int g_lost, g_pct;
+ int v_out = ntohl(s->tr_vifout) - ntohl(r->tr_vifout);
+ int g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt);
+ int v_pps, g_pps;
char v_str[8], g_str[8];
- register have = NEITHER;
+ int have = NEITHER;
+ int res = *rst;
- if (timediff == 0)
- timediff = 1;
+ if (timediff == 0) timediff = 1;
v_pps = v_out / timediff;
g_pps = g_out / timediff;
- if (v_out || s->tr_vifout != 0xFFFFFFFF)
- have |= OUTS;
+ if (v_out && (s->tr_vifout != 0xFFFFFFFF && s->tr_vifout != 0) ||
+ (r->tr_vifout != 0xFFFFFFFF && r->tr_vifout != 0))
+ have |= OUTS;
if (have_next) {
- --r, --s;
- if (s->tr_vifin != 0xFFFFFFFF || r->tr_vifin != 0xFFFFFFFF)
+ --r, --s, --rst;
+ if ((s->tr_vifin != 0xFFFFFFFF && s->tr_vifin != 0) ||
+ (r->tr_vifin != 0xFFFFFFFF && r->tr_vifin != 0))
have |= INS;
+ if (*rst)
+ res = 1;
}
switch (have) {
case BOTH:
v_lost = v_out - (ntohl(s->tr_vifin) - ntohl(r->tr_vifin));
- if (v_out)
- v_pct = (v_lost * 100 + (v_out >> 1)) / v_out;
- else
- v_pct = 0;
+ if (v_out) v_pct = (v_lost * 100 + (v_out >> 1)) / v_out;
+ else v_pct = 0;
if (-100 < v_pct && v_pct < 101 && v_out > 10)
- sprintf(v_str, "%3d", v_pct);
- else
- memcpy(v_str, " --", 4);
+ sprintf(v_str, "%3d", v_pct);
+ else memcpy(v_str, " --", 4);
g_lost = g_out - (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
- if (g_out)
- g_pct = (g_lost * 100 + (g_out >> 1))/ g_out;
- else
- g_pct = 0;
+ if (g_out) g_pct = (g_lost * 100 + (g_out >> 1))/ g_out;
+ else g_pct = 0;
if (-100 < g_pct && g_pct < 101 && g_out > 10)
- sprintf(g_str, "%3d", g_pct);
+ sprintf(g_str, "%3d", g_pct);
+ else memcpy(g_str, " --", 4);
+
+ printf("%6d/%-5d=%s%%%4d pps",
+ v_lost, v_out, v_str, v_pps);
+ if (res)
+ printf("\n");
else
- memcpy(g_str, " --", 4);
-
- printf("%6d/%-5d=%s%%%4d pps%6d/%-5d=%s%%%4d pps\n",
- v_lost, v_out, v_str, v_pps, g_lost, g_out, g_str, g_pps);
- if (debug > 2) {
- printf("\t\t\t\tv_in: %ld ", ntohl(s->tr_vifin));
- printf("v_out: %ld ", ntohl(s->tr_vifout));
- printf("pkts: %ld\n", ntohl(s->tr_pktcnt));
- printf("\t\t\t\tv_in: %ld ", ntohl(r->tr_vifin));
- printf("v_out: %ld ", ntohl(r->tr_vifout));
- printf("pkts: %ld\n", ntohl(r->tr_pktcnt));
- printf("\t\t\t\tv_in: %ld ",ntohl(s->tr_vifin)-ntohl(r->tr_vifin));
- printf("v_out: %ld ", ntohl(s->tr_vifout) - ntohl(r->tr_vifout));
- printf("pkts: %ld ", ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
- printf("time: %d\n", timediff);
- }
+ printf("%6d/%-5d=%s%%%4d pps\n",
+ g_lost, g_out, g_str, g_pps);
break;
case INS:
- v_out = (ntohl(s->tr_vifin) - ntohl(r->tr_vifin));
- g_out = (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
+ v_out = ntohl(s->tr_vifin) - ntohl(r->tr_vifin);
v_pps = v_out / timediff;
- g_pps = g_out / timediff;
/* Fall through */
case OUTS:
- printf(" %-5d %4d pps %-5d %4d pps\n",
- v_out, v_pps, g_out, g_pps);
+ printf(" %-5d %4d pps",
+ v_out, v_pps);
+ if (res)
+ printf("\n");
+ else
+ printf(" %-5d %4d pps\n",
+ g_out, g_pps);
break;
case NEITHER:
printf("\n");
break;
}
+
+ if (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", ntohl(s->tr_vifin));
+ printf("v_out: %ld ", ntohl(s->tr_vifout));
+ printf("pkts: %ld\n", ntohl(s->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", ntohl(r->tr_vifin));
+ printf("v_out: %ld ", ntohl(r->tr_vifout));
+ printf("pkts: %ld\n", ntohl(r->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ",ntohl(s->tr_vifin)-ntohl(r->tr_vifin));
+ printf("v_out: %ld ", ntohl(s->tr_vifout) - ntohl(r->tr_vifout));
+ printf("pkts: %ld ", ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
+ printf("time: %d\n", timediff);
+ printf("\t\t\t\tres: %d\n", res);
+ }
}
/*
- * A fixup to check if any pktcnt has been reset.
+ * A fixup to check if any pktcnt has been reset, and to fix the
+ * byteorder bugs in mrouted 3.6 on little-endian machines.
*/
void
-fixup_stats(base, new)
- struct resp_buf *base, *new;
+fixup_stats(base, prev, new)
+ struct resp_buf *base, *prev, *new;
{
- register rno = base->len;
- register struct tr_resp *b = base->resps + rno;
- register struct tr_resp *n = new->resps + rno;
-
- while (--rno >= 0)
- if (ntohl((--n)->tr_pktcnt) < ntohl((--b)->tr_pktcnt))
- break;
+ int rno = base->len;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int *r = reset + rno;
+ int *s = swaps + rno;
+ int res;
+
+ /* Check for byte-swappers */
+ while (--rno >= 0) {
+ --n; --p; --b; --s;
+ if (*s || abs(ntohl(n->tr_vifout) - ntohl(p->tr_vifout)) > 100000) {
+ /* This host sends byteswapped reports; swap 'em */
+ if (!*s) {
+ *s = 1;
+ b->tr_qarr = byteswap(b->tr_qarr);
+ b->tr_vifin = byteswap(b->tr_vifin);
+ b->tr_vifout = byteswap(b->tr_vifout);
+ b->tr_pktcnt = byteswap(b->tr_pktcnt);
+ }
- if (rno < 0)
- return;
+ n->tr_qarr = byteswap(n->tr_qarr);
+ n->tr_vifin = byteswap(n->tr_vifin);
+ n->tr_vifout = byteswap(n->tr_vifout);
+ n->tr_pktcnt = byteswap(n->tr_pktcnt);
+ }
+ }
rno = base->len;
b = base->resps + rno;
+ p = prev->resps + rno;
n = new->resps + rno;
- while (--rno >= 0)
- (--b)->tr_pktcnt = (--n)->tr_pktcnt;
+ while (--rno >= 0) {
+ --n; --p; --b; --r;
+ res = ((ntohl(n->tr_pktcnt) < ntohl(b->tr_pktcnt)) ||
+ (ntohl(n->tr_pktcnt) < ntohl(p->tr_pktcnt)));
+ if (debug > 2)
+ printf("\t\tr=%d, res=%d\n", *r, res);
+ if (*r) {
+ if (res || *r > 1) {
+ /*
+ * This router appears to be a 3.4 with that nasty ol'
+ * neighbor version bug, which causes it to constantly
+ * reset. Just nuke the statistics for this node, and
+ * don't even bother giving it the benefit of the
+ * doubt from now on.
+ */
+ p->tr_pktcnt = b->tr_pktcnt = n->tr_pktcnt;
+ *r++;
+ } else {
+ /*
+ * This is simply the situation that the original
+ * fixup_stats was meant to deal with -- that a
+ * 3.3 or 3.4 router deleted a cache entry while
+ * traffic was still active.
+ */
+ *r = 0;
+ break;
+ }
+ } else
+ *r = res;
+ }
+
+ if (rno < 0) return;
+
+ rno = base->len;
+ b = base->resps + rno;
+ p = prev->resps + rno;
+
+ while (--rno >= 0) (--b)->tr_pktcnt = (--p)->tr_pktcnt;
}
/*
* Print responses with statistics for forward path (from src to dst)
*/
-void
+int
print_stats(base, prev, new)
struct resp_buf *base, *prev, *new;
{
int rtt, hop;
- register char *ms;
- register u_int32_t smask;
- register rno = base->len - 1;
- register struct tr_resp *b = base->resps + rno;
- register struct tr_resp *p = prev->resps + rno;
- register struct tr_resp *n = new->resps + rno;
- register u_long resptime = new->rtime;
- register u_long qarrtime = fixtime(ntohl(n->tr_qarr));
- register ttl = n->tr_fttl;
+ char *ms;
+ u_int32_t smask;
+ int rno = base->len - 1;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int *r = reset + rno;
+ u_long resptime = new->rtime;
+ u_long qarrtime = fixtime(ntohl(n->tr_qarr));
+ u_int ttl = n->tr_fttl;
+ int first = (base == prev);
VAL_TO_MASK(smask, b->tr_smask);
printf(" Source Response Dest");
@@ -811,12 +1079,14 @@ print_stats(base, prev, new)
inet_fmt(base->qhdr.tr_raddr, s2), inet_fmt(qsrc, s1));
rtt = t_diff(resptime, new->qtime);
ms = scale(&rtt);
- printf(" | __/ rtt%5d%s Lost/Sent = Pct Rate To %s\n",
- rtt, ms, inet_fmt(qgrp, s2));
- hop = t_diff(resptime, qarrtime);
- ms = scale(&hop);
- printf(" v / hop%5d%s", hop, ms);
- printf(" --------------------- --------------------\n");
+ printf(" %c __/ rtt%5d%s Lost/Sent = Pct Rate To %s\n",
+ first ? 'v' : '|', rtt, ms, inet_fmt(qgrp, s2));
+ if (!first) {
+ hop = t_diff(resptime, qarrtime);
+ ms = scale(&hop);
+ printf(" v / hop%5d%s", hop, ms);
+ printf(" --------------------- --------------------\n");
+ }
if (debug > 2) {
printf("\t\t\t\tv_in: %ld ", ntohl(n->tr_vifin));
printf("v_out: %ld ", ntohl(n->tr_vifout));
@@ -827,13 +1097,13 @@ print_stats(base, prev, new)
printf("\t\t\t\tv_in: %ld ", ntohl(n->tr_vifin) - ntohl(b->tr_vifin));
printf("v_out: %ld ", ntohl(n->tr_vifout) - ntohl(b->tr_vifout));
printf("pkts: %ld\n", ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt));
+ printf("\t\t\t\treset: %d\n", *r);
}
while (TRUE) {
- if ((n->tr_inaddr != b->tr_inaddr) || (n->tr_inaddr != b->tr_inaddr)) {
- printf("Route changed, start again.\n");
- exit(1);
- }
+ if ((n->tr_inaddr != b->tr_inaddr) || (n->tr_inaddr != b->tr_inaddr))
+ return 1; /* Route changed */
+
if ((n->tr_inaddr != n->tr_outaddr))
printf("%-15s\n", inet_fmt(n->tr_inaddr, s1));
printf("%-15s %-14s %s\n", inet_fmt(n->tr_outaddr, s1), names[rno],
@@ -841,36 +1111,33 @@ print_stats(base, prev, new)
if (rno-- < 1) break;
- printf(" | ^ ttl%5d ", ttl);
- if (prev == new)
- printf("\n");
- else
- stat_line(p, n, TRUE);
- resptime = qarrtime;
- qarrtime = fixtime(ntohl((n-1)->tr_qarr));
- hop = t_diff(resptime, qarrtime);
- ms = scale(&hop);
- printf(" v | hop%5d%s", hop, ms);
- stat_line(b, n, TRUE);
+ printf(" %c ^ ttl%5d ", first ? 'v' : '|', ttl);
+ stat_line(p, n, TRUE, r);
+ if (!first) {
+ resptime = qarrtime;
+ qarrtime = fixtime(ntohl((n-1)->tr_qarr));
+ hop = t_diff(resptime, qarrtime);
+ ms = scale(&hop);
+ printf(" v | hop%5d%s", hop, ms);
+ stat_line(b, n, TRUE, r);
+ }
- --b, --p, --n;
- if (ttl < n->tr_fttl)
- ttl = n->tr_fttl;
- else
- ++ttl;
+ --b, --p, --n, --r;
+ if (ttl < n->tr_fttl) ttl = n->tr_fttl;
+ else ++ttl;
}
- printf(" | \\__ ttl%5d ", ttl);
- if (prev == new)
- printf("\n");
- else
- stat_line(p, n, FALSE);
- hop = t_diff(qarrtime, new->qtime);
- ms = scale(&hop);
- printf(" v \\ hop%5d%s", hop, ms);
- stat_line(b, n, FALSE);
+ printf(" %c \\__ ttl%5d ", first ? 'v' : '|', ttl);
+ stat_line(p, n, FALSE, r);
+ if (!first) {
+ hop = t_diff(qarrtime, new->qtime);
+ ms = scale(&hop);
+ printf(" v \\ hop%5d%s", hop, ms);
+ stat_line(b, n, FALSE, r);
+ }
printf("%-15s %s\n", inet_fmt(qdst, s1), inet_fmt(lcl_addr, s2));
printf(" Receiver Query Source\n\n");
+ return 0;
}
@@ -889,147 +1156,155 @@ char *argv[];
int recvlen;
struct timeval tv;
struct resp_buf *prev, *new;
- struct tr_query *query;
struct tr_resp *r;
u_int32_t smask;
int rno;
- int hops, tries;
+ int hops, nexthop, tries;
+ u_int32_t lastout = 0;
int numstats = 1;
int waittime;
int seed;
- int ch;
if (geteuid() != 0) {
fprintf(stderr, "mtrace: must be root\n");
exit(1);
}
- while ((ch = getopt(argc, argv, "d:g:i:lMm:npq:r:st:w:")) != -1) {
- switch (ch) {
- case 'd': /* Unlisted debug print option */
- if (!isdigit(*optarg))
- usage();
- debug = atoi(optarg);
- if (debug < 0)
- debug = 0;
- else if (debug > 3)
- debug = 3;
- break;
-
- case 'M': /* Use multicast for reponse */
- multicast = TRUE;
- break;
-
- case 'l': /* Loop updating stats indefinitely */
- numstats = 3153600;
- break;
-
- case 'n': /* Don't reverse map host addresses */
- numeric = TRUE;
- break;
-
- case 'p': /* Passive listen for traces */
- passive = TRUE;
- break;
-
- case 's': /* Short form, don't wait for stats */
- numstats = 0;
- break;
-
- case 'w': /* Time to wait for packet arrival */
- if (!isdigit(*optarg))
- usage();
- timeout = atoi(optarg);
- if (timeout < 1)
- timeout = 1;
- break;
-
- case 'm': /* Max number of hops to trace */
- if (!isdigit(*optarg))
- usage();
- qno = atoi(optarg);
- if (qno > MAXHOPS)
- qno = MAXHOPS;
- else if (qno < 1)
- qno = 0;
- break;
-
- case 'q': /* Number of query retries */
- if (!isdigit(*optarg))
- usage();
- nqueries = atoi(optarg);
- if (nqueries < 1)
- nqueries = 1;
- break;
-
- case 'g': /* Last-hop gateway (dest of query) */
- if ((gwy = host_addr(optarg)) == 0)
- usage();
- break;
-
- case 't': /* TTL for query packet */
- if (!isdigit(*optarg))
- usage();
- qttl = atoi(optarg);
- if (qttl < 1)
- qttl = 1;
- rttl = qttl;
- break;
-
- case 'r': /* Dest for response packet */
- if ((raddr = host_addr(optarg)) == 0)
- usage();
- break;
-
- case 'i': /* Local interface address */
- if ((lcl_addr = host_addr(optarg)) == 0)
- usage();
- break;
-
- default:
- usage();
- } /* switch */
- } /* while */
- argv += optind;
- argc -= optind;
-
- switch (argc) {
- case 3: /* Path via group */
- if ((qgrp = host_addr(argv[2])) == 0)
- usage();
- /* FALLTHROUGH */
- case 2: /* dest of path */
- if ((qdst = host_addr(argv[1])) == 0)
- usage();
- /* FALLTHROUGH */
- case 1: /* source of path */
- if ((qsrc = host_addr(argv[0])) == 0 || IN_MULTICAST(ntohl(qsrc)))
- usage();
- break;
+ argv++, argc--;
+ if (argc == 0) goto usage;
+
+ while (argc > 0 && *argv[0] == '-') {
+ char *p = *argv++; argc--;
+ p++;
+ do {
+ char c = *p++;
+ char *arg = (char *) 0;
+ if (isdigit(*p)) {
+ arg = p;
+ p = "";
+ } else if (argc > 0) arg = argv[0];
+ switch (c) {
+ case 'd': /* Unlisted debug print option */
+ if (arg && isdigit(*arg)) {
+ debug = atoi(arg);
+ if (debug < 0) debug = 0;
+ if (debug > 3) debug = 3;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'M': /* Use multicast for reponse */
+ multicast = TRUE;
+ break;
+ case 'l': /* Loop updating stats indefinitely */
+ numstats = 3153600;
+ break;
+ case 'n': /* Don't reverse map host addresses */
+ numeric = TRUE;
+ break;
+ case 'p': /* Passive listen for traces */
+ passive = TRUE;
+ break;
+ case 'v': /* Verbosity */
+ verbose = TRUE;
+ break;
+ case 's': /* Short form, don't wait for stats */
+ numstats = 0;
+ break;
+ case 'w': /* Time to wait for packet arrival */
+ if (arg && isdigit(*arg)) {
+ timeout = atoi(arg);
+ if (timeout < 1) timeout = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'm': /* Max number of hops to trace */
+ if (arg && isdigit(*arg)) {
+ qno = atoi(arg);
+ if (qno > MAXHOPS) qno = MAXHOPS;
+ else if (qno < 1) qno = 0;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'q': /* Number of query retries */
+ if (arg && isdigit(*arg)) {
+ nqueries = atoi(arg);
+ if (nqueries < 1) nqueries = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'g': /* Last-hop gateway (dest of query) */
+ if (arg && (gwy = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 't': /* TTL for query packet */
+ if (arg && isdigit(*arg)) {
+ qttl = atoi(arg);
+ if (qttl < 1) qttl = 1;
+ rttl = qttl;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'r': /* Dest for response packet */
+ if (arg && (raddr = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'i': /* Local interface address */
+ if (arg && (lcl_addr = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'S': /* Stat accumulation interval */
+ if (arg && isdigit(*arg)) {
+ statint = atoi(arg);
+ if (statint < 1) statint = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ default:
+ goto usage;
+ }
+ } while (*p);
+ }
- default:
- usage();
+ if (argc > 0 && (qsrc = host_addr(argv[0]))) { /* Source of path */
+ if (IN_MULTICAST(ntohl(qsrc))) goto usage;
+ argv++, argc--;
+ if (argc > 0 && (qdst = host_addr(argv[0]))) { /* Dest of path */
+ argv++, argc--;
+ if (argc > 0 && (qgrp = host_addr(argv[0]))) { /* Path via group */
+ argv++, argc--;
+ }
+ if (IN_MULTICAST(ntohl(qdst))) {
+ u_int32_t temp = qdst;
+ qdst = qgrp;
+ qgrp = temp;
+ if (IN_MULTICAST(ntohl(qdst))) goto usage;
+ } else if (qgrp && !IN_MULTICAST(ntohl(qgrp))) goto usage;
+ }
}
- /*
- * If argc is > 1 and the second argument is a multicast address,
- * assume that the second argument is actually qgrp and the third
- * (if any) is qdst; in this case, the third argument is not allowed
- * to be a multicast address.
- */
- if (argc > 1) {
- if (IN_MULTICAST(ntohl(qdst))) {
- u_int32_t temp = qdst;
- qdst = qgrp;
- qgrp = temp;
- if (IN_MULTICAST(ntohl(qdst)))
- usage();
- } else if (qgrp != 0 && !IN_MULTICAST(ntohl(qgrp)))
- usage();
+ if (passive) {
+ passive_mode();
+ return(0);
}
- if (qsrc == 0)
- usage();
+ if (argc > 0 || qsrc == 0) {
+usage: printf("\
+Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\
+ [-S statint] [-t ttl] [-r resp_dest] [-i if_addr] source [receiver] [group]\n");
+ exit(1);
+ }
init_igmp();
@@ -1040,8 +1315,7 @@ char *argv[];
defgrp = htonl(0xE0020001); /* MBone Audio (224.2.0.1) */
query_cast = htonl(0xE0000002); /* All routers multicast addr */
resp_cast = htonl(0xE0000120); /* Mtrace response multicast addr */
- if (qgrp == 0)
- qgrp = defgrp;
+ if (qgrp == 0) qgrp = defgrp;
/*
* Get default local address for multicasts to use in setting defaults.
@@ -1060,41 +1334,67 @@ char *argv[];
exit(-1);
}
+#ifdef SUNOS5
/*
- * Default destination for path to be queried is the local host.
+ * SunOS 5.X prior to SunOS 2.6, getsockname returns 0 for udp socket.
+ * This call to sysinfo will return the hostname.
+ * If the default multicast interfface (set with the route
+ * for 224.0.0.0) is not the same as the hostname,
+ * mtrace -i [if_addr] will have to be used.
*/
- if (qdst == 0)
- qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
+ if (addr.sin_addr.s_addr == 0) {
+ char myhostname[MAXHOSTNAMELEN];
+ struct hostent *hp;
+ int error;
+
+ error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
+ if (error == -1) {
+ perror("Getting my hostname");
+ exit(-1);
+ }
+
+ hp = gethostbyname(myhostname);
+ if (hp == NULL || hp->h_addrtype != AF_INET ||
+ hp->h_length != sizeof(addr.sin_addr)) {
+ perror("Finding IP address for my hostname");
+ exit(-1);
+ }
+
+ memcpy((char *)&addr.sin_addr.s_addr, hp->h_addr, hp->h_length);
+ }
+#endif
/*
- * If the destination is on the local net, the last-hop router can
- * be found by multicast to the all-routers multicast group.
- * Otherwise, use the group address that is the subject of the
- * query since by definition the last hop router will be a member.
- * Set default TTLs for local remote multicasts.
+ * Default destination for path to be queried is the local host.
*/
+ if (qdst == 0) qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
dst_netmask = get_netmask(udp, qdst);
close(udp);
- if (lcl_addr == 0)
- lcl_addr = addr.sin_addr.s_addr;
- if (gwy == 0)
- if ((qdst & dst_netmask) == (lcl_addr & dst_netmask))
- gwy = query_cast;
- else
- gwy = qgrp;
+ if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr;
- if (IN_MULTICAST(ntohl(gwy))) {
- k_set_loop(1); /* If I am running on a router, I need to hear this */
- if (gwy == query_cast)
- k_set_ttl(qttl ? qttl : 1);
- else
- k_set_ttl(qttl ? qttl : MULTICAST_TTL1);
- } else
- if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0]))
- if (ntohl(incr[0].igmp.igmp_group.s_addr) == 0x0303) {
- printf("Don't use -g to address an mrouted 3.3, it might crash\n");
+ /*
+ * Initialize the seed for random query identifiers.
+ */
+ gettimeofday(&tv, 0);
+ seed = tv.tv_usec ^ lcl_addr;
+#ifdef SYSV
+ srand48(seed);
+#else
+ srandom(seed);
+#endif
+
+ /*
+ * Protect against unicast queries to mrouted versions that might crash.
+ */
+ if (gwy && !IN_MULTICAST(ntohl(gwy)))
+ if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0])) {
+ int version = ntohl(incr[0].igmp.igmp_group.s_addr) & 0xFFFF;
+ if (version == 0x0303 || version == 0x0503) {
+ printf("Don't use -g to address an mrouted 3.%d, it might crash\n",
+ (version >> 8) & 0xFF);
exit(0);
}
+ }
printf("Mtrace from %s to %s via group %s\n",
inet_fmt(qsrc, s1), inet_fmt(qdst, s2), inet_fmt(qgrp, s3));
@@ -1105,29 +1405,35 @@ char *argv[];
}
/*
- * Make up the IGMP_MTRACE_QUERY query packet to send (some parameters
- * are set later), including initializing the seed for random
- * query identifiers.
+ * If the response is to be a multicast address, make sure we
+ * are listening on that multicast address.
*/
- query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
- query->tr_src = qsrc;
- query->tr_dst = qdst;
-
- gettimeofday(&tv, 0);
- seed = tv.tv_usec ^ lcl_addr;
- srandom(seed);
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr);
+ } else k_join(resp_cast, lcl_addr);
/*
- * If the response is to be a multicast address, make sure we
- * are listening on that multicast address.
+ * If the destination is on the local net, the last-hop router can
+ * be found by multicast to the all-routers multicast group.
+ * Otherwise, use the group address that is the subject of the
+ * query since by definition the last-hop router will be a member.
+ * Set default TTLs for local remote multicasts.
*/
- if (raddr && IN_MULTICAST(ntohl(raddr)))
- k_join(raddr, lcl_addr);
- else
- k_join(resp_cast, lcl_addr);
+ restart:
+
+ if (gwy == 0)
+ if ((qdst & dst_netmask) == (lcl_addr & dst_netmask)) tdst = query_cast;
+ else tdst = qgrp;
+ else tdst = gwy;
+
+ if (IN_MULTICAST(ntohl(tdst))) {
+ k_set_loop(1); /* If I am running on a router, I need to hear this */
+ if (tdst == query_cast) k_set_ttl(qttl ? qttl : 1);
+ else k_set_ttl(qttl ? qttl : MULTICAST_TTL1);
+ }
/*
- * Try a query at the requested number of hops or MAXOPS if unspecified.
+ * Try a query at the requested number of hops or MAXHOPS if unspecified.
*/
if (qno == 0) {
hops = MAXHOPS;
@@ -1143,12 +1449,14 @@ char *argv[];
base.rtime = 0;
base.len = 0;
- recvlen = send_recv(gwy, IGMP_MTRACE_QUERY, hops, tries, &base);
+ recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, hops, tries, &base);
/*
* If the initial query was successful, print it. Otherwise, if
* the query max hop count is the default of zero, loop starting
- * from one until a timeout occurs.
+ * from one until there is no response for four hops. The extra
+ * hops allow getting past an mtrace-capable mrouter that can't
+ * send multicast packets because all phyints are disabled.
*/
if (recvlen) {
printf("\n 0 ");
@@ -1156,10 +1464,12 @@ char *argv[];
printf("\n");
print_trace(1, &base);
r = base.resps + base.len - 1;
- if (r->tr_rflags == TR_OLD_ROUTER) {
+ if (r->tr_rflags == TR_OLD_ROUTER || r->tr_rflags == TR_NO_SPACE ||
+ qno != 0) {
printf("%3d ", -(base.len+1));
- fflush(stdout);
- what_kind(&base);
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace"
+ : "is the next hop");
} else {
VAL_TO_MASK(smask, r->tr_smask);
if ((r->tr_inaddr & smask) == (qsrc & smask)) {
@@ -1173,50 +1483,113 @@ char *argv[];
print_host(qdst);
printf("\n");
- for (hops = 1; hops <= MAXHOPS; ++hops) {
+ for (hops = 1, nexthop = 1; hops <= MAXHOPS; ++hops) {
printf("%3d ", -hops);
fflush(stdout);
- recvlen = send_recv(gwy, IGMP_MTRACE_QUERY, hops, nqueries, &base);
+ /*
+ * After a successful first hop, try switching to the unicast
+ * address of the last-hop router instead of multicasting the
+ * trace query. This should be safe for mrouted versions 3.3
+ * and 3.5 because there is a long route timeout with metric
+ * infinity before a route disappears. Switching to unicast
+ * reduces the amount of multicast traffic and avoids a bug
+ * with duplicate suppression in mrouted 3.5.
+ */
+ if (hops == 2 && gwy == 0 &&
+ (recvlen = send_recv(lastout, IGMP_MTRACE_QUERY, hops, 1, &base)))
+ tdst = lastout;
+ else recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, hops, nqueries, &base);
if (recvlen == 0) {
- if (--hops == 0) break;
- what_kind(&base);
- break;
+ if (hops == 1) break;
+ if (hops == nexthop) {
+ if (what_kind(&base, "didn't respond")) {
+ /* the ask_neighbors determined that the
+ * not-responding router is the first-hop. */
+ break;
+ }
+ } else if (hops < nexthop + 3) {
+ printf("\n");
+ } else {
+ printf("...giving up\n");
+ break;
+ }
+ continue;
}
r = base.resps + base.len - 1;
- if (base.len == hops)
- print_trace(-hops, &base);
- else {
- hops = base.len;
- if (r->tr_rflags == TR_OLD_ROUTER) {
- what_kind(&base);
- break;
+ if (base.len == hops &&
+ (hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) {
+ if (hops == nexthop) {
+ print_trace(-hops, &base);
+ } else {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base);
}
- if (r->tr_rflags == TR_NO_SPACE) {
- printf("No space left in trace packet for further hops\n");
- break; /* XXX could do segmented trace */
+ } else {
+ if (base.len < hops) {
+ /*
+ * A shorter trace than requested means a fatal error
+ * occurred along the path, or that the route changed
+ * to a shorter one.
+ *
+ * If the trace is longer than the last one we received,
+ * then we are resuming from a skipped router (but there
+ * is still probably a problem).
+ *
+ * If the trace is shorter than the last one we
+ * received, then the route must have changed (and
+ * there is still probably a problem).
+ */
+ if (nexthop <= base.len) {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base);
+ } else if (nexthop > base.len + 1) {
+ hops = base.len;
+ printf("\nRoute must have changed...\n");
+ print_trace(1, &base);
+ }
+ } else {
+ /*
+ * The last hop address is not the same as it was;
+ * the route probably changed underneath us.
+ */
+ hops = base.len;
+ printf("\nRoute must have changed...\n");
+ print_trace(1, &base);
}
- printf("Route must have changed...\n\n");
- print_trace(1, &base);
}
-
- VAL_TO_MASK(smask, r->tr_smask);
- if ((r->tr_inaddr & smask) == (qsrc & smask)) {
- printf("%3d ", -(hops+1));
- print_host(qsrc);
- printf("\n");
+ lastout = r->tr_outaddr;
+
+ if (base.len < hops ||
+ r->tr_rmtaddr == 0 ||
+ (r->tr_rflags & 0x80)) {
+ VAL_TO_MASK(smask, r->tr_smask);
+ if (r->tr_rmtaddr) {
+ if (hops != nexthop) {
+ printf("\n%3d ", -(base.len+1));
+ }
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace" :
+ "would be the next hop");
+ /* XXX could do segmented trace if TR_NO_SPACE */
+ } else if (r->tr_rflags == TR_NO_ERR &&
+ (r->tr_inaddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(hops + 1));
+ print_host(qsrc);
+ printf("\n");
+ }
break;
}
- if (r->tr_rmtaddr == 0 || (r->tr_rflags & 0x80))
- break;
+
+ nexthop = hops + 1;
}
}
if (base.rtime == 0) {
printf("Timed out receiving responses\n");
- if (IN_MULTICAST(ntohl(gwy)))
- if (gwy == query_cast)
+ if (IN_MULTICAST(ntohl(tdst)))
+ if (tdst == query_cast)
printf("Perhaps no local router has a route for source %s\n",
inet_fmt(qsrc, s1));
else
@@ -1237,8 +1610,9 @@ or multicast at ttl %d doesn't reach its last-hop router for that source\n",
raddr = base.qhdr.tr_raddr;
rttl = base.qhdr.tr_rttl;
gettimeofday(&tv, 0);
- waittime = 10 - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16));
- prev = new = &incr[numstats&1];
+ waittime = statint - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16));
+ prev = &base;
+ new = &incr[numstats&1];
while (numstats--) {
if (waittime < 1) printf("\n");
@@ -1248,7 +1622,7 @@ or multicast at ttl %d doesn't reach its last-hop router for that source\n",
sleep((unsigned)waittime);
}
rno = base.len;
- recvlen = send_recv(gwy, IGMP_MTRACE_QUERY, rno, nqueries, new);
+ recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, rno, nqueries, new);
if (recvlen == 0) {
printf("Timed out.\n");
@@ -1256,24 +1630,41 @@ or multicast at ttl %d doesn't reach its last-hop router for that source\n",
}
if (rno != new->len) {
- printf("Trace length doesn't match.\n");
- exit(1);
+ printf("Trace length doesn't match:\n");
+ /*
+ * XXX Should this trace result be printed, or is that
+ * too verbose? Perhaps it should just say restarting.
+ * But if the path is changing quickly, this may be the
+ * only snapshot of the current path. But, if the path
+ * is changing that quickly, does the current path really
+ * matter?
+ */
+ print_trace(1, new);
+ printf("Restarting.\n\n");
+ numstats++;
+ goto restart;
}
printf("Results after %d seconds:\n\n",
- (new->qtime - base.qtime) >> 16);
- fixup_stats(&base, new);
- print_stats(&base, prev, new);
+ (int)((new->qtime - base.qtime) >> 16));
+ fixup_stats(&base, prev, new);
+ if (print_stats(&base, prev, new)) {
+ printf("Route changed:\n");
+ print_trace(1, new);
+ printf("Restarting.\n\n");
+ goto restart;
+ }
prev = new;
new = &incr[numstats&1];
- waittime = 10;
+ waittime = statint;
}
/*
* If the response was multicast back, leave the group
*/
- if (raddr && IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr);
- else k_leave(resp_cast, lcl_addr);
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr);
+ } else k_leave(resp_cast, lcl_addr);
return (0);
}
@@ -1289,30 +1680,37 @@ check_vif_state()
* of the message and the current debug level. For errors of severity
* LOG_ERR or worse, terminate the program.
*/
-/*VARARGS3*/
+#ifdef __STDC__
void
-log(severity, syserr, format, a, b, c, d, e)
- int severity, syserr;
- char *format;
- int a, b, c, d, e;
+log(int severity, int syserr, char *format, ...)
{
- char fmt[100];
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap, format);
+#else
+/*VARARGS3*/
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap);
+#endif
switch (debug) {
- case 0:
- if (severity > LOG_WARNING)
- return;
- case 1:
- if (severity > LOG_NOTICE)
- return;
- case 2:
- if (severity > LOG_INFO)
- return;
+ case 0: if (severity > LOG_WARNING) return;
+ case 1: if (severity > LOG_NOTICE) return;
+ case 2: if (severity > LOG_INFO ) return;
default:
fmt[0] = '\0';
if (severity == LOG_WARNING) strcat(fmt, "warning - ");
strncat(fmt, format, 80);
- fprintf(stderr, fmt, a, b, c, d, e);
+ vfprintf(stderr, fmt, ap);
if (syserr == 0)
fprintf(stderr, "\n");
else if(syserr < sys_nerr)
@@ -1320,24 +1718,94 @@ log(severity, syserr, format, a, b, c, d, e)
else
fprintf(stderr, ": errno %d\n", syserr);
}
- if (severity <= LOG_ERR)
- exit(-1);
+ if (severity <= LOG_ERR) exit(-1);
}
/* dummies */
-
-/*VARARGS*/
-void accept_probe() {} /*VARARGS*/
-void accept_group_report() {} /*VARARGS*/
-void accept_neighbors() {} /*VARARGS*/
-void accept_neighbors2() {} /*VARARGS*/
-void accept_neighbor_request() {} /*VARARGS*/
-void accept_neighbor_request2() {} /*VARARGS*/
-void accept_report() {} /*VARARGS*/
-void accept_prune() {} /*VARARGS*/
-void accept_graft() {} /*VARARGS*/
-void accept_g_ack() {} /*VARARGS*/
-void add_table_entry() {} /*VARARGS*/
-void accept_mtrace() {} /*VARARGS*/
-void accept_leave_message() {} /*VARARGS*/
-void accept_membership_query() {} /*VARARGS*/
+void accept_probe(src, dst, p, datalen, level)
+ u_int32_t src, dst, level;
+ char *p;
+ int datalen;
+{
+}
+void accept_group_report(src, dst, group, r_type)
+ u_int32_t src, dst, group;
+ int r_type;
+{
+}
+void accept_neighbor_request2(src, dst)
+ u_int32_t src, dst;
+{
+}
+void accept_report(src, dst, p, datalen, level)
+ u_int32_t src, dst, level;
+ char *p;
+ int datalen;
+{
+}
+void accept_neighbor_request(src, dst)
+ u_int32_t src, dst;
+{
+}
+void accept_prune(src, dst, p, datalen)
+ u_int32_t src, dst;
+ char *p;
+ int datalen;
+{
+}
+void accept_graft(src, dst, p, datalen)
+ u_int32_t src, dst;
+ char *p;
+ int datalen;
+{
+}
+void accept_g_ack(src, dst, p, datalen)
+ u_int32_t src, dst;
+ char *p;
+ int datalen;
+{
+}
+void add_table_entry(origin, mcastgrp)
+ u_int32_t origin, mcastgrp;
+{
+}
+void accept_leave_message(src, dst, group)
+ u_int32_t src, dst, group;
+{
+}
+void accept_mtrace(src, dst, group, data, no, datalen)
+ u_int32_t src, dst, group;
+ char *data;
+ u_int no;
+ int datalen;
+{
+}
+void accept_membership_query(src, dst, group, tmo)
+ u_int32_t src, dst, group;
+ int tmo;
+{
+}
+void accept_neighbors(src, dst, p, datalen, level)
+ u_int32_t src, dst, level;
+ u_char *p;
+ int datalen;
+{
+}
+void accept_neighbors2(src, dst, p, datalen, level)
+ u_int32_t src, dst, level;
+ u_char *p;
+ int datalen;
+{
+}
+void accept_info_request(src, dst, p, datalen)
+ u_int32_t src, dst;
+ u_char *p;
+ int datalen;
+{
+}
+void accept_info_reply(src, dst, p, datalen)
+ u_int32_t src, dst;
+ u_char *p;
+ int datalen;
+{
+}