From 54e56efac0e04ad25128fe0907e689d663798551 Mon Sep 17 00:00:00 2001 From: Bill Fenner Date: Fri, 20 Dec 1996 01:29:00 +0000 Subject: [PATCH] Import the mtrace "5.1" release. The version number jump is because mtrace is now distributed seperately from mrouted. --- usr.sbin/mrouted/mtrace.8 | 165 +++-- usr.sbin/mrouted/mtrace.c | 1328 +++++++++++++++++++++++++++++++------ usr.sbin/mrouted/mtrace.h | 87 +++ 3 files changed, 1347 insertions(+), 233 deletions(-) create mode 100644 usr.sbin/mrouted/mtrace.h diff --git a/usr.sbin/mrouted/mtrace.8 b/usr.sbin/mrouted/mtrace.8 index 5c8e1fe13465..4329ffbef61b 100644 --- a/usr.sbin/mrouted/mtrace.8 +++ b/usr.sbin/mrouted/mtrace.8 @@ -29,7 +29,7 @@ .\" Copyright (c) 1988 The Regents of the University of California. .\" All rights reserved. .\" -.\" $Id: mtrace.8,v 3.8 1995/11/29 22:37:21 fenner Rel $ +.\" mtrace.8,v 5.1 1996/12/19 21:31:26 fenner Exp .\" .TH MTRACE 8 "May 8, 1995" .UC 6 @@ -38,6 +38,9 @@ mtrace \- print multicast path from a source to a receiver .SH SYNOPSIS .B mtrace [ +.B \-e +.I extrahops +] [ .B \-g .I gateway ] [ @@ -53,8 +56,12 @@ mtrace \- print multicast path from a source to a receiver ] [ .B \-n ] [ +.B \-O +] [ .B \-p ] [ +.B \-P +] [ .B \-q .I nqueries ] [ @@ -69,6 +76,10 @@ mtrace \- print multicast path from a source to a receiver .B \-t .I ttl ] [ +.B \-T +] [ +.B \-U +] [ .B \-v ] [ .B \-w @@ -84,9 +95,7 @@ mtrace \- print multicast path from a source to a receiver Assessing problems in the distribution of IP multicast traffic can be difficult. .B mtrace -utilizes a tracing feature implemented in multicast routers -.RB ( mrouted -version 3.3 and later) that is +utilizes a tracing feature implemented in multicast routers that is accessed via an extension to the IGMP protocol. A trace query is passed hop-by-hop along the reverse path from the .I receiver @@ -110,11 +119,23 @@ detailed below. The two parameters can be distinguished because the is a unicast address and the .I group is a multicast address. +If the +.B \-g +flag is specified, the source address defaults to the host running +mtrace, and the receiver defaults to the router being addressed with +the +.B \-g +flag. In this case, there are no required parameters. .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 \-e\ extrahops +Try tracing +.I extrahops +hops past a non-responding router. +.TP 8 8 .BI \-g\ gwy Send the trace query via unicast directly to the multicast router .I gwy @@ -155,8 +176,8 @@ multicast path every 10 seconds (see .IR stat_int ). .TP 8 8 .B \-M -Always send the response using multicast rather than attempting -unicast first. +Always request the response using multicast rather than attempting +unicast for the last half of the tries. .TP 8 8 .BI \-m\ n Set to @@ -177,10 +198,22 @@ Set the maximum number of query attempts for any hop to .IR n . The default is 3. .TP 8 8 +.B \-O +Do not use the Router-Alert IP option on those requests which need it. +Some versions of Cisco's IOS cannot handle +multicast traceroutes with IP options, so it may be necessary to use the +-O flag if the last-hop router is a Cisco. +.TP 8 8 .B \-p Listen passively for multicast responses from traces initiated by others. This works best when run on a multicast router. .TP 8 8 +.B \-P +Loop indefinitely collecting the path every 10 seconds (see +.B \-S +.IR stat_int ) +and printing it when it changes. Do not print any statistics. +.TP 8 8 .BI \-r\ host Send the trace response to .I host @@ -202,11 +235,20 @@ seconds (default 10 seconds). Set the .I ttl (time-to-live, or number of hops) for multicast trace queries and -responses. The default is 64, except for local queries to the "all +responses. The default is 127, except for local queries to the "all routers" multicast group which use ttl 1. .TP 8 8 +.B \-T +"Tunnel statistics" mode; show loss rates for overall traffic. +These statistics can be extremely misleading. +.TP 8 8 +.B \-U +Always request the response using unicast rather than attempting +multicast first. +.TP 8 8 .B \-v Verbose mode; show hop times on the initial trace and statistics display. +Also show the route that was used to forward the initial trace. .TP 8 8 .BI \-w\ n Set the time to wait for a trace response to @@ -273,7 +315,7 @@ trace query is multicast to the address since the last hop router will be a member of that group if the receiver is. Therefore it is necessary to specify a group that the intended receiver has joined. This multicast is sent with a -default ttl of 64, which may not be sufficient for all cases (changed +default ttl of 127, which may not be sufficient for all cases (changed with the .B \-t option). @@ -307,28 +349,40 @@ increasing by one until the full path is traced or no response is received. At each hop, multiple probes are sent (default is three, changed with .B \-q -option). The first half of the attempts (default is one) are made with -the unicast address of the host running -.B mtrace -as the destination for the response. Since the unicast route may be -blocked, the remainder of attempts request that the response be -multicast to mtrace.mcast.net (224.0.1.32) with the ttl set to 32 more -than what's needed to pass the thresholds seen so far along the path -to the receiver. For the last quarter of the attempts (default is -one), the ttl is increased by another 32 each time up to a maximum of -192. Alternatively, the ttl may be set explicity with the +option). The first half of the attempts (default is two) are made with +the reply address set to standard multicast address, mtrace.mcast.net +(224.0.1.32) with the ttl set to 32 more than what's needed to pass the +thresholds seen so far along the path to the receiver. For each +additional attempt, the ttl is increased by another 32 each time up to +a maximum of 192. Since the desired router may not be able to send a +multicast reply, the remainder of the attempts request that the +response be sent via unicast to the host running +.B mtrace . +Alternatively, the multicast ttl may be set explicitly with the .B \-t -option and/or the initial unicast attempts can be forced to use -multicast instead with the +option, the initial multicast attempts can be forced to use unicast +instead with the +.B \-U +option, the final unicast attempts can be forced to use multicast +isntead with the .B \-M -option. For each attempt, if no response is received within the -timeout, a "*" is printed. After the specified number of attempts -have failed, +option, or if you specify +.B \-UM +.B mtrace +will first attempt using unicast and then multicast. For each attempt, +if no response is received within the timeout, a "*" is printed. After +the specified number of attempts have failed, .B mtrace will try to query the next hop router with a DVMRP_ASK_NEIGHBORS2 request (as used by the .B mrinfo program) to see what kind of router it is. +.B mtrace +will try to query three (changed with the +.B \-e +option) hops past a non-responding router, in the hopes that even +though it isn't capable of sending a response, it might be capable of +forwarding the request on. .SH EXAMPLES The output of .B mtrace @@ -346,7 +400,8 @@ up-arrow character); and the cumulative delay for the query to reach that hop (valid only if the clocks are synchronized). This first section ends with a line showing the round-trip time which measures the interval from when the query is issued until the response is -received, both derived from the local system clock. A sample use and +received, both derived from the local system clock, and the total +ttl required for a packet to travel along this path. A sample use and output might be: .PP .nf @@ -361,18 +416,29 @@ Querying full reverse path... -4 bbn.dart.net (140.173.32.1) DVMRP thresh^ 1 63 ms -5 mit.dart.net (140.173.48.2) DVMRP thresh^ 1 71 ms -6 caraway.lcs.mit.edu (18.26.0.170) -Round trip time 124 ms +Round trip time 124 ms; total ttl of 6 required. .fi .PP +If a hop reports that it is using the default route to forward packets, +the word +.B [default] +is printed after that hop. If the +.B \-v +flag is supplied, the route being used to forward packets is printed +in the form +.B [18.26.0/24] . +.PP The second section provides a pictorial view of the path in the forward direction with data flow indicated by arrows pointing downward and the query path indicated by arrows pointing upward. For each hop, both the entry and exit addresses of the router are shown if different, along with the initial ttl required on the packet in order to be forwarded at this hop and the propagation delay across the hop -assuming that the routers at both ends have synchronized clocks. The -right half of this section is composed of several columns of -statistics in two groups. Within each group, the columns are the +assuming that the routers at both ends have synchronized clocks. +The right half of this section is composed of two sets of statistics. +The first column contains the average packet rate for all traffic at +each hop. +The remaining columns are the number of packets lost, the number of packets sent, the percentage lost, and the average packet rate at each hop. These statistics are calculated from differences between traces and from hop to hop as @@ -383,6 +449,11 @@ from the specified .I source to the specified .IR group . +The first group of statistics may be expanded to include loss rates +using the +.B \-T +option. However, these numbers can be extremely misleading and require +detailed knowledge of the routers involved to be interpreted properly. .PP These statistics are shown on one or two lines for each hop. Without any options, this second section of the output is printed only once, @@ -397,7 +468,7 @@ statistics over the period since the initial trace, which is 101 seconds in the example below. The second section of the output is omitted if the .B \-s -option is set. +option is set or if no multicast group is specified. .ie t \{\ .ft C . ie \w'i'<>\w'm' \{\" looks like this is not proper Courier font @@ -411,30 +482,30 @@ and try again.) .nf Waiting to accumulate statistics... Results after 101 seconds: - Source Response Dest Packet Statistics For Only For Traffic -18.26.0.170 128.9.160.100 All Multicast Traffic From 18.26.0.170 - | __/ rtt 125 ms Lost/Sent = Pct Rate To 224.2.0.3 - v / hop 65 ms --------------------- ------------------ + Source Response Dest Overall Packet Statistics For Traffic From +18.26.0.170 128.9.160.100 Packet 18.26.0.170 To 224.2.0.3 + | __/ rtt 125 ms Rate Lost/Sent = Pct Rate + v / hop 65 ms ------- --------------------- 18.26.0.144 140.173.48.2 mit.dart.net - | ^ ttl 1 0/6 = --% 0 pps 0/2 = --% 0 pps - v | hop 8 ms 1/52 = 2% 0 pps 0/18 = 0% 0 pps + | ^ ttl 1 0 pps 0/2 = --% 0 pps + v | hop 8 ms 0 pps 0/18 = 0% 0 pps 140.173.48.1 140.173.32.1 bbn.dart.net - | ^ ttl 2 0/6 = --% 0 pps 0/2 = --% 0 pps - v | hop 12 ms 1/52 = 2% 0 pps 0/18 = 0% 0 pps + | ^ ttl 2 0 pps 0/2 = --% 0 pps + v | hop 12 ms 0 pps 0/18 = 0% 0 pps 140.173.32.2 140.173.64.1 dc.dart.net - | ^ ttl 3 0/271 = 0% 27 pps 0/2 = --% 0 pps - v | hop 34 ms -1/2652 = 0% 26 pps 0/18 = 0% 0 pps + | ^ ttl 3 27 pps 0/2 = --% 0 pps + v | hop 34 ms 26 pps 0/18 = 0% 0 pps 140.173.64.2 140.173.128.1 la.dart.net - | ^ ttl 4 -2/831 = 0% 83 pps 0/2 = --% 0 pps - v | hop 11 ms -3/8072 = 0% 79 pps 0/18 = 0% 0 pps + | ^ ttl 4 83 pps 0/2 = --% 0 pps + v | hop 11 ms 79 pps 0/18 = 0% 0 pps 140.173.128.2 128.9.160.153 cub.isi.edu - | \\__ ttl 5 833 83 pps 2 0 pps - v \\ hop -8 ms 8075 79 pps 18 0 pps + | \\__ ttl 5 83 pps ?/2 0 pps + v \\ hop -8 ms 79 pps ?/18 0 pps 128.9.160.100 128.9.160.100 Receiver Query Source .fi @@ -480,7 +551,7 @@ in no response because there was a node running an old version of .B mrouted that did not implement the multicast traceroute function, so .B mtrace -switched to hop-by-hop mode. The \*(lqRoute pruned\*(rq error code +switched to hop-by-hop mode. The \*(lqOutput pruned\*(rq error code indicates that traffic for group 224.2.143.24 would not be forwarded. .PP .nf @@ -490,7 +561,7 @@ oak.isi.edu 108# mtrace -g 140.173.48.2 204.62.246.73 \\ Mtrace from 204.62.246.73 to 18.26.0.151 via group 224.2.143.24 Querying full reverse path... * switching to hop-by-hop: 0 butter.lcs.mit.edu (18.26.0.151) - -1 jam.lcs.mit.edu (18.26.0.144) DVMRP thresh^ 1 33 ms Route pruned + -1 jam.lcs.mit.edu (18.26.0.144) DVMRP thresh^ 1 33 ms Output pruned -2 bbn.dart.net (140.173.48.1) DVMRP thresh^ 1 36 ms -3 dc.dart.net (140.173.32.2) DVMRP thresh^ 1 44 ms -4 darpa.dart.net (140.173.240.2) DVMRP thresh^ 16 47 ms @@ -514,3 +585,7 @@ program written by Van Jacobson. .BR mrinfo (8) , .BR map-mbone (8) , .BR traceroute (8) +.SH BUGS +.PP +Statistics collection in passive mode doesn't always produce the same output +as when actively collecting data. diff --git a/usr.sbin/mrouted/mtrace.c b/usr.sbin/mrouted/mtrace.c index 389084ae2416..2164b91a1b7b 100644 --- a/usr.sbin/mrouted/mtrace.c +++ b/usr.sbin/mrouted/mtrace.c @@ -23,16 +23,16 @@ * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its - * documentation in source and binary forms for non-commercial purposes - * and without fee is hereby granted, provided that the above copyright - * notice appear in all copies and that both the copyright notice and - * this permission notice appear in supporting documentation, and that - * any documentation, advertising materials, and other materials related - * to such distribution and use acknowledge that the software was - * developed by the University of Southern California, Information - * Sciences Institute. The name of the University may not be used to - * endorse or promote products derived from this software without - * specific prior written permission. + * documentation in source and binary forms for any purposes and without + * fee is hereby granted, provided that the above copyright notice + * appear in all copies and that both the copyright notice and this + * permission notice appear in supporting documentation, and that any + * documentation, advertising materials, and other materials related to + * such distribution and use acknowledge that the software was developed + * by the University of Southern California, Information Sciences + * Institute. The name of the University may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. * * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about * the suitability of this software for any purpose. THIS SOFTWARE IS @@ -43,23 +43,87 @@ * Other copyrights might apply to parts of this software and are so * noted when applicable. * - * In particular, parts of the prototype version of this program may - * have been derived from mrouted programs sources covered by the - * license in the accompanying file named "LICENSE". + * Parts of this software are derived from mrouted, which has the + * following license: + * + * The mrouted program is covered by the following license. Use of the + * mrouted program represents acceptance of these terms and conditions. + * + * 1. STANFORD grants to LICENSEE a nonexclusive and nontransferable + * license to use, copy and modify the computer software ``mrouted'' + * (hereinafter called the ``Program''), upon the terms and conditions + * hereinafter set out and until Licensee discontinues use of the Licensed + * Program. + * + * 2. LICENSEE acknowledges that the Program is a research tool still in + * the development state, that it is being supplied ``as is,'' without any + * accompanying services from STANFORD, and that this license is entered + * into in order to encourage scientific collaboration aimed at further + * development and application of the Program. + * + * 3. LICENSEE may copy the Program and may sublicense others to use + * object code copies of the Program or any derivative version of the + * Program. All copies must contain all copyright and other proprietary + * notices found in the Program as provided by STANFORD. Title to + * copyright to the Program remains with STANFORD. + * + * 4. LICENSEE may create derivative versions of the Program. LICENSEE + * hereby grants STANFORD a royalty-free license to use, copy, modify, + * distribute and sublicense any such derivative works. At the time + * LICENSEE provides a copy of a derivative version of the Program to a + * third party, LICENSEE shall provide STANFORD with one copy of the + * source code of the derivative version at no charge to STANFORD. + * + * 5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + * IMPLIED. By way of example, but not limitation, STANFORD MAKES NO + * REPRESENTATION OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY + * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED PROGRAM WILL NOT + * INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD + * shall not be held liable for any liability nor for any direct, indirect + * or consequential damages with respect to any claim by LICENSEE or any + * third party on account of or arising from this Agreement or use of the + * Program. + * + * 6. This agreement shall be construed, interpreted and applied in + * accordance with the State of California and any legal action arising + * out of this Agreement or use of the Program shall be filed in a court + * in the State of California. + * + * 7. Nothing in this Agreement shall be construed as conferring rights to + * use in advertising, publicity or otherwise any trademark or the name + * of ``Stanford''. + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * */ #ifndef lint static char rcsid[] = - "@(#) $Id: mtrace.c,v 3.8 1995/11/29 22:36:34 fenner Rel $"; + "@(#) mtrace.c,v 5.1.1.1 1996/12/20 00:43:40 fenner Exp"; #endif -#include -#include +#include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include "defs.h" +#ifdef SYSV +#include +#endif #include #ifdef __STDC__ #include @@ -70,14 +134,31 @@ static char rcsid[] = #include #endif +typedef unsigned int u_int32; /* XXX */ + +#include "mtrace.h" + #define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */ #define DEFAULT_RETRIES 3 /* How many times to try */ -#define MAXHOPS UNREACHABLE /* Don't need more hops than max metric */ +#define DEFAULT_EXTRAHOPS 3 /* How many hops past a non-responding rtr */ +#define MAXHOPS 32 /* Don't need more hops than max metric */ #define UNICAST_TTL 255 /* TTL for unicast response */ -#define MULTICAST_TTL1 64 /* Default TTL for multicast query/response */ +#define MULTICAST_TTL1 127 /* Default TTL for multicast query/response */ #define MULTICAST_TTL_INC 32 /* TTL increment for increase after timeout */ #define MULTICAST_TTL_MAX 192 /* Maximum TTL allowed (protect low-BW links */ +#define TRUE 1 +#define FALSE 0 +#define DVMRP_ASK_NEIGHBORS2 5 /* DVMRP msg requesting neighbors */ +#define DVMRP_NEIGHBORS2 6 /* reply to above */ +#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */ +#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */ +#define MAX_IP_PACKET_LEN 576 +#define MIN_IP_HEADER_LEN 20 +#define MAX_IP_HEADER_LEN 60 +#define MAX_DVMRP_DATA_LEN \ + ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN ) + struct resp_buf { u_long qtime; /* Time query was issued */ u_long rtime; /* Time response was received */ @@ -97,8 +178,44 @@ 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, ... */ +/* + * In mrouted 3.3 and 3.4 (and in some Cisco IOS releases), + * cache entries can get deleted even if there is traffic + * flowing, which will reset the per-source/group counters. + */ +#define BUG_RESET 0x01 + +/* + * Also in mrouted 3.3 and 3.4, there's a bug in neighbor + * version processing which can cause them to believe that + * the neighbor is constantly resetting. This causes them + * to constantly delete all their state. + */ +#define BUG_RESET2X 0x02 + +/* + * Pre-3.7 mrouted's forget to byte-swap their reports. + */ +#define BUG_SWAP 0x04 + +/* + * Pre-3.9 mrouted's forgot a parenthesis in the htonl() + * on the time calculation so supply bogus times. + */ +#define BUG_BOGUSTIME 0x08 + +#define BUG_NOPRINT (BUG_RESET | BUG_RESET2X) + +int bugs[MAXHOPS]; /* List of bugs noticed at each hop */ + +struct mtrace { + struct mtrace *next; + struct resp_buf base, incr[2]; + struct resp_buf *new, *prev; + int nresp; + struct timeval last; + int bugs[MAXHOPS]; +}; int timeout = DEFAULT_TIMEOUT; int nqueries = DEFAULT_RETRIES; @@ -106,8 +223,14 @@ int numeric = FALSE; int debug = 0; int passive = FALSE; int multicast = FALSE; +int unicast = FALSE; int statint = 10; -int verbose = 0; +int verbose = FALSE; +int tunstats = FALSE; +int weak = FALSE; +int extrahops = DEFAULT_EXTRAHOPS; +int printstats = TRUE; +int sendopts = TRUE; u_int32 defgrp; /* Default group if not specified */ u_int32 query_cast; /* All routers multicast addr */ @@ -130,13 +253,56 @@ u_char rttl = 0; /* TTL for the response packet */ u_int32 gwy = 0; /* User-supplied last-hop router address */ u_int32 tdst = 0; /* Address where trace is sent (last-hop) */ -vifi_t numvifs; /* to keep loader happy */ - /* (see kern.c) */ -#ifndef SYSV -extern long random(); -#endif -extern int errno; +char s1[19]; /* buffers to hold the string representations */ +char s2[19]; /* of IP addresses, to be passed to inet_fmt() */ +char s3[19]; /* or inet_fmts(). */ +#if !(defined(BSD) && (BSD >= 199103)) +extern int errno; +extern int sys_nerr; +extern char * sys_errlist[]; +#endif + +#define RECV_BUF_SIZE 8192 +char *send_buf, *recv_buf; +int igmp_socket; +u_int32 allrtrs_group; +char router_alert[4]; /* Router Alert IP Option */ +#ifndef IPOPT_RA +#define IPOPT_RA 148 +#endif +#ifdef SUNOS5 +char no_op[4]; /* Null IP Option */ +int ip_addlen = 0; /* Workaround for Option bug #2 */ +#endif + +/* + * max macro, with weird case to avoid conflicts + */ +#define MaX(a,b) (a) > (b) ? (a) : (b) + +#ifndef __P +#ifdef __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif +#endif + +void init_igmp __P(()); +void send_igmp __P((u_int32 src, u_int32 dst, int type, + int code, u_int32 group, + int datalen)); +int inet_cksum __P((u_short *addr, u_int len)); +void k_set_rcvbuf __P((int bufsize)); +void k_hdr_include __P((int bool)); +void k_set_ttl __P((int t)); +void k_set_loop __P((int l)); +void k_set_if __P((u_int32 ifa)); +void k_join __P((u_int32 grp, u_int32 ifa)); +void k_leave __P((u_int32 grp, u_int32 ifa)); +char * inet_fmt __P((u_int32 addr, char *s)); +char * inet_fmts __P((u_int32 addr, u_int32 mask, char *s)); char * inet_name __P((u_int32 addr)); u_int32 host_addr __P((char *name)); /* u_int is promoted u_char */ @@ -146,7 +312,7 @@ char * flag_type __P((u_int type)); u_int32 get_netmask __P((int s, u_int32 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)); +u_long fixtime __P((u_long time, struct resp_buf *base)); int send_recv __P((u_int32 dst, int type, int code, int tries, struct resp_buf *save)); char * print_host __P((u_int32 addr)); @@ -158,15 +324,400 @@ 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)); + struct resp_buf *new, + int *bugs)); int print_stats __P((struct resp_buf *base, struct resp_buf *prev, + struct resp_buf *new, + int *bugs)); +int path_changed __P((struct resp_buf *base, struct resp_buf *new)); void check_vif_state __P((void)); int main __P((int argc, char *argv[])); +void log __P((int, int, char *, ...)); +/* + * Open and initialize the igmp socket, and fill in the non-changing + * IP header fields in the output packet buffer. + */ +void +init_igmp() +{ + struct ip *ip; +#ifdef SUNOS5 + u_int32 localhost = htonl(0x7f000001); +#endif + + recv_buf = (char *)malloc(RECV_BUF_SIZE); + if (recv_buf == 0) + log(LOG_ERR, 0, "Out of memory allocating recv_buf!"); + send_buf = (char *)malloc(RECV_BUF_SIZE); + if (send_buf == 0) + log(LOG_ERR, 0, "Out of memory allocating send_buf!"); + + if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) + log(LOG_ERR, errno, "IGMP socket"); + + k_hdr_include(TRUE); /* include IP header when sending */ + k_set_rcvbuf(48*1024); /* lots of input buffering */ + k_set_ttl(1); /* restrict multicasts to one hop */ + k_set_loop(FALSE); /* disable multicast loopback */ + + ip = (struct ip *)send_buf; + ip->ip_hl = sizeof(struct ip) >> 2; + ip->ip_v = IPVERSION; + ip->ip_tos = 0; + ip->ip_off = 0; + ip->ip_p = IPPROTO_IGMP; + ip->ip_ttl = MAXTTL; /* applies to unicasts only */ + +#ifndef INADDR_ALLRTRS_GROUP +#define INADDR_ALLRTRS_GROUP 0xe0000002 /* 224.0.0.2 */ +#endif + allrtrs_group = htonl(INADDR_ALLRTRS_GROUP); + + router_alert[0] = IPOPT_RA; /* Router Alert */ + router_alert[1] = 4; /* 4 bytes */ + router_alert[2] = 0; + router_alert[3] = 0; + +#ifdef SUNOS5 + if (!sendopts) + return; + + no_op[0] = IPOPT_EOL; + no_op[1] = IPOPT_EOL; + no_op[2] = IPOPT_EOL; + no_op[3] = IPOPT_EOL; + + setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, no_op, sizeof(no_op)); + /* + * Check if the kernel adds the options length to the packet + * length. Send myself an IGMP packet of type 0 (illegal), + * with 4 IPOPT_EOL options, my PID (for collision detection) + * and 4 bytes of zero (so that the checksum works whether + * the 4 bytes of zero get truncated or not). + */ + bzero(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN, 8); + *(int *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN) = getpid(); + send_igmp(localhost, localhost, 0, 0, 0, 8); + while (1) { + int recvlen, dummy = 0; + + recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, + 0, NULL, &dummy); + /* 8 == 4 bytes of options and 4 bytes of PID */ + if (recvlen >= MIN_IP_HEADER_LEN + IGMP_MINLEN + 8) { + struct ip *ip = (struct ip *)recv_buf; + struct igmp *igmp; + int *p; + + if (ip->ip_hl != 6 || + ip->ip_p != IPPROTO_IGMP || + ip->ip_src.s_addr != localhost || + ip->ip_dst.s_addr != localhost) + continue; + + igmp = (struct igmp *)(recv_buf + (ip->ip_hl << 2)); + if (igmp->igmp_group.s_addr != 0) + continue; + if (igmp->igmp_type != 0 || igmp->igmp_code != 0) + continue; + + p = (int *)((char *)igmp + IGMP_MINLEN); + if (*p != getpid()) + continue; + + if (ip->ip_len == IGMP_MINLEN + 4) + ip_addlen = 4; + else if (ip->ip_len == IGMP_MINLEN + 8) + ip_addlen = 0; + else + log(LOG_ERR, 0, "while checking for Solaris bug: Sent %d bytes and got back %d!", IGMP_MINLEN + 8, ip->ip_len); + + break; + } + } +#endif +} + +/* + * Construct an IGMP message in the output packet buffer. The caller may + * have already placed data in that buffer, of length 'datalen'. Then send + * the message from the interface with IP address 'src' to destination 'dst'. + */ +void +send_igmp(src, dst, type, code, group, datalen) + u_int32 src, dst; + int type, code; + u_int32 group; + int datalen; +{ + struct sockaddr_in sdst; + struct ip *ip; + struct igmp *igmp; + int setloop = 0; + static int raset = 0; + int sendra = 0; + int sendlen; + + ip = (struct ip *)send_buf; + ip->ip_src.s_addr = src; + ip->ip_dst.s_addr = dst; + ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen; + sendlen = ip->ip_len; +#ifdef SUNOS5 + ip->ip_len += ip_addlen; +#endif + + igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); + igmp->igmp_type = type; + igmp->igmp_code = code; + igmp->igmp_group.s_addr = group; + igmp->igmp_cksum = 0; + igmp->igmp_cksum = inet_cksum((u_short *)igmp, + IGMP_MINLEN + datalen); + + if (IN_MULTICAST(ntohl(dst))) { + k_set_if(src); + setloop = 1; + k_set_loop(TRUE); + if (dst != allrtrs_group) + sendra = 1; + } + + if (sendopts && sendra && !raset) { + setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, + router_alert, sizeof(router_alert)); + raset = 1; + } else if (!sendra && raset) { +#ifdef SUNOS5 + /* + * SunOS5 < 5.6 cannot properly reset the IP_OPTIONS "socket" + * option. Instead, set up a string of 4 no-op's. + */ + setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, + no_op, sizeof(no_op)); +#else + setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, + NULL, 0); +#endif + raset = 0; + } + + bzero(&sdst, sizeof(sdst)); + sdst.sin_family = AF_INET; +#if (defined(BSD) && (BSD >= 199103)) + sdst.sin_len = sizeof(sdst); +#endif + sdst.sin_addr.s_addr = dst; + if (sendto(igmp_socket, send_buf, sendlen, 0, + (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { + log(LOG_WARNING, errno, "sendto to %s on %s", + inet_fmt(dst, s1), inet_fmt(src, s2)); + } + + if (setloop) + k_set_loop(FALSE); + + log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", + type == IGMP_MTRACE ? "mtrace request" : "ask_neighbors", + src == INADDR_ANY ? "INADDR_ANY" : inet_fmt(src, s1), + inet_fmt(dst, s2)); +} + +/* + * inet_cksum extracted from: + * P I N G . C + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * Modified at Uc Berkeley + * + * (ping.c) Status - + * Public Domain. Distribution Unlimited. + * + * I N _ C K S U M + * + * Checksum routine for Internet Protocol family headers (C Version) + * + */ +int +inet_cksum(addr, len) + u_short *addr; + u_int len; +{ + register int nleft = (int)len; + register u_short *w = addr; + u_short answer = 0; + register int sum = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) { + *(u_char *) (&answer) = *(u_char *)w ; + sum += answer; + } + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +void +k_set_rcvbuf(bufsize) + int bufsize; +{ + if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF, + (char *)&bufsize, sizeof(bufsize)) < 0) + log(LOG_ERR, errno, "setsockopt SO_RCVBUF %u", bufsize); +} + + +void +k_hdr_include(bool) + int bool; +{ +#ifdef IP_HDRINCL + if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL, + (char *)&bool, sizeof(bool)) < 0) + log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool); +#endif +} + +void +k_set_ttl(t) + int t; +{ + u_char ttl; + + ttl = t; + if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&ttl, sizeof(ttl)) < 0) + log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl); +} + + +void +k_set_loop(l) + int l; +{ + u_char loop; + + loop = l; + if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&loop, sizeof(loop)) < 0) + log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop); +} + +void +k_set_if(ifa) + u_int32 ifa; +{ + struct in_addr adr; + + adr.s_addr = ifa; + if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&adr, sizeof(adr)) < 0) + log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s", + inet_fmt(ifa, s1)); +} + +void +k_join(grp, ifa) + u_int32 grp; + u_int32 ifa; +{ + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = grp; + mreq.imr_interface.s_addr = ifa; + + if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)) < 0) + log(LOG_WARNING, errno, "can't join group %s on interface %s", + inet_fmt(grp, s1), inet_fmt(ifa, s2)); +} + + +void +k_leave(grp, ifa) + u_int32 grp; + u_int32 ifa; +{ + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = grp; + mreq.imr_interface.s_addr = ifa; + + if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)) < 0) + log(LOG_WARNING, errno, "can't leave group %s on interface %s", + inet_fmt(grp, s1), inet_fmt(ifa, s2)); +} + +/* + * Convert an IP address in u_long (network) format into a printable string. + */ +char * +inet_fmt(addr, s) + u_int32 addr; + char *s; +{ + register u_char *a; + + a = (u_char *)&addr; + sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]); + return (s); +} + + +/* + * Convert an IP subnet number in u_long (network) format into a printable + * string including the netmask as a number of bits. + */ +char * +inet_fmts(addr, mask, s) + u_int32 addr, mask; + char *s; +{ + register u_char *a, *m; + int bits; + + if ((addr == 0) && (mask == 0)) { + sprintf(s, "default"); + return (s); + } + a = (u_char *)&addr; + m = (u_char *)&mask; + bits = 33 - ffs(ntohl(mask)); + + if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3], + bits); + else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d", a[0], a[1], a[2], bits); + else if (m[1] != 0) sprintf(s, "%u.%u/%d", a[0], a[1], bits); + else sprintf(s, "%u/%d", a[0], bits); + + return (s); +} char * inet_name(addr) @@ -211,7 +762,7 @@ host_addr(name) if (e) memcpy((char *)&addr, e->h_addr_list[0], e->h_length); else { addr = inet_addr(buf); - if (addr == -1) { + if (addr == -1 || (IN_MULTICAST(addr) && dots)) { addr = 0; printf("Could not parse %s as host name or address\n", name); } @@ -235,6 +786,14 @@ proto_type(type) return ("PIM"); case PROTO_CBT: return ("CBT"); + case PROTO_PIM_SPECIAL: + return ("PIM/Special"); + case PROTO_PIM_STATIC: + return ("PIM/Static"); + case PROTO_DVMRP_STATIC: + return ("DVMRP/Static"); + case 0: + return ("None"); default: (void) sprintf(buf, "Unknown protocol code %d", type); return (buf); @@ -261,12 +820,20 @@ flag_type(type) return ("Hit scope boundary"); case TR_NO_RTE: return ("No route"); - case TR_OLD_ROUTER: - return ("Next router no mtrace"); case TR_NO_FWD: return ("Not forwarding"); + case TR_HIT_RP: + return ("Reached RP/Core"); + case TR_RPF_IF: + return ("RPF Interface"); + case TR_NO_MULTI: + return ("Multicast disabled"); + case TR_OLD_ROUTER: + return ("Next router no mtrace"); case TR_NO_SPACE: return ("No space in packet"); + case TR_ADMIN_PROHIB: + return ("Admin. Prohibited"); default: (void) sprintf(buf, "Unknown error code %d", type); return (buf); @@ -280,7 +847,6 @@ flag_type(type) * local net, use that one; in either case, verify that the local * address is valid. */ - u_int32 get_netmask(s, dst) int s; @@ -321,6 +887,9 @@ get_netmask(s, dst) } +/* + * Try to pick a TTL that will get past all the thresholds in the path. + */ int get_ttl(buf) struct resp_buf *buf; @@ -364,10 +933,11 @@ t_diff(a, b) * so correct and incorrect times will be far apart. */ u_long -fixtime(time) +fixtime(time, base) u_long time; + struct resp_buf *base; { - if (abs((int)(time-base.qtime)) > 0x3FFFFFFF) + if (abs((int)(time-base->qtime)) > 0x3FFFFFFF) time = ((time & 0xFFFF0000) + (JAN_1970 << 16)) + ((time & 0xFFFF) << 14) / 15625; return (time); @@ -395,10 +965,12 @@ send_recv(dst, type, code, tries, save) struct ip *ip; struct igmp *igmp; struct tr_query *query, *rquery; - int ipdatalen, iphdrlen, igmpdatalen; + struct tr_resp *r; + struct sockaddr_in recvaddr; u_int32 local, group; + int ipdatalen, iphdrlen, igmpdatalen; int datalen; - int count, recvlen, dummy = 0; + int count, recvlen, socklen = sizeof(recvaddr); int len; int i; @@ -406,7 +978,7 @@ send_recv(dst, type, code, tries, save) group = qgrp; datalen = sizeof(struct tr_query); } else { - group = htonl(MROUTED_LEVEL); + group = htonl(0xff03); datalen = 0; } if (IN_MULTICAST(ntohl(dst))) local = lcl_addr; @@ -414,14 +986,16 @@ send_recv(dst, type, code, tries, save) /* * If the reply address was not explictly specified, start off - * with the unicast address of this host. Then, if there is no - * response after trying half the tries with unicast, switch to - * the standard multicast reply address. If the TTL was also not - * specified, set a multicast TTL and if needed increase it for the - * last quarter of the tries. + * with the standard multicast reply address, or the unicast + * address of this host if the unicast flag was specified. + * Then, if there is no response after trying half the tries + * with multicast, switch to the unicast address of this host + * if the multicast flag was not specified. If the TTL was + * also not specified, set a multicast TTL and increase it + * for every try. */ query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); - query->tr_raddr = raddr ? raddr : multicast ? resp_cast : lcl_addr; + query->tr_raddr = raddr ? raddr : unicast ? lcl_addr : resp_cast; query->tr_rttl = rttl ? rttl : IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL; query->tr_src = qsrc; @@ -429,11 +1003,18 @@ send_recv(dst, type, code, tries, save) for (i = tries ; i > 0; --i) { if (tries == nqueries && raddr == 0) { - if (i == ((nqueries + 1) >> 1)) { - query->tr_raddr = resp_cast; - if (rttl == 0) query->tr_rttl = get_ttl(save); + if (i == (nqueries >> 1)) { + if (multicast && unicast) { + query->tr_raddr = resp_cast; + if (!rttl) + query->tr_rttl = get_ttl(save); + } else if (!multicast) { + query->tr_raddr = lcl_addr; + query->tr_rttl = UNICAST_TTL; + } } - if (i <= ((nqueries + 3) >> 2) && rttl == 0) { + if (i < tries && IN_MULTICAST(ntohl(query->tr_raddr)) && + rttl == 0) { query->tr_rttl += MULTICAST_TTL_INC; if (query->tr_rttl > MULTICAST_TTL_MAX) query->tr_rttl = MULTICAST_TTL_MAX; @@ -482,7 +1063,7 @@ send_recv(dst, type, code, tries, save) gettimeofday(&tr, 0); recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, - 0, (struct sockaddr *)0, &dummy); + 0, (struct sockaddr *)&recvaddr, &socklen); if (recvlen <= 0) { if (recvlen && errno != EINTR) perror("recvfrom"); @@ -519,6 +1100,8 @@ send_recv(dst, type, code, tries, save) switch (igmp->igmp_type) { case IGMP_DVMRP: + if (type != IGMP_DVMRP || code != DVMRP_ASK_NEIGHBORS2) + continue; if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue; len = igmpdatalen; /* @@ -544,9 +1127,11 @@ send_recv(dst, type, code, tries, save) case IGMP_MTRACE: /* For backward compatibility with 3.3 */ case IGMP_MTRACE_RESP: + if (type != IGMP_MTRACE) continue; if (igmpdatalen <= QLEN) continue; if ((igmpdatalen - QLEN)%RLEN) { - printf("packet with incorrect datalen\n"); + printf("packet with incomplete responses (%d bytes)\n", + igmpdatalen); continue; } @@ -558,6 +1143,7 @@ send_recv(dst, type, code, tries, save) if (rquery->tr_src != qsrc) continue; if (rquery->tr_dst != qdst) continue; len = (igmpdatalen - QLEN)/RLEN; + r = (struct tr_resp *)(rquery+1) + len - 1; /* * Ignore trace queries passing through this node when @@ -566,7 +1152,6 @@ send_recv(dst, type, code, tries, save) * for backward compatibility with multicast release 3.3). */ if (igmp->igmp_type == IGMP_MTRACE) { - struct tr_resp *r = (struct tr_resp *)(rquery+1) + len - 1; u_int32 smask; VAL_TO_MASK(smask, r->tr_smask); @@ -574,6 +1159,13 @@ send_recv(dst, type, code, tries, save) && r->tr_rmtaddr != 0 && !(r->tr_rflags & 0x80)) continue; } + /* + * Some routers will return error messages without + * filling in their addresses. We fill in the address + * for them. + */ + if (r->tr_outaddr == 0) + r->tr_outaddr = recvaddr.sin_addr.s_addr; /* * A match, we'll keep this one. @@ -625,19 +1217,26 @@ passive_mode() struct ip *ip; struct igmp *igmp; struct tr_resp *r; + struct sockaddr_in recvaddr; + struct tm *now; + char timebuf[32]; + int socklen; int ipdatalen, iphdrlen, igmpdatalen; int len, recvlen, dummy = 0; u_int32 smask; - - init_igmp(); + struct mtrace *remembered = NULL, *m, *n, **nn; + int pc = 0; if (raddr) { if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, INADDR_ANY); } else k_join(htonl(0xE0000120), INADDR_ANY); while (1) { + fflush(stdout); /* make sure previous trace is flushed */ + + socklen = sizeof(recvaddr); recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, - 0, (struct sockaddr *)0, &dummy); + 0, (struct sockaddr *)&recvaddr, &socklen); gettimeofday(&tr,0); if (recvlen <= 0) { @@ -706,16 +1305,107 @@ passive_mode() (qgrp != 0 && qgrp != igmp->igmp_group.s_addr)) continue; - printf("Mtrace from %s to %s via group %s (mxhop=%d)\n", + /* XXX This should be a hash table */ + /* XXX garbage-collection should be more efficient */ + for (nn = &remembered, n = *nn, m = 0; n; n = *nn) { + if ((n->base.qhdr.tr_src == base.qhdr.tr_src) && + (n->base.qhdr.tr_dst == base.qhdr.tr_dst) && + (n->base.igmp.igmp_group.s_addr == igmp->igmp_group.s_addr)) + m = n; + if (tr.tv_sec - n->last.tv_sec > 500) { /* XXX don't hardcode */ + *nn = n->next; + free(n); + } else { + nn = &n->next; + } + } + + if (m) + bcopy(&tr, &m->last, sizeof(tr)); + + now = localtime(&tr.tv_sec); + strftime(timebuf, sizeof(timebuf) - 1, "%b %e %k:%M:%S", now); + printf("Mtrace %s at %s", + len == 0 ? "query" : + igmp->igmp_type == IGMP_MTRACE_RESP ? "response" : + "in transit", + timebuf); + if (len == 0) + printf(" by %s", inet_fmt(recvaddr.sin_addr.s_addr, s1)); + if (!IN_MULTICAST(base.qhdr.tr_raddr)) + printf(", resp to %s", (len == 0 && recvaddr.sin_addr.s_addr == base.qhdr.tr_raddr) ? "same" : inet_fmt(base.qhdr.tr_raddr, s1)); + else + printf(", respttl %d", base.qhdr.tr_rttl); + printf(", qid %06x\n", base.qhdr.tr_qid); + printf("packet from %s to %s\n", + inet_fmt(ip->ip_src.s_addr, s1), + inet_fmt(ip->ip_dst.s_addr, s2)); + + printf("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) + if (len == 0) { + printf("\n"); continue; + } + r = base.resps + base.len - 1; + /* + * Some routers will return error messages without + * filling in their addresses. We fill in the address + * for them. + */ + if (r->tr_outaddr == 0) + r->tr_outaddr = recvaddr.sin_addr.s_addr; + + /* + * If there was a previous trace, it see if this is a + * statistics candidate. + */ + if (m && base.len == m->base.len && + !(pc = path_changed(&m->base, &base))) { + /* + * Some mtrace responders send multiple copies of the same + * reply. Skip this packet if it's exactly the same as the + * last one. + */ + if (bcmp((char *)&base.igmp, (char *)&m->prev->igmp, ipdatalen) == 0) + continue; + + ++m->nresp; + + bcopy(&base, m->new, sizeof(base)); + + printf("Results after %d seconds:\n\n", + (int)((m->new->qtime - m->base.qtime) >> 16)); + fixup_stats(&m->base, m->prev, m->new, m->bugs); + print_stats(&m->base, m->prev, m->new, m->bugs); + m->prev = m->new; + m->new = &m->incr[(m->nresp & 1)]; + + continue; + } + + if (m == NULL) { + m = (struct mtrace *)malloc(sizeof(struct mtrace)); + m->next = remembered; + remembered = m; + bcopy(&tr, &m->last, sizeof(tr)); + } + + /* Either it's a hop-by-hop in progress, or the path changed. */ + if (pc) { + printf("[Path Changed...]\n"); + bzero(m->bugs, sizeof(m->bugs)); + } + bcopy(&base, &m->base, sizeof(base)); + m->prev = &m->base; + m->new = &m->incr[0]; + m->nresp = 0; + 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)); @@ -723,7 +1413,8 @@ passive_mode() printf("\n"); } else if (r->tr_rmtaddr != 0) { printf("%3d ", -(base.len+1)); - what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ? + print_host(r->tr_rmtaddr); + printf(" %s\n", r->tr_rflags == TR_OLD_ROUTER ? "doesn't support mtrace" : "is the next hop"); } @@ -781,13 +1472,30 @@ print_trace(index, buf) for (; i <= buf->len; ++i, ++r) { if (index > 0) printf("%3d ", -i); name = print_host2(r->tr_outaddr, r->tr_inaddr); - printf(" %s thresh^ %d", proto_type(r->tr_rproto), r->tr_fttl); + if (r->tr_rflags != TR_NO_RTE) + printf(" %s thresh^ %d", proto_type(r->tr_rproto), r->tr_fttl); if (verbose) { - hop = t_diff(fixtime(ntohl(r->tr_qarr)), buf->qtime); + hop = t_diff(fixtime(ntohl(r->tr_qarr), &base), buf->qtime); ms = scale(&hop); printf(" %d%s", hop, ms); } - printf(" %s\n", flag_type(r->tr_rflags)); + printf(" %s", flag_type(r->tr_rflags)); + if (i > 1 && r->tr_outaddr != (r-1)->tr_rmtaddr) { + printf(" !RPF!"); + print_host((r-1)->tr_rmtaddr); + } + if (r->tr_rflags != TR_NO_RTE) { + if (r->tr_smask <= 1) /* MASK_TO_VAL() returns 1 for default */ + printf(" [default]"); + else if (verbose) { + u_int32 smask; + + VAL_TO_MASK(smask, r->tr_smask); + printf(" [%s]", inet_fmts(buf->qhdr.tr_src & smask, + smask, s1)); + } + } + printf("\n"); memcpy(names[i-1], name, sizeof(names[0]) - 1); names[i-1][sizeof(names[0])-1] = '\0'; } @@ -813,7 +1521,8 @@ what_kind(buf, why) u_int32 version = ntohl(incr[0].igmp.igmp_group.s_addr); u_int32 *p = (u_int32 *)incr[0].ndata; u_int32 *ep = p + (incr[0].len >> 2); - char *type = ""; + char *type = "version "; + retval = 0; switch (version & 0xFF) { case 1: @@ -821,15 +1530,8 @@ what_kind(buf, why) retval = 1; break; - case 2: - case 3: - if (((version >> 8) & 0xFF) < 3) retval = 1; - /* Fall through */ - case 4: - type = "mrouted "; - break; - case 10: + case 11: type = "cisco "; } printf(" [%s%d.%d] %s\n", @@ -880,35 +1582,54 @@ stat_line(r, s, have_next, rst) int have_next; int *rst; { - int timediff = (fixtime(ntohl(s->tr_qarr)) - - fixtime(ntohl(r->tr_qarr))) >> 16; + /* this may fail in passive statistics mode due to wrong "base". */ + int timediff = (fixtime(ntohl(s->tr_qarr), &base) - + fixtime(ntohl(r->tr_qarr), &base)) >> 16; 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]; - int have = NEITHER; - int res = *rst; + int vhave = NEITHER; + int ghave = NEITHER; + int gmissing = NEITHER; + char whochar; if (timediff == 0) timediff = 1; v_pps = v_out / timediff; g_pps = g_out / timediff; - if (v_out && (s->tr_vifout != 0xFFFFFFFF && s->tr_vifout != 0) || - (r->tr_vifout != 0xFFFFFFFF && r->tr_vifout != 0)) - have |= OUTS; +#define STATS_MISSING(x) ((x) == 0xFFFFFFFF || (x) == 0) + + if (v_out && !STATS_MISSING(s->tr_vifout) && !STATS_MISSING(r->tr_vifout)) + vhave |= OUTS; + if (STATS_MISSING(s->tr_pktcnt) || STATS_MISSING(r->tr_pktcnt)) + gmissing |= OUTS; + if (!(*rst & BUG_NOPRINT)) + ghave |= OUTS; if (have_next) { --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; + if (!STATS_MISSING(s->tr_vifin) && !STATS_MISSING(r->tr_vifin)) + vhave |= INS; + if (STATS_MISSING(s->tr_pktcnt) || STATS_MISSING(r->tr_pktcnt)) + gmissing |= INS; + if (!(*rst & BUG_NOPRINT)) + ghave |= INS; } - switch (have) { + /* + * If both hops report as missing, then it's likely that there's just + * no traffic flowing. + * + * If just one hop is missing, then we really don't have it. + */ + if (gmissing != BOTH) + ghave &= ~gmissing; + + whochar = have_next ? '^' : ' '; + switch (vhave) { 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; @@ -917,6 +1638,42 @@ stat_line(r, s, have_next, rst) sprintf(v_str, "%3d", v_pct); else memcpy(v_str, " --", 4); + if (tunstats) + printf("%6d/%-5d=%s%%", v_lost, v_out, v_str); + else + printf(" "); + printf("%4d pps", v_pps); + + break; + + case INS: + v_out = ntohl(s->tr_vifin) - ntohl(r->tr_vifin); + v_pps = v_out / timediff; + whochar = 'v'; + /* Fall through */ + + case OUTS: + if (tunstats) + printf(" %c%-5d ", whochar, v_out); + else + printf(" %c", whochar); + printf("%4d pps", v_pps); + + break; + + case NEITHER: + if (ghave != NEITHER) + if (tunstats) + printf(" "); + else + printf(" "); + + break; + } + + whochar = have_next ? '^' : ' '; + switch (ghave) { + case BOTH: 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; @@ -924,35 +1681,30 @@ stat_line(r, s, have_next, rst) 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 - printf("%6d/%-5d=%s%%%4d pps\n", - g_lost, g_out, g_str, g_pps); + printf("%s%6d/%-5d=%s%%%4d pps\n", + tunstats ? "" : " ", g_lost, g_out, g_str, g_pps); + break; + +#if 0 + case INS: + g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt); + g_pps = g_out / timediff; + whochar = 'v'; + /* Fall through */ +#endif + + case OUTS: + printf("%s ?/%-5d %4d pps\n", + tunstats ? "" : " ", g_out, g_pps); break; case INS: - v_out = ntohl(s->tr_vifin) - ntohl(r->tr_vifin); - v_pps = v_out / timediff; - /* Fall through */ - - case OUTS: - 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)); @@ -964,7 +1716,6 @@ stat_line(r, s, have_next, rst) 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); } } @@ -973,24 +1724,25 @@ stat_line(r, s, have_next, rst) * byteorder bugs in mrouted 3.6 on little-endian machines. */ void -fixup_stats(base, prev, new) +fixup_stats(base, prev, new, bugs) struct resp_buf *base, *prev, *new; + int *bugs; { 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 *r = bugs + 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) { + --n; --p; --b; + if ((*r & BUG_SWAP) || + abs(ntohl(n->tr_vifout) - ntohl(p->tr_vifout)) > 100000) { /* This host sends byteswapped reports; swap 'em */ - if (!*s) { - *s = 1; + if (!(*r & BUG_SWAP)) { + *r |= BUG_SWAP; b->tr_qarr = byteswap(b->tr_qarr); b->tr_vifin = byteswap(b->tr_vifin); b->tr_vifout = byteswap(b->tr_vifout); @@ -1002,12 +1754,33 @@ fixup_stats(base, prev, new) n->tr_vifout = byteswap(n->tr_vifout); n->tr_pktcnt = byteswap(n->tr_pktcnt); } + /* + * A missing parenthesis in mrouted 3.5-3.8's prune.c + * causes extremely bogus time diff's. + * One half of the time calculation was + * inside an htonl() and one half wasn't. Therefore, on + * a little-endian machine, both halves of the calculation + * would get added together in the little end. Thus, the + * low-order 2 bytes are either 0000 (no overflow) or + * 0100 (overflow from the addition). + * + * Odds are against these particular bit patterns + * happening in both prev and new for actual time values. + */ + if ((*r & BUG_BOGUSTIME) || (((ntohl(n->tr_qarr) & 0xfeff) == 0x0000) && + ((ntohl(p->tr_qarr) & 0xfeff) == 0x0000))) { + *r |= BUG_BOGUSTIME; + n->tr_qarr = new->rtime; + p->tr_qarr = prev->rtime; + b->tr_qarr = base->rtime; + } } rno = base->len; b = base->resps + rno; p = prev->resps + rno; n = new->resps + rno; + r = bugs + rno; while (--rno >= 0) { --n; --p; --b; --r; @@ -1015,8 +1788,8 @@ fixup_stats(base, prev, new) (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) { + if (*r & BUG_RESET) { + if (res || (*r & BUG_RESET2X)) { /* * This router appears to be a 3.4 with that nasty ol' * neighbor version bug, which causes it to constantly @@ -1025,7 +1798,7 @@ fixup_stats(base, prev, new) * doubt from now on. */ p->tr_pktcnt = b->tr_pktcnt = n->tr_pktcnt; - *r++; + *r |= BUG_RESET2X; } else { /* * This is simply the situation that the original @@ -1033,11 +1806,12 @@ fixup_stats(base, prev, new) * 3.3 or 3.4 router deleted a cache entry while * traffic was still active. */ - *r = 0; + *r &= ~BUG_RESET; break; } } else - *r = res; + if (res) + *r |= BUG_RESET; } if (rno < 0) return; @@ -1053,8 +1827,9 @@ fixup_stats(base, prev, new) * Print responses with statistics for forward path (from src to dst) */ int -print_stats(base, prev, new) +print_stats(base, prev, new, bugs) struct resp_buf *base, *prev, *new; + int *bugs; { int rtt, hop; char *ms; @@ -1063,27 +1838,44 @@ print_stats(base, prev, new) 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 *r = bugs + rno; u_long resptime = new->rtime; - u_long qarrtime = fixtime(ntohl(n->tr_qarr)); - u_int ttl = n->tr_fttl; + u_long qarrtime = fixtime(ntohl(n->tr_qarr), base); + u_int ttl = n->tr_fttl + 1; int first = (base == prev); VAL_TO_MASK(smask, b->tr_smask); - printf(" Source Response Dest"); - printf(" Packet Statistics For Only For Traffic\n"); - printf("%-15s %-15s All Multicast Traffic From %s\n", - ((b->tr_inaddr & smask) == (qsrc & smask)) ? s1 : " * * * ", - inet_fmt(base->qhdr.tr_raddr, s2), inet_fmt(qsrc, s1)); + printf(" Source Response Dest "); + if (tunstats) + printf("Packet Statistics For Only For Traffic\n"); + else + printf("Overall Packet Statistics For Traffic From\n"); + (void)inet_fmt(base->qhdr.tr_src, s1); + printf("%-15s %-15s ", + ((b->tr_inaddr & smask) == (base->qhdr.tr_src & smask)) ? + s1 : " * * * ", + inet_fmt(base->qhdr.tr_raddr, s2)); + (void)inet_fmt(base->igmp.igmp_group.s_addr, s2); + if (tunstats) + printf("All Multicast Traffic From %s\n", s1); + else + printf("Packet %s To %s\n", s1, s2); rtt = t_diff(resptime, new->qtime); ms = scale(&rtt); - printf(" %c __/ rtt%5d%s Lost/Sent = Pct Rate To %s\n", - first ? 'v' : '|', rtt, ms, inet_fmt(qgrp, s2)); - if (!first) { + printf(" %c __/ rtt%5d%s ", + (first && !verbose) ? 'v' : '|', rtt, ms); + if (tunstats) + printf("Lost/Sent = Pct Rate To %s\n", s2); + else + printf(" Rate Lost/Sent = Pct Rate\n"); + if (!first || verbose) { hop = t_diff(resptime, qarrtime); ms = scale(&hop); - printf(" v / hop%5d%s", hop, ms); - printf(" --------------------- --------------------\n"); + printf(" v / hop%5d%s ", hop, ms); + if (tunstats) + printf("--------------------- --------------------\n"); + else + printf("------- ---------------------\n"); } if (debug > 2) { printf("\t\t\t\tv_in: %ld ", ntohl(n->tr_vifin)); @@ -1095,49 +1887,86 @@ 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); + printf("\t\t\t\treset: %x\n", *r); } while (TRUE) { - if ((n->tr_inaddr != b->tr_inaddr) || (n->tr_inaddr != b->tr_inaddr)) + if ((n->tr_inaddr != b->tr_inaddr) || + (n->tr_outaddr != b->tr_outaddr) || + (n->tr_rmtaddr != b->tr_rmtaddr)) return 1; /* Route changed */ - if ((n->tr_inaddr != n->tr_outaddr)) + if ((n->tr_inaddr != n->tr_outaddr) && n->tr_inaddr) printf("%-15s\n", inet_fmt(n->tr_inaddr, s1)); - printf("%-15s %-14s %s\n", inet_fmt(n->tr_outaddr, s1), names[rno], - flag_type(n->tr_rflags)); + printf("%-15s %-14s %s%s\n", inet_fmt(n->tr_outaddr, s1), names[rno], + flag_type(n->tr_rflags), + (*r & BUG_NOPRINT) ? " [reset counters]" : ""); if (rno-- < 1) break; - printf(" %c ^ ttl%5d ", first ? 'v' : '|', ttl); + printf(" %c ^ ttl%5d ", (first && !verbose) ? 'v' : '|', + ttl); stat_line(p, n, TRUE, r); - if (!first) { + if (!first || verbose) { resptime = qarrtime; - qarrtime = fixtime(ntohl((n-1)->tr_qarr)); + qarrtime = fixtime(ntohl((n-1)->tr_qarr), base); hop = t_diff(resptime, qarrtime); ms = scale(&hop); printf(" v | hop%5d%s", hop, ms); - stat_line(b, n, TRUE, r); + if (first) + printf("\n"); + else + stat_line(b, n, TRUE, r); } --b, --p, --n, --r; - if (ttl < n->tr_fttl) ttl = n->tr_fttl; - else ++ttl; + ttl = MaX(ttl, n->tr_fttl + base->len - rno); } - printf(" %c \\__ ttl%5d ", first ? 'v' : '|', ttl); + printf(" %c \\__ ttl%5d ", (first && !verbose) ? 'v' : '|', + ttl); stat_line(p, n, FALSE, r); - if (!first) { + if (!first || verbose) { hop = t_diff(qarrtime, new->qtime); ms = scale(&hop); printf(" v \\ hop%5d%s", hop, ms); - stat_line(b, n, FALSE, r); + if (first) + printf("\n"); + else + stat_line(b, n, FALSE, r); } - printf("%-15s %s\n", inet_fmt(qdst, s1), inet_fmt(lcl_addr, s2)); + /* lcl_addr is 0 in passive mode, where we don't know the query source. */ + printf("%-15s %s\n", inet_fmt(base->qhdr.tr_dst, s1), + lcl_addr ? inet_fmt(lcl_addr, s2) : " * * * "); printf(" Receiver Query Source\n\n"); return 0; } +/* + * Determine whether or not the path has changed. + */ +int +path_changed(base, new) + struct resp_buf *base, *new; +{ + int rno = base->len - 1; + struct tr_resp *b = base->resps + rno; + struct tr_resp *n = new->resps + rno; + + while (rno-- >= 0) { + if ((n->tr_inaddr != b->tr_inaddr) || + (n->tr_outaddr != b->tr_outaddr) || + (n->tr_rmtaddr != b->tr_rmtaddr)) + return 1; /* Route changed */ + if ((b->tr_rflags == TR_NO_RTE) && + (n->tr_rflags != TR_NO_RTE)) + return 1; /* Route got longer? */ + --n; + --b; + } + return 0; +} + /*************************************************************************** * main @@ -1162,12 +1991,16 @@ char *argv[]; int numstats = 1; int waittime; int seed; + int hopbyhop; if (geteuid() != 0) { fprintf(stderr, "mtrace: must be root\n"); exit(1); } + init_igmp(); + setuid(getuid()); + argv++, argc--; if (argc == 0) goto usage; @@ -1194,6 +2027,43 @@ char *argv[]; case 'M': /* Use multicast for reponse */ multicast = TRUE; break; + case 'U': /* Use unicast for response */ + unicast = TRUE; + break; + case 'O': /* Don't use IP options */ + sendopts = FALSE; + break; + case 'P': /* Just watch the path */ + printstats = FALSE; + numstats = 3153600; + break; + case 'T': /* Print confusing tunnel stats */ + tunstats = TRUE; + break; + case 'W': /* Cisco's "weak" mtrace */ + weak = TRUE; + break; + case 'V': /* Print version and exit */ + { + char *p = strchr(rcsid, ','); + + while (p && *(p+1) != 'v') + p = strchr(p + 1, ','); + + if (p) { + char *q; + + p += 3; /* , v sp */ + q = strchr(p, ' '); + if (q) + *q = '\0'; + fprintf(stderr, "mtrace version %s\n", p); + } else { + fprintf(stderr, "mtrace could not determine version number!?\n"); + } + exit(1); + } + break; case 'l': /* Loop updating stats indefinitely */ numstats = 3153600; break; @@ -1249,6 +2119,14 @@ char *argv[]; break; } else goto usage; + case 'e': /* Extra hops past non-responder */ + if (arg && isdigit(*arg)) { + extrahops = atoi(arg); + if (extrahops < 0) extrahops = 0; + 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--; @@ -1276,7 +2154,16 @@ char *argv[]; } if (argc > 0 && (qsrc = host_addr(argv[0]))) { /* Source of path */ - if (IN_MULTICAST(ntohl(qsrc))) goto usage; + if (IN_MULTICAST(ntohl(qsrc))) { + if (gwy) { + /* Should probably rewrite arg parsing at some point, as + * this makes "mtrace -g foo 224.1.2.3 224.2.3.4" valid!... */ + qgrp = qsrc; + qsrc = 0; + } else { + goto usage; + } + } argv++, argc--; if (argc > 0 && (qdst = host_addr(argv[0]))) { /* Dest of path */ argv++, argc--; @@ -1297,15 +2184,13 @@ char *argv[]; return(0); } - if (argc > 0 || qsrc == 0) { + if (argc > 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(); - /* * Set useful defaults for as many parameters as possible. */ @@ -1313,7 +2198,18 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ 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) { + if (!weak) + qgrp = defgrp; + if (printstats && numstats != 0 && !tunstats) { + /* Stats are useless without a group */ + fprintf(stderr, "mtrace: WARNING: no multicast group specified, so no statistics printed\n"); + numstats = 0; + } + } else { + if (weak) + fprintf(stderr, "mtrace: WARNING: group was specified so not performing \"weak\" mtrace\n"); + } /* * Get default local address for multicasts to use in setting defaults. @@ -1322,7 +2218,7 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ #if (defined(BSD) && (BSD >= 199103)) addr.sin_len = sizeof(addr); #endif - addr.sin_addr.s_addr = qgrp; + addr.sin_addr.s_addr = qgrp ? qgrp : query_cast; addr.sin_port = htons(2000); /* Any port above 1024 will do */ if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) || @@ -1364,8 +2260,20 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ /* * Default destination for path to be queried is the local host. + * When gateway specified, default destination is that gateway + * and default source is local host. */ - if (qdst == 0) qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr; + if (qdst == 0) { + qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr; + dst_netmask = get_netmask(udp, qdst); + if (gwy && (gwy & dst_netmask) != (qdst & dst_netmask) && + !IN_MULTICAST(ntohl(gwy))) + qdst = gwy; + } + if (qsrc == 0 && gwy) + qsrc = lcl_addr ? lcl_addr : addr.sin_addr.s_addr; + if (qsrc == 0) + goto usage; dst_netmask = get_netmask(udp, qdst); close(udp); if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr; @@ -1383,15 +2291,25 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ /* * Protect against unicast queries to mrouted versions that might crash. + * Also use the obsolete "can mtrace" neighbor bit to warn about + * older implementations. */ 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) { + int flags = ntohl(incr[0].igmp.igmp_group.s_addr); + int version = flags & 0xFFFF; + int info = (flags & 0xFF0000) >> 16; + + 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); } + if ((info & 0x08) == 0) { + printf("mtrace: "); + print_host(gwy); + printf(" probably doesn't support mtrace, trying anyway...\n"); + } } printf("Mtrace from %s to %s via group %s\n", @@ -1410,6 +2328,8 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr); } else k_join(resp_cast, lcl_addr); + memset(&base, 0, sizeof(base)); + /* * If the destination is on the local net, the last-hop router can * be found by multicast to the all-routers multicast group. @@ -1417,12 +2337,14 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ * query since by definition the last-hop router will be a member. * Set default TTLs for local remote multicasts. */ - restart: - if (gwy == 0) if ((qdst & dst_netmask) == (lcl_addr & dst_netmask)) tdst = query_cast; else tdst = qgrp; else tdst = gwy; + if (tdst == 0 && weak) { + fprintf(stderr, "mtrace: -W requires -g if destination is not local.\n"); + exit(1); + } if (IN_MULTICAST(ntohl(tdst))) { k_set_loop(1); /* If I am running on a router, I need to hear this */ @@ -1452,18 +2374,19 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ /* * 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 there is no response for four hops. The extra - * hops allow getting past an mtrace-capable mrouter that can't + * from one until there is no response for extrahops more hops. The + * extra hops allow getting past an mtrace-capable mrouter that can't * send multicast packets because all phyints are disabled. */ if (recvlen) { + hopbyhop = FALSE; printf("\n 0 "); print_host(qdst); printf("\n"); print_trace(1, &base); r = base.resps + base.len - 1; if (r->tr_rflags == TR_OLD_ROUTER || r->tr_rflags == TR_NO_SPACE || - qno != 0) { + (qno != 0 && r->tr_rmtaddr != 0)) { printf("%3d ", -(base.len+1)); what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ? "doesn't support mtrace" @@ -1477,6 +2400,7 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ } } } else if (qno == 0) { + hopbyhop = TRUE; printf("switching to hop-by-hop:\n 0 "); print_host(qdst); printf("\n"); @@ -1507,7 +2431,9 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ * not-responding router is the first-hop. */ break; } - } else if (hops < nexthop + 3) { + if (extrahops == 0) + break; + } else if (hops < nexthop + extrahops) { printf("\n"); } else { printf("...giving up\n"); @@ -1515,7 +2441,6 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ } continue; } - r = base.resps + base.len - 1; if (base.len == hops && (hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) { if (hops == nexthop) { @@ -1557,6 +2482,8 @@ Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ print_trace(1, &base); } } +continuehop: + r = base.resps + base.len - 1; lastout = r->tr_outaddr; if (base.len < hops || @@ -1599,7 +2526,18 @@ or multicast at ttl %d doesn't reach its last-hop router for that source\n", exit(1); } - printf("Round trip time %d ms\n\n", t_diff(base.rtime, base.qtime)); + printf("Round trip time %d ms; ", t_diff(base.rtime, base.qtime)); + { + struct tr_resp *n = base.resps + base.len - 1; + u_int ttl = n->tr_fttl + 1; + + rno = base.len - 1; + while (--rno > 0) { + --n; + ttl = MaX(ttl, n->tr_fttl + base.len - rno); + } + printf("total ttl of %d required.\n\n",ttl); + } /* * Use the saved response which was the longest one received, @@ -1612,45 +2550,59 @@ or multicast at ttl %d doesn't reach its last-hop router for that source\n", prev = &base; new = &incr[numstats&1]; + /* + * Zero out bug-avoidance counters + */ + memset(bugs, 0, sizeof(bugs)); + + if (!printstats) + printf("Monitoring path.."); + while (numstats--) { if (waittime < 1) printf("\n"); else { - printf("Waiting to accumulate statistics... "); + printf("%s", printstats ? "Waiting to accumulate statistics... " + : "."); fflush(stdout); sleep((unsigned)waittime); } - rno = base.len; + rno = hopbyhop ? base.len : qno ? qno : MAXHOPS; recvlen = send_recv(tdst, IGMP_MTRACE, rno, nqueries, new); if (recvlen == 0) { printf("Timed out.\n"); - exit(1); + if (numstats) { + numstats++; + continue; + } else + exit(1); } - if (rno != new->len) { - 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? - */ + if (base.len != new->len || path_changed(&base, new)) { + printf("%s", base.len == new->len ? "Route changed" : + "Trace length doesn't match"); + if (!printstats) + printf(" after %d seconds", + (int)((new->qtime - base.qtime) >> 16)); + printf(":\n"); +printandcontinue: print_trace(1, new); - printf("Restarting.\n\n"); numstats++; - goto restart; + bcopy(new, &base, sizeof(base)); + nexthop = hops = new->len; + printf("Continuing with hop-by-hop...\n"); + goto continuehop; } - printf("Results after %d seconds:\n\n", - (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; + if (printstats) { + printf("Results after %d seconds:\n\n", + (int)((new->qtime - base.qtime) >> 16)); + fixup_stats(&base, prev, new, bugs); + if (print_stats(&base, prev, new, bugs)) { + printf("This should have been detected earlier, but "); + printf("Route changed:\n"); + goto printandcontinue; + } } prev = new; new = &incr[numstats&1]; @@ -1711,7 +2663,7 @@ log(severity, syserr, format, va_alist) vfprintf(stderr, fmt, ap); if (syserr == 0) fprintf(stderr, "\n"); - else if(syserr < sys_nerr) + else if (syserr < sys_nerr) fprintf(stderr, ": %s\n", sys_errlist[syserr]); else fprintf(stderr, ": errno %d\n", syserr); diff --git a/usr.sbin/mrouted/mtrace.h b/usr.sbin/mrouted/mtrace.h new file mode 100644 index 000000000000..1d704910475b --- /dev/null +++ b/usr.sbin/mrouted/mtrace.h @@ -0,0 +1,87 @@ +/* + * Multicast traceroute related definitions + * + * mtrace.h,v 5.1 1996/12/19 21:31:26 fenner Exp + */ + +/* + * The packet format for a traceroute request. + */ +struct tr_query { + u_int32 tr_src; /* traceroute source */ + u_int32 tr_dst; /* traceroute destination */ + u_int32 tr_raddr; /* traceroute response address */ +#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) + struct { + u_int qid : 24; /* traceroute query id */ + u_int ttl : 8; /* traceroute response ttl */ + } q; +#else + struct { + u_int ttl : 8; /* traceroute response ttl */ + u_int qid : 24; /* traceroute query id */ + } q; +#endif /* BYTE_ORDER */ +}; + +#define tr_rttl q.ttl +#define tr_qid q.qid + +/* + * Traceroute response format. A traceroute response has a tr_query at the + * beginning, followed by one tr_resp for each hop taken. + */ +struct tr_resp { + u_int32 tr_qarr; /* query arrival time */ + u_int32 tr_inaddr; /* incoming interface address */ + u_int32 tr_outaddr; /* outgoing interface address */ + u_int32 tr_rmtaddr; /* parent address in source tree */ + u_int32 tr_vifin; /* input packet count on interface */ + u_int32 tr_vifout; /* output packet count on interface */ + u_int32 tr_pktcnt; /* total incoming packets for src-grp */ + u_char tr_rproto; /* routing protocol deployed on router */ + u_char tr_fttl; /* ttl required to forward on outvif */ + u_char tr_smask; /* subnet mask for src addr */ + u_char tr_rflags; /* forwarding error codes */ +}; + +/* defs within mtrace */ +#define QUERY 1 +#define RESP 2 +#define QLEN sizeof(struct tr_query) +#define RLEN sizeof(struct tr_resp) + +/* fields for tr_rflags (forwarding error codes) */ +#define TR_NO_ERR 0 +#define TR_WRONG_IF 1 +#define TR_PRUNED 2 +#define TR_OPRUNED 3 +#define TR_SCOPED 4 +#define TR_NO_RTE 5 +#define TR_NO_FWD 7 +#define TR_HIT_RP 8 +#define TR_RPF_IF 9 +#define TR_NO_MULTI 10 +#define TR_NO_SPACE 0x81 +#define TR_OLD_ROUTER 0x82 +#define TR_ADMIN_PROHIB 0x83 + +/* fields for tr_rproto (routing protocol) */ +#define PROTO_DVMRP 1 +#define PROTO_MOSPF 2 +#define PROTO_PIM 3 +#define PROTO_CBT 4 +#define PROTO_PIM_SPECIAL 5 +#define PROTO_PIM_STATIC 6 +#define PROTO_DVMRP_STATIC 7 + +#define VAL_TO_MASK(x, i) { \ + x = htonl(~((1 << (32 - (i))) - 1)); \ + }; + +#if defined(__STDC__) || defined(__GNUC__) +#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ +#else +#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */ +#define const /**/ +#endif