diff options
Diffstat (limited to 'usr.sbin/mtrace')
-rw-r--r-- | usr.sbin/mtrace/mtrace.8 | 34 | ||||
-rw-r--r-- | usr.sbin/mtrace/mtrace.c | 1430 |
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; +{ +} |