4 * This tool traces the branch of a multicast tree from a source to a
5 * receiver for a particular multicast group and gives statistics
6 * about packet rate and loss for each hop along the path. It can
7 * usually be invoked just as
11 * to trace the route from that source to the local host for a default
12 * group when only the route is desired and not group-specific packet
13 * counts. See the usage line for more complex forms.
16 * Released 4 Apr 1995. This program was adapted by Steve Casner
17 * (USC/ISI) from a prototype written by Ajit Thyagarajan (UDel and
18 * Xerox PARC). It attempts to parallel in command syntax and output
19 * format the unicast traceroute program written by Van Jacobson (LBL)
20 * for the parts where that makes sense.
22 * Copyright (c) 1995 by the University of Southern California
23 * All rights reserved.
25 * Permission to use, copy, modify, and distribute this software and its
26 * documentation in source and binary forms for any purposes and without
27 * fee is hereby granted, provided that the above copyright notice
28 * appear in all copies and that both the copyright notice and this
29 * permission notice appear in supporting documentation, and that any
30 * documentation, advertising materials, and other materials related to
31 * such distribution and use acknowledge that the software was developed
32 * by the University of Southern California, Information Sciences
33 * Institute. The name of the University may not be used to endorse or
34 * promote products derived from this software without specific prior
37 * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
38 * the suitability of this software for any purpose. THIS SOFTWARE IS
39 * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
40 * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
41 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
43 * Other copyrights might apply to parts of this software and are so
44 * noted when applicable.
46 * Parts of this software are derived from mrouted, which has the
49 * The mrouted program is covered by the following license. Use of the
50 * mrouted program represents acceptance of these terms and conditions.
52 * 1. STANFORD grants to LICENSEE a nonexclusive and nontransferable
53 * license to use, copy and modify the computer software ``mrouted''
54 * (hereinafter called the ``Program''), upon the terms and conditions
55 * hereinafter set out and until Licensee discontinues use of the Licensed
58 * 2. LICENSEE acknowledges that the Program is a research tool still in
59 * the development state, that it is being supplied ``as is,'' without any
60 * accompanying services from STANFORD, and that this license is entered
61 * into in order to encourage scientific collaboration aimed at further
62 * development and application of the Program.
64 * 3. LICENSEE may copy the Program and may sublicense others to use
65 * object code copies of the Program or any derivative version of the
66 * Program. All copies must contain all copyright and other proprietary
67 * notices found in the Program as provided by STANFORD. Title to
68 * copyright to the Program remains with STANFORD.
70 * 4. LICENSEE may create derivative versions of the Program. LICENSEE
71 * hereby grants STANFORD a royalty-free license to use, copy, modify,
72 * distribute and sublicense any such derivative works. At the time
73 * LICENSEE provides a copy of a derivative version of the Program to a
74 * third party, LICENSEE shall provide STANFORD with one copy of the
75 * source code of the derivative version at no charge to STANFORD.
77 * 5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
78 * IMPLIED. By way of example, but not limitation, STANFORD MAKES NO
79 * REPRESENTATION OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
80 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED PROGRAM WILL NOT
81 * INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD
82 * shall not be held liable for any liability nor for any direct, indirect
83 * or consequential damages with respect to any claim by LICENSEE or any
84 * third party on account of or arising from this Agreement or use of the
87 * 6. This agreement shall be construed, interpreted and applied in
88 * accordance with the State of California and any legal action arising
89 * out of this Agreement or use of the Program shall be filed in a court
90 * in the State of California.
92 * 7. Nothing in this Agreement shall be construed as conferring rights to
93 * use in advertising, publicity or otherwise any trademark or the name
96 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
97 * Leland Stanford Junior University.
100 * The mtrace program has been modified and improved by Xerox
101 * Corporation. Xerox grants to LICENSEE a non-exclusive and
102 * non-transferable license to use, copy, and modify the Xerox modified
103 * and improved mrouted software on the same terms and conditions which
104 * govern the license Stanford and ISI grant with respect to the mtrace
105 * program. These terms and conditions are incorporated in this grant
106 * by reference and shall be deemed to have been accepted by LICENSEE
107 * to cover its relationship with Xerox Corporation with respect to any
108 * use of the Xerox improved program.
110 * The mtrace program is COPYRIGHT 1998 by Xerox Corporation.
112 * $FreeBSD: src/usr.sbin/mrouted/mtrace.c,v 1.17.2.3 2002/09/12 16:27:49 nectar Exp $
125 #include <sys/param.h>
126 #include <sys/types.h>
127 #include <sys/socket.h>
128 #include <sys/time.h>
130 #include <netinet/in.h>
131 #include <netinet/in_systm.h>
132 #include <netinet/ip.h>
133 #include <netinet/igmp.h>
134 #include <sys/ioctl.h>
136 #include <sys/sockio.h>
138 #include <arpa/inet.h>
141 #include <sys/systeminfo.h>
144 typedef unsigned int u_int32
; /* XXX */
147 #define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */
148 #define DEFAULT_RETRIES 3 /* How many times to try */
149 #define DEFAULT_EXTRAHOPS 3 /* How many hops past a non-responding rtr */
150 #define MAXHOPS 60 /* Don't need more hops than this */
151 #define UNICAST_TTL 255 /* TTL for unicast response */
152 #define MULTICAST_TTL1 127 /* Default TTL for multicast query/response */
153 #define MULTICAST_TTL_INC 32 /* TTL increment for increase after timeout */
154 #define MULTICAST_TTL_MAX 192 /* Maximum TTL allowed (protect low-BW links */
158 #define DVMRP_ASK_NEIGHBORS2 5 /* DVMRP msg requesting neighbors */
159 #define DVMRP_NEIGHBORS2 6 /* reply to above */
160 #define DVMRP_NF_DOWN 0x10 /* kernel state of interface */
161 #define DVMRP_NF_DISABLED 0x20 /* administratively disabled */
162 #define MAX_IP_PACKET_LEN 576
163 #define MIN_IP_HEADER_LEN 20
164 #define MAX_IP_HEADER_LEN 60
165 #define MAX_DVMRP_DATA_LEN \
166 ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN )
169 u_long qtime
; /* Time query was issued */
170 u_long rtime
; /* Time response was received */
171 int len
; /* Number of reports or length of data */
172 struct igmp igmp
; /* IGMP header */
175 struct tr_query q
; /* Query/response header */
176 struct tr_resp r
[MAXHOPS
]; /* Per-hop reports */
178 char d
[MAX_DVMRP_DATA_LEN
]; /* Neighbor data */
186 char *names
[MAXHOPS
];
189 * In mrouted 3.3 and 3.4 (and in some Cisco IOS releases),
190 * cache entries can get deleted even if there is traffic
191 * flowing, which will reset the per-source/group counters.
193 #define BUG_RESET 0x01
196 * Also in mrouted 3.3 and 3.4, there's a bug in neighbor
197 * version processing which can cause them to believe that
198 * the neighbor is constantly resetting. This causes them
199 * to constantly delete all their state.
201 #define BUG_RESET2X 0x02
204 * Pre-3.7 mrouted's forget to byte-swap their reports.
206 #define BUG_SWAP 0x04
209 * Pre-3.9 mrouted's forgot a parenthesis in the htonl()
210 * on the time calculation so supply bogus times.
212 #define BUG_BOGUSTIME 0x08
214 #define BUG_NOPRINT (BUG_RESET | BUG_RESET2X)
216 int bugs
[MAXHOPS
]; /* List of bugs noticed at each hop */
220 struct resp_buf base
, incr
[2];
221 struct resp_buf
*new, *prev
;
225 char *names
[MAXHOPS
];
229 int timeout
= DEFAULT_TIMEOUT
;
230 int nqueries
= DEFAULT_RETRIES
;
234 int multicast
= FALSE
;
238 int tunstats
= FALSE
;
240 int extrahops
= DEFAULT_EXTRAHOPS
;
241 int printstats
= TRUE
;
247 u_int32 defgrp
; /* Default group if not specified */
248 u_int32 query_cast
; /* All routers multicast addr */
249 u_int32 resp_cast
; /* Mtrace response multicast addr */
251 u_int32 lcl_addr
= 0; /* This host address, in NET order */
252 u_int32 dst_netmask
= 0; /* netmask to go with qdst */
255 * Query/response parameters, all initialized to zero and set later
256 * to default values or from options.
258 u_int32 qsrc
= 0; /* Source address in the query */
259 u_int32 qgrp
= 0; /* Group address in the query */
260 u_int32 qdst
= 0; /* Destination (receiver) address in query */
261 u_char qno
= 0; /* Max number of hops to query */
262 u_int32 raddr
= 0; /* Address where response should be sent */
263 int qttl
= 0; /* TTL for the query packet */
264 u_char rttl
= 0; /* TTL for the response packet */
265 u_int32 gwy
= 0; /* User-supplied last-hop router address */
266 u_int32 tdst
= 0; /* Address where trace is sent (last-hop) */
268 char s1
[19]; /* buffers to hold the string representations */
269 char s2
[19]; /* of IP addresses, to be passed to inet_fmt() */
270 char s3
[19]; /* or inet_fmts(). */
272 #if !(defined(BSD) && (BSD >= 199103))
275 extern char * sys_errlist
[];
278 #define RECV_BUF_SIZE 8192
279 char *send_buf
, *recv_buf
;
281 u_int32 allrtrs_group
;
282 char router_alert
[4]; /* Router Alert IP Option */
287 char eol
[4]; /* EOL IP Option */
288 int ip_addlen
= 0; /* Workaround for Option bug #2 */
292 * max macro, with weird case to avoid conflicts
294 #define MaX(a,b) ((a) > (b) ? (a) : (b))
296 typedef int (*callback_t
)(int, u_char
*, int, struct igmp
*, int,
297 struct sockaddr
*, int *, struct timeval
*);
299 void init_igmp(void);
300 void send_igmp(u_int32 src
, u_int32 dst
, int type
,
301 int code
, u_int32 group
,
303 int inet_cksum(u_short
*addr
, u_int len
);
304 void k_set_rcvbuf(int bufsize
);
305 void k_hdr_include(int boolv
);
306 void k_set_ttl(int t
);
307 void k_set_loop(int l
);
308 void k_set_if(u_int32 ifa
);
309 void k_join(u_int32 grp
, u_int32 ifa
);
310 void k_leave(u_int32 grp
, u_int32 ifa
);
311 char * inet_fmt(u_int32 addr
, char *s
);
312 char * inet_fmts(u_int32 addr
, u_int32 mask
, char *s
);
313 char * inet_name(u_int32 addr
);
314 u_int32
host_addr(char *name
);
315 /* u_int is promoted u_char */
316 char * proto_type(u_int type
);
317 char * flag_type(u_int type
);
319 u_int32
get_netmask(int s
, u_int32
*dst
);
320 int get_ttl(struct resp_buf
*buf
);
321 int t_diff(u_long a
, u_long b
);
322 u_long
byteswap(u_long v
);
323 int mtrace_callback(int, u_char
*, int, struct igmp
*,
324 int, struct sockaddr
*, int *,
326 int send_recv(u_int32 dst
, int type
, int code
,
327 int tries
, struct resp_buf
*save
,
328 callback_t callback
);
329 void passive_mode(void);
330 char * print_host(u_int32 addr
);
331 char * print_host2(u_int32 addr1
, u_int32 addr2
);
332 void print_trace(int idx
, struct resp_buf
*buf
,
334 int what_kind(struct resp_buf
*buf
, char *why
);
335 char * scale(int *hop
);
336 void stat_line(struct tr_resp
*r
, struct tr_resp
*s
,
337 int have_next
, int *res
);
338 void fixup_stats(struct resp_buf
*base
,
339 struct resp_buf
*prev
,
340 struct resp_buf
*new,
342 int check_thresh(int thresh
,
343 struct resp_buf
*base
,
344 struct resp_buf
*prev
,
345 struct resp_buf
*new);
346 int print_stats(struct resp_buf
*base
,
347 struct resp_buf
*prev
,
348 struct resp_buf
*new,
351 int path_changed(struct resp_buf
*base
,
352 struct resp_buf
*new);
353 void check_vif_state(void);
355 void dolog(int, int, char *, ...) __printflike(3, 4);
356 static void usage(void);
360 * Open and initialize the igmp socket, and fill in the non-changing
361 * IP header fields in the output packet buffer.
368 recv_buf
= (char *)malloc(RECV_BUF_SIZE
);
369 if (recv_buf
== NULL
)
370 dolog(LOG_ERR
, 0, "Out of memory allocating recv_buf!");
371 send_buf
= (char *)malloc(RECV_BUF_SIZE
);
372 if (send_buf
== NULL
)
373 dolog(LOG_ERR
, 0, "Out of memory allocating send_buf!");
375 if ((igmp_socket
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
)) < 0)
376 dolog(LOG_ERR
, errno
, "IGMP socket");
378 k_hdr_include(TRUE
); /* include IP header when sending */
379 k_set_rcvbuf(48*1024); /* lots of input buffering */
380 k_set_ttl(1); /* restrict multicasts to one hop */
381 k_set_loop(FALSE
); /* disable multicast loopback */
383 ip
= (struct ip
*)send_buf
;
384 ip
->ip_hl
= sizeof(struct ip
) >> 2;
385 ip
->ip_v
= IPVERSION
;
388 ip
->ip_p
= IPPROTO_IGMP
;
389 ip
->ip_ttl
= MAXTTL
; /* applies to unicasts only */
391 #ifndef INADDR_ALLRTRS_GROUP
392 #define INADDR_ALLRTRS_GROUP 0xe0000002 /* 224.0.0.2 */
394 allrtrs_group
= htonl(INADDR_ALLRTRS_GROUP
);
396 router_alert
[0] = IPOPT_RA
; /* Router Alert */
397 router_alert
[1] = 4; /* 4 bytes */
404 checkforsolarisbug(void)
406 u_int32 localhost
= htonl(0x7f000001);
413 setsockopt(igmp_socket
, IPPROTO_IP
, IP_OPTIONS
, eol
, sizeof(eol
));
415 * Check if the kernel adds the options length to the packet
416 * length. Send myself an IGMP packet of type 0 (illegal),
417 * with 4 IPOPT_EOL options, my PID (for collision detection)
418 * and 4 bytes of zero (so that the checksum works whether
419 * the 4 bytes of zero get truncated or not).
421 bzero(send_buf
+ MIN_IP_HEADER_LEN
+ IGMP_MINLEN
, 8);
422 *(int *)(send_buf
+ MIN_IP_HEADER_LEN
+ IGMP_MINLEN
) = getpid();
423 send_igmp(localhost
, localhost
, 0, 0, 0, 8);
425 int recvlen
, dummy
= 0;
427 recvlen
= recvfrom(igmp_socket
, recv_buf
, RECV_BUF_SIZE
,
429 /* 8 == 4 bytes of options and 4 bytes of PID */
430 if (recvlen
>= MIN_IP_HEADER_LEN
+ IGMP_MINLEN
+ 8) {
431 struct ip
*ip
= (struct ip
*)recv_buf
;
435 if (ip
->ip_hl
!= 6 ||
436 ip
->ip_p
!= IPPROTO_IGMP
||
437 ip
->ip_src
.s_addr
!= localhost
||
438 ip
->ip_dst
.s_addr
!= localhost
)
441 igmp
= (struct igmp
*)(recv_buf
+ (ip
->ip_hl
<< 2));
442 if (igmp
->igmp_group
.s_addr
!= 0)
444 if (igmp
->igmp_type
!= 0 || igmp
->igmp_code
!= 0)
447 p
= (int *)((char *)igmp
+ IGMP_MINLEN
);
451 #ifdef RAW_INPUT_IS_RAW
452 ip
->ip_len
= ntohs(ip
->ip_len
);
454 if (ip
->ip_len
== IGMP_MINLEN
+ 4)
456 else if (ip
->ip_len
== IGMP_MINLEN
+ 8)
459 dolog(LOG_ERR
, 0, "while checking for Solaris bug: Sent %d bytes and got back %d!", IGMP_MINLEN
+ 8, ip
->ip_len
);
468 * Construct an IGMP message in the output packet buffer. The caller may
469 * have already placed data in that buffer, of length 'datalen'. Then send
470 * the message from the interface with IP address 'src' to destination 'dst'.
473 send_igmp(u_int32 src
, u_int32 dst
, int type
, int code
, u_int32 group
,
476 struct sockaddr_in sdst
;
480 static int raset
= 0;
484 ip
= (struct ip
*)send_buf
;
485 ip
->ip_src
.s_addr
= src
;
486 ip
->ip_dst
.s_addr
= dst
;
487 ip
->ip_len
= MIN_IP_HEADER_LEN
+ IGMP_MINLEN
+ datalen
;
488 sendlen
= ip
->ip_len
;
490 ip
->ip_len
+= ip_addlen
;
492 #ifdef RAW_OUTPUT_IS_RAW
493 ip
->ip_len
= htons(ip
->ip_len
);
496 igmp
= (struct igmp
*)(send_buf
+ MIN_IP_HEADER_LEN
);
497 igmp
->igmp_type
= type
;
498 igmp
->igmp_code
= code
;
499 igmp
->igmp_group
.s_addr
= group
;
500 igmp
->igmp_cksum
= 0;
501 igmp
->igmp_cksum
= inet_cksum((u_short
*)igmp
,
502 IGMP_MINLEN
+ datalen
);
504 if (IN_MULTICAST(ntohl(dst
))) {
508 if (dst
!= allrtrs_group
)
512 if (sendopts
&& sendra
&& !raset
) {
513 setsockopt(igmp_socket
, IPPROTO_IP
, IP_OPTIONS
,
514 router_alert
, sizeof(router_alert
));
516 } else if (!sendra
&& raset
) {
519 * SunOS5 < 5.6 cannot properly reset the IP_OPTIONS "socket"
520 * option. Instead, set up a string of 4 EOL's.
522 setsockopt(igmp_socket
, IPPROTO_IP
, IP_OPTIONS
,
525 setsockopt(igmp_socket
, IPPROTO_IP
, IP_OPTIONS
,
531 bzero(&sdst
, sizeof(sdst
));
532 sdst
.sin_family
= AF_INET
;
533 #if (defined(BSD) && (BSD >= 199103))
534 sdst
.sin_len
= sizeof(sdst
);
536 sdst
.sin_addr
.s_addr
= dst
;
537 if (sendto(igmp_socket
, send_buf
, sendlen
, 0,
538 (struct sockaddr
*)&sdst
, sizeof(sdst
)) < 0) {
539 dolog(LOG_WARNING
, errno
, "sendto to %s on %s",
540 inet_fmt(dst
, s1
), inet_fmt(src
, s2
));
546 dolog(LOG_DEBUG
, 0, "SENT %s from %-15s to %s",
547 type
== IGMP_MTRACE
? "mtrace request" : "ask_neighbors",
548 src
== INADDR_ANY
? "INADDR_ANY" : inet_fmt(src
, s1
),
553 * inet_cksum extracted from:
558 * U. S. Army Ballistic Research Laboratory
560 * Modified at Uc Berkeley
563 * Public Domain. Distribution Unlimited.
567 * Checksum routine for Internet Protocol family headers (C Version)
571 inet_cksum(u_short
*addr
, u_int len
)
573 int nleft
= (int)len
;
579 * Our algorithm is simple, using a 32 bit accumulator (sum),
580 * we add sequential 16 bit words to it, and at the end, fold
581 * back all the carry bits from the top 16 bits into the lower
589 /* mop up an odd byte, if necessary */
591 *(u_char
*) (&answer
) = *(u_char
*)w
;
596 * add back carry outs from top 16 bits to low 16 bits
598 sum
= (sum
>> 16) + (sum
& 0xffff); /* add hi 16 to low 16 */
599 sum
+= (sum
>> 16); /* add carry */
600 answer
= ~sum
; /* truncate to 16 bits */
605 k_set_rcvbuf(int bufsize
)
607 if (setsockopt(igmp_socket
, SOL_SOCKET
, SO_RCVBUF
,
608 (char *)&bufsize
, sizeof(bufsize
)) < 0)
609 dolog(LOG_ERR
, errno
, "setsockopt SO_RCVBUF %u", bufsize
);
614 k_hdr_include(int boolv
)
617 if (setsockopt(igmp_socket
, IPPROTO_IP
, IP_HDRINCL
,
618 (char *)&boolv
, sizeof(boolv
)) < 0)
619 dolog(LOG_ERR
, errno
, "setsockopt IP_HDRINCL %u", boolv
);
629 if (setsockopt(igmp_socket
, IPPROTO_IP
, IP_MULTICAST_TTL
,
630 (char *)&ttl
, sizeof(ttl
)) < 0)
631 dolog(LOG_ERR
, errno
, "setsockopt IP_MULTICAST_TTL %u", ttl
);
641 if (setsockopt(igmp_socket
, IPPROTO_IP
, IP_MULTICAST_LOOP
,
642 (char *)&loop
, sizeof(loop
)) < 0)
643 dolog(LOG_ERR
, errno
, "setsockopt IP_MULTICAST_LOOP %u", loop
);
647 k_set_if(u_int32 ifa
)
652 if (setsockopt(igmp_socket
, IPPROTO_IP
, IP_MULTICAST_IF
,
653 (char *)&adr
, sizeof(adr
)) < 0)
654 dolog(LOG_ERR
, errno
, "setsockopt IP_MULTICAST_IF %s",
659 k_join(u_int32 grp
, u_int32 ifa
)
663 mreq
.imr_multiaddr
.s_addr
= grp
;
664 mreq
.imr_interface
.s_addr
= ifa
;
666 if (setsockopt(igmp_socket
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
667 (char *)&mreq
, sizeof(mreq
)) < 0)
668 dolog(LOG_WARNING
, errno
, "can't join group %s on interface %s",
669 inet_fmt(grp
, s1
), inet_fmt(ifa
, s2
));
674 k_leave(u_int32 grp
, u_int32 ifa
)
678 mreq
.imr_multiaddr
.s_addr
= grp
;
679 mreq
.imr_interface
.s_addr
= ifa
;
681 if (setsockopt(igmp_socket
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
682 (char *)&mreq
, sizeof(mreq
)) < 0)
683 dolog(LOG_WARNING
, errno
, "can't leave group %s on interface %s",
684 inet_fmt(grp
, s1
), inet_fmt(ifa
, s2
));
688 * Convert an IP address in u_long (network) format into a printable string.
691 inet_fmt(u_int32 addr
, char *s
)
696 sprintf(s
, "%u.%u.%u.%u", a
[0], a
[1], a
[2], a
[3]);
702 * Convert an IP subnet number in u_long (network) format into a printable
703 * string including the netmask as a number of bits.
706 inet_fmts(u_int32 addr
, u_int32 mask
, char *s
)
711 if ((addr
== 0) && (mask
== 0)) {
712 sprintf(s
, "default");
717 bits
= 33 - ffs(ntohl(mask
));
719 if (m
[3] != 0) sprintf(s
, "%u.%u.%u.%u/%d", a
[0], a
[1], a
[2], a
[3],
721 else if (m
[2] != 0) sprintf(s
, "%u.%u.%u/%d", a
[0], a
[1], a
[2], bits
);
722 else if (m
[1] != 0) sprintf(s
, "%u.%u/%d", a
[0], a
[1], bits
);
723 else sprintf(s
, "%u/%d", a
[0], bits
);
729 inet_name(u_int32 addr
)
733 e
= gethostbyaddr(&addr
, sizeof(addr
), AF_INET
);
735 return e
? e
->h_name
: "?";
740 host_addr(char *name
)
742 struct hostent
*e
= NULL
;
750 * Undo BSD's favor -- take fewer than 4 octets as net/subnet address
751 * if the name is all numeric.
753 for (i
= sizeof(buf
) - 7; i
> 0; --i
) {
754 if (*ip
== '.') --dots
;
755 else if (*ip
== '\0') break;
756 else if (!isdigit(*ip
)) dots
= 0; /* Not numeric, don't add zeroes */
759 for (i
= 0; i
< dots
; ++i
) {
766 e
= gethostbyname(name
);
767 if (e
&& (e
->h_length
== sizeof(addr
))) {
768 memcpy((char *)&addr
, e
->h_addr_list
[0], e
->h_length
);
769 if (e
->h_addr_list
[1])
770 fprintf(stderr
, "Warning: %s has multiple addresses, using %s\n",
771 name
, inet_fmt(addr
, s1
));
773 addr
= inet_addr(buf
);
774 if (addr
== -1 || (IN_MULTICAST(addr
) && dots
)) {
776 printf("Could not parse %s as host name or address\n", name
);
784 proto_type(u_int type
)
797 case PROTO_PIM_SPECIAL
:
798 return ("PIM/Special");
799 case PROTO_PIM_STATIC
:
800 return ("PIM/Static");
801 case PROTO_DVMRP_STATIC
:
802 return ("DVMRP/Static");
803 case PROTO_PIM_BGP4PLUS
:
804 return ("PIM/BGP4+");
805 case PROTO_CBT_SPECIAL
:
806 return ("CBT/Special");
807 case PROTO_CBT_STATIC
:
808 return ("CBT/Static");
809 case PROTO_PIM_ASSERT
:
810 return ("PIM/Assert");
814 sprintf(buf
, "Unknown protocol code %d", type
);
821 flag_type(u_int type
)
829 return ("Wrong interface");
831 return ("Prune sent upstream");
833 return ("Output pruned");
835 return ("Hit scope boundary");
839 return ("Not forwarding");
841 return ("Reached RP/Core");
843 return ("RPF Interface");
845 return ("Multicast disabled");
847 return ("Next router no mtrace");
849 return ("No space in packet");
850 case TR_ADMIN_PROHIB
:
851 return ("Admin. Prohibited");
853 sprintf(buf
, "Unknown error code %d", type
);
859 * If destination is on a local net, get the netmask, else set the
860 * netmask to all ones. There are two side effects: if the local
861 * address was not explicitly set, and if the destination is on a
862 * local net, use that one; in either case, verify that the local
866 get_netmask(int s
, u_int32
*dst
)
870 struct ifreq
*ifrp
, *ifend
;
871 u_int32 if_addr
, if_mask
;
872 u_int32 retval
= 0xFFFFFFFF;
876 ifc
.ifc_len
= num_ifreq
* sizeof(struct ifreq
);
877 ifc
.ifc_buf
= malloc(ifc
.ifc_len
);
878 while (ifc
.ifc_buf
) {
879 if (ioctl(s
, SIOCGIFCONF
, (char *)&ifc
) < 0) {
880 perror("ioctl SIOCGIFCONF");
885 * If the buffer was large enough to hold all the addresses
886 * then break out, otherwise increase the buffer size and
889 * The only way to know that we definitely had enough space
890 * is to know that there was enough space for at least one
891 * more struct ifreq. ???
893 if ((num_ifreq
* sizeof(struct ifreq
)) >=
894 ifc
.ifc_len
+ sizeof(struct ifreq
))
898 ifc
.ifc_len
= num_ifreq
* sizeof(struct ifreq
);
899 ifc
.ifc_buf
= realloc(ifc
.ifc_buf
, ifc
.ifc_len
);
901 if (ifc
.ifc_buf
== NULL
) {
902 fprintf(stderr
, "getting interface list: ran out of memory");
906 ifrp
= (struct ifreq
*)ifc
.ifc_buf
;
907 ifend
= (struct ifreq
*)(ifc
.ifc_buf
+ ifc
.ifc_len
);
909 * Loop through all of the interfaces.
911 for (; ifrp
< ifend
&& !found
; ifrp
= (struct ifreq
*)((char *)ifrp
+ n
)) {
913 n
= ifrp
->ifr_addr
.sa_len
+ sizeof(ifrp
->ifr_name
);
914 if (n
< sizeof(*ifrp
))
920 * Ignore any interface for an address family other than IP.
922 if (ifrp
->ifr_addr
.sa_family
!= AF_INET
)
925 if_addr
= ((struct sockaddr_in
*)&(ifrp
->ifr_addr
))->sin_addr
.s_addr
;
926 if (ioctl(s
, SIOCGIFFLAGS
, (char *)ifrp
) < 0) {
927 fprintf(stderr
, "SIOCGIFFLAGS on ");
928 perror(ifrp
->ifr_name
);
931 if ((ifrp
->ifr_flags
& (IFF_MULTICAST
|IFF_UP
|IFF_LOOPBACK
)) !=
932 (IFF_MULTICAST
|IFF_UP
))
936 if (ioctl(s
, SIOCGIFNETMASK
, (char *)ifrp
) >= 0) {
937 if_mask
= ((struct sockaddr_in
*)&(ifrp
->ifr_addr
))->sin_addr
.s_addr
;
938 if (if_mask
!= 0 && (*dst
& if_mask
) == (if_addr
& if_mask
)) {
940 if (lcl_addr
== 0) lcl_addr
= if_addr
; /* XXX what about aliases? */
943 if (lcl_addr
== if_addr
) found
= TRUE
;
945 if (!found
&& lcl_addr
!= 0) {
946 printf("Interface address is not valid\n");
954 * Try to pick a TTL that will get past all the thresholds in the path.
957 get_ttl(struct resp_buf
*buf
)
963 if (buf
&& (rno
= buf
->len
) > 0) {
964 b
= buf
->resps
+ rno
- 1;
969 if (ttl
< b
->tr_fttl
) ttl
= b
->tr_fttl
;
972 ttl
+= MULTICAST_TTL_INC
;
973 if (ttl
< MULTICAST_TTL1
) ttl
= MULTICAST_TTL1
;
974 if (ttl
> MULTICAST_TTL_MAX
) ttl
= MULTICAST_TTL_MAX
;
976 } else return(MULTICAST_TTL1
);
980 * Calculate the difference between two 32-bit NTP timestamps and return
981 * the result in milliseconds.
984 t_diff(u_long a
, u_long b
)
988 return ((d
* 125) >> 13);
992 * Swap bytes for poor little-endian machines that don't byte-swap
997 return ((v
<< 24) | ((v
& 0xff00) << 8) |
998 ((v
>> 8) & 0xff00) | (v
>> 24));
1003 * XXX incomplete - need private callback data, too?
1004 * XXX since dst doesn't get passed through?
1007 neighbors_callback(int tmo
, u_char
*buf
, int buflen
, struct igmp
*igmp
,
1008 int igmplen
, struct sockaddr
*addr
, int *addrlen
,
1013 struct ip
*ip
= (struct ip
*)buf
;
1018 if (igmp
->igmp_code
!= DVMRP_NEIGHBORS2
)
1022 * Accept DVMRP_NEIGHBORS2 response if it comes from the
1023 * address queried or if that address is one of the local
1024 * addresses in the response.
1026 if (ip
->ip_src
.s_addr
!= dst
) {
1027 u_int32
*p
= (u_int32
*)(igmp
+ 1);
1028 u_int32
*ep
= p
+ (len
>> 2);
1030 u_int32 laddr
= *p
++;
1031 int n
= ntohl(*p
++) & 0xFF;
1033 ep
= p
+ 1; /* ensure p < ep after loop */
1046 mtrace_callback(int tmo
, u_char
*buf
, int buflen
, struct igmp
*igmp
,
1047 int igmplen
, struct sockaddr
*addr
, int *addrlen
,
1050 static u_char
*savbuf
= NULL
;
1051 static int savbuflen
;
1052 static struct sockaddr
*savaddr
;
1053 static int savaddrlen
;
1054 static struct timeval savts
;
1056 int len
= (igmplen
- QLEN
) / RLEN
;
1057 struct tr_resp
*r
= (struct tr_resp
*)((struct tr_query
*)(igmp
+ 1) + 1);
1061 * If we timed out with a packet saved, then return that packet.
1062 * send_recv won't send this same packet to the callback again.
1065 bcopy(savbuf
, buf
, savbuflen
);
1068 bcopy(savaddr
, addr
, savaddrlen
);
1070 *addrlen
= savaddrlen
;
1071 bcopy(&savts
, ts
, sizeof(savts
));
1082 * Check for IOS bug described in CSCdi68628, where a router that does
1083 * not have multicast enabled responds to an mtrace request with a 1-hop
1086 * If there is only one hop reported in the packet,
1087 * And the protocol code is 0,
1088 * And there is no previous hop,
1089 * And the forwarding information is "Not Forwarding",
1090 * And the router is not on the same subnet as the destination of the
1092 * then drop this packet. The "#if 0"'d code saves it and returns
1093 * it on timeout, but timeouts are too common (e.g. routers with
1094 * limited unicast routing tables, etc).
1096 if (len
== 1 && r
->tr_rproto
== 0 && r
->tr_rmtaddr
== 0 &&
1097 r
->tr_rflags
== TR_NO_FWD
) {
1100 VAL_TO_MASK(smask
, r
->tr_smask
);
1101 if ((r
->tr_outaddr
& smask
) != (qdst
& smask
)) {
1103 /* XXX should do this silently? */
1104 fprintf(stderr
, "mtrace: probably IOS-buggy packet from %s\n",
1105 inet_fmt(((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
, s1
));
1106 /* Save the packet to return if a timeout occurs. */
1107 savbuf
= (u_char
*)malloc(buflen
);
1108 if (savbuf
!= NULL
) {
1109 bcopy(buf
, savbuf
, buflen
);
1111 savaddr
= (struct sockaddr
*)malloc(*addrlen
);
1112 if (savaddr
!= NULL
) {
1113 bcopy(addr
, savaddr
, *addrlen
);
1114 savaddrlen
= *addrlen
;
1115 bcopy(ts
, &savts
, sizeof(savts
));
1129 send_recv(u_int32 dst
, int type
, int code
, int tries
, struct resp_buf
*save
,
1130 callback_t callback
)
1133 struct timeval tq
, tr
, tv
;
1136 struct tr_query
*query
, *rquery
;
1138 struct sockaddr_in recvaddr
;
1139 u_int32 local
, group
;
1140 int ipdatalen
, iphdrlen
, igmpdatalen
;
1142 int count
, recvlen
, socklen
= sizeof(recvaddr
);
1146 if (type
== IGMP_MTRACE
) {
1148 datalen
= sizeof(struct tr_query
);
1150 group
= htonl(0xff03);
1153 if (IN_MULTICAST(ntohl(dst
))) local
= lcl_addr
;
1154 else local
= INADDR_ANY
;
1157 * If the reply address was not explictly specified, start off
1158 * with the standard multicast reply address, or the unicast
1159 * address of this host if the unicast flag was specified.
1160 * Then, if there is no response after trying half the tries
1161 * with multicast, switch to the unicast address of this host
1162 * if the multicast flag was not specified. If the TTL was
1163 * also not specified, set a multicast TTL and increase it
1166 query
= (struct tr_query
*)(send_buf
+ MIN_IP_HEADER_LEN
+ IGMP_MINLEN
);
1167 query
->tr_raddr
= raddr
? raddr
: unicast
? lcl_addr
: resp_cast
;
1168 TR_SETTTL(query
->tr_rttlqid
, rttl
? rttl
:
1169 IN_MULTICAST(ntohl(query
->tr_raddr
)) ? get_ttl(save
) : UNICAST_TTL
);
1170 query
->tr_src
= qsrc
;
1171 query
->tr_dst
= qdst
;
1173 for (i
= tries
; i
> 0; --i
) {
1176 if (tries
== nqueries
&& raddr
== 0) {
1177 if (i
== (nqueries
>> 1)) {
1178 if (multicast
&& unicast
) {
1179 query
->tr_raddr
= resp_cast
;
1181 TR_SETTTL(query
->tr_rttlqid
, get_ttl(save
));
1182 } else if (!multicast
) {
1183 query
->tr_raddr
= lcl_addr
;
1184 TR_SETTTL(query
->tr_rttlqid
, UNICAST_TTL
);
1187 if (i
< tries
&& IN_MULTICAST(ntohl(query
->tr_raddr
)) &&
1189 TR_SETTTL(query
->tr_rttlqid
,
1190 TR_GETTTL(query
->tr_rttlqid
) + MULTICAST_TTL_INC
);
1191 if (TR_GETTTL(query
->tr_rttlqid
) > MULTICAST_TTL_MAX
)
1192 TR_SETTTL(query
->tr_rttlqid
, MULTICAST_TTL_MAX
);
1197 * Change the qid for each request sent to avoid being confused
1198 * by duplicate responses
1200 oqid
= TR_GETQID(query
->tr_rttlqid
);
1202 TR_SETQID(query
->tr_rttlqid
, staticqid
);
1205 TR_SETQID(query
->tr_rttlqid
, ((u_int32
)lrand48() >> 8));
1207 TR_SETQID(query
->tr_rttlqid
, ((u_int32
)arc4random() >> 8));
1211 * Set timer to calculate delays, then send query
1213 gettimeofday(&tq
, 0);
1214 send_igmp(local
, dst
, type
, code
, group
, datalen
);
1217 * Wait for response, discarding false alarms
1220 if (igmp_socket
>= FD_SETSIZE
)
1221 dolog(LOG_ERR
, 0, "descriptor too big");
1223 FD_SET(igmp_socket
, &fds
);
1224 gettimeofday(&tv
, 0);
1225 tv
.tv_sec
= tq
.tv_sec
+ timeout
- tv
.tv_sec
;
1226 tv
.tv_usec
= tq
.tv_usec
- tv
.tv_usec
;
1227 if (tv
.tv_usec
< 0) tv
.tv_usec
+= 1000000L, --tv
.tv_sec
;
1228 if (tv
.tv_sec
< 0) tv
.tv_sec
= tv
.tv_usec
= 0;
1230 count
= select(igmp_socket
+ 1, &fds
, NULL
, NULL
, &tv
);
1233 if (errno
!= EINTR
) warn("select");
1235 } else if (count
== 0) {
1237 * Timed out. Notify the callback.
1239 if (!callback
|| (recvlen
= (callback
)(1, recv_buf
, 0, NULL
, 0, (struct sockaddr
*)&recvaddr
, &socklen
, &tr
)) == 0) {
1246 * Data is available on the socket, so read it.
1248 gettimeofday(&tr
, 0);
1249 recvlen
= recvfrom(igmp_socket
, recv_buf
, RECV_BUF_SIZE
,
1250 0, (struct sockaddr
*)&recvaddr
, &socklen
);
1254 if (recvlen
&& errno
!= EINTR
) warn("recvfrom");
1258 if (recvlen
< sizeof(struct ip
)) {
1259 warnx("packet too short (%u bytes) for IP header", recvlen
);
1262 ip
= (struct ip
*) recv_buf
;
1263 if (ip
->ip_p
== 0) /* ignore cache creation requests */
1266 iphdrlen
= ip
->ip_hl
<< 2;
1267 #ifdef RAW_INPUT_IS_RAW
1268 ipdatalen
= ntohs(ip
->ip_len
);
1270 ipdatalen
= ip
->ip_len
;
1272 if (iphdrlen
+ ipdatalen
!= recvlen
) {
1273 warnx("packet shorter (%u bytes) than hdr+data len (%u+%u)",
1274 recvlen
, iphdrlen
, ipdatalen
);
1278 igmp
= (struct igmp
*) (recv_buf
+ iphdrlen
);
1279 igmpdatalen
= ipdatalen
- IGMP_MINLEN
;
1280 if (igmpdatalen
< 0) {
1281 warnx("IP data field too short (%u bytes) for IGMP from %s",
1282 ipdatalen
, inet_fmt(ip
->ip_src
.s_addr
, s1
));
1286 switch (igmp
->igmp_type
) {
1289 if (type
!= IGMP_DVMRP
|| code
!= DVMRP_ASK_NEIGHBORS2
)
1291 if (igmp
->igmp_code
!= DVMRP_NEIGHBORS2
) continue;
1294 * Accept DVMRP_NEIGHBORS2 response if it comes from the
1295 * address queried or if that address is one of the local
1296 * addresses in the response.
1298 if (ip
->ip_src
.s_addr
!= dst
) {
1299 u_int32
*p
= (u_int32
*)(igmp
+ 1);
1300 u_int32
*ep
= p
+ (len
>> 2);
1302 u_int32 laddr
= *p
++;
1303 int n
= ntohl(*p
++) & 0xFF;
1305 ep
= p
+ 1; /* ensure p < ep after loop */
1310 if (p
>= ep
) continue;
1314 case IGMP_MTRACE
: /* For backward compatibility with 3.3 */
1315 case IGMP_MTRACE_RESP
:
1316 if (type
!= IGMP_MTRACE
) continue;
1317 if (igmpdatalen
<= QLEN
) continue;
1318 if ((igmpdatalen
- QLEN
)%RLEN
) {
1319 printf("packet with incomplete responses (%d bytes)\n",
1325 * Ignore responses that don't match query.
1327 rquery
= (struct tr_query
*)(igmp
+ 1);
1328 if (rquery
->tr_src
!= qsrc
|| rquery
->tr_dst
!= qdst
)
1330 if (TR_GETQID(rquery
->tr_rttlqid
) !=
1331 TR_GETQID(query
->tr_rttlqid
)) {
1332 if (verbose
&& TR_GETQID(rquery
->tr_rttlqid
) == oqid
)
1336 len
= (igmpdatalen
- QLEN
)/RLEN
;
1337 r
= (struct tr_resp
*)(rquery
+1) + len
- 1;
1340 * Ignore trace queries passing through this node when
1341 * mtrace is run on an mrouter that is in the path
1342 * (needed only because IGMP_MTRACE is accepted above
1343 * for backward compatibility with multicast release 3.3).
1345 if (igmp
->igmp_type
== IGMP_MTRACE
) {
1348 VAL_TO_MASK(smask
, r
->tr_smask
);
1349 if (len
< code
&& (r
->tr_inaddr
& smask
) != (qsrc
& smask
)
1350 && r
->tr_rmtaddr
!= 0 && !(r
->tr_rflags
& 0x80))
1354 * Some routers will return error messages without
1355 * filling in their addresses. We fill in the address
1358 if (r
->tr_outaddr
== 0)
1359 r
->tr_outaddr
= recvaddr
.sin_addr
.s_addr
;
1362 * A match, we'll keep this one.
1365 warnx("num hops received (%d) exceeds request (%d)",
1368 rquery
->tr_raddr
= query
->tr_raddr
; /* Insure these are */
1369 TR_SETTTL(rquery
->tr_rttlqid
, TR_GETTTL(query
->tr_rttlqid
));
1370 /* as we sent them */
1378 * We're pretty sure we want to use this packet now,
1379 * but if the caller gave a callback function, it might
1380 * want to handle it instead. Give the callback a chance,
1381 * unless the select timed out (in which case the only way
1382 * to get here is because the callback returned a packet).
1384 if (callback
&& (count
!= 0) && ((callback
)(0, recv_buf
, recvlen
, igmp
, igmpdatalen
, (struct sockaddr
*)&recvaddr
, &socklen
, &tr
)) == 0) {
1386 * The callback function didn't like this packet.
1387 * Go try receiving another one.
1393 * Most of the sanity checking done at this point.
1394 * Return this packet we have been waiting for.
1397 save
->qtime
= ((tq
.tv_sec
+ JAN_1970
) << 16) +
1398 (tq
.tv_usec
<< 10) / 15625;
1399 save
->rtime
= ((tr
.tv_sec
+ JAN_1970
) << 16) +
1400 (tr
.tv_usec
<< 10) / 15625;
1402 bcopy((char *)igmp
, (char *)&save
->igmp
, ipdatalen
);
1411 * Most of this code is duplicated elsewhere. I'm not sure if
1412 * the duplication is absolutely required or not.
1414 * Ideally, this would keep track of ongoing statistics
1415 * collection and print out statistics. (& keep track
1416 * of h-b-h traces and only print the longest) For now,
1417 * it just snoops on what traces it can.
1427 struct sockaddr_in recvaddr
;
1431 int ipdatalen
, iphdrlen
, igmpdatalen
;
1435 struct mtrace
*remembered
= NULL
, *m
, *n
, **nn
;
1439 if (IN_MULTICAST(ntohl(raddr
))) k_join(raddr
, lcl_addr
);
1440 } else k_join(htonl(0xE0000120), lcl_addr
);
1443 fflush(stdout
); /* make sure previous trace is flushed */
1445 socklen
= sizeof(recvaddr
);
1446 recvlen
= recvfrom(igmp_socket
, recv_buf
, RECV_BUF_SIZE
,
1447 0, (struct sockaddr
*)&recvaddr
, &socklen
);
1448 gettimeofday(&tr
,0);
1451 if (recvlen
&& errno
!= EINTR
) warn("recvfrom");
1455 if (recvlen
< sizeof(struct ip
)) {
1456 warnx("packet too short (%u bytes) for IP header", recvlen
);
1459 ip
= (struct ip
*) recv_buf
;
1460 if (ip
->ip_p
== 0) /* ignore cache creation requests */
1463 iphdrlen
= ip
->ip_hl
<< 2;
1464 #ifdef RAW_INPUT_IS_RAW
1465 ipdatalen
= ntohs(ip
->ip_len
);
1467 ipdatalen
= ip
->ip_len
;
1469 if (iphdrlen
+ ipdatalen
!= recvlen
) {
1470 warnx("packet shorter (%u bytes) than hdr+data len (%u+%u)",
1471 recvlen
, iphdrlen
, ipdatalen
);
1475 igmp
= (struct igmp
*) (recv_buf
+ iphdrlen
);
1476 igmpdatalen
= ipdatalen
- IGMP_MINLEN
;
1477 if (igmpdatalen
< 0) {
1478 warnx("IP data field too short (%u bytes) for IGMP from %s",
1479 ipdatalen
, inet_fmt(ip
->ip_src
.s_addr
, s1
));
1483 switch (igmp
->igmp_type
) {
1485 case IGMP_MTRACE
: /* For backward compatibility with 3.3 */
1486 case IGMP_MTRACE_RESP
:
1487 if (igmpdatalen
< QLEN
) continue;
1488 if ((igmpdatalen
- QLEN
)%RLEN
) {
1489 printf("packet with incorrect datalen\n");
1493 len
= (igmpdatalen
- QLEN
)/RLEN
;
1501 base
.qtime
= ((tr
.tv_sec
+ JAN_1970
) << 16) +
1502 (tr
.tv_usec
<< 10) / 15625;
1503 base
.rtime
= ((tr
.tv_sec
+ JAN_1970
) << 16) +
1504 (tr
.tv_usec
<< 10) / 15625;
1506 bcopy((char *)igmp
, (char *)&base
.igmp
, ipdatalen
);
1508 * If the user specified which traces to monitor,
1509 * only accept traces that correspond to the
1512 if ((qsrc
!= 0 && qsrc
!= base
.qhdr
.tr_src
) ||
1513 (qdst
!= 0 && qdst
!= base
.qhdr
.tr_dst
) ||
1514 (qgrp
!= 0 && qgrp
!= igmp
->igmp_group
.s_addr
))
1517 /* XXX This should be a hash table */
1518 /* XXX garbage-collection should be more efficient */
1519 for (nn
= &remembered
, n
= *nn
, m
= NULL
; n
; n
= *nn
) {
1520 if ((n
->base
.qhdr
.tr_src
== base
.qhdr
.tr_src
) &&
1521 (n
->base
.qhdr
.tr_dst
== base
.qhdr
.tr_dst
) &&
1522 (n
->base
.igmp
.igmp_group
.s_addr
== igmp
->igmp_group
.s_addr
)) {
1526 if (tr
.tv_sec
- n
->last
.tv_sec
> 500) { /* XXX don't hardcode */
1535 now
= localtime(&tr_sec
);
1536 strftime(timebuf
, sizeof(timebuf
) - 1, "%b %e %k:%M:%S", now
);
1537 printf("Mtrace %s at %s",
1538 len
== 0 ? "query" :
1539 igmp
->igmp_type
== IGMP_MTRACE_RESP
? "response" :
1543 printf(" by %s", inet_fmt(recvaddr
.sin_addr
.s_addr
, s1
));
1544 if (!IN_MULTICAST(base
.qhdr
.tr_raddr
))
1545 printf(", resp to %s", (len
== 0 && recvaddr
.sin_addr
.s_addr
== base
.qhdr
.tr_raddr
) ? "same" : inet_fmt(base
.qhdr
.tr_raddr
, s1
));
1547 printf(", respttl %d", TR_GETTTL(base
.qhdr
.tr_rttlqid
));
1548 printf(", qid %06x\n", qid
= TR_GETQID(base
.qhdr
.tr_rttlqid
));
1549 printf("packet from %s to %s\n",
1550 inet_fmt(ip
->ip_src
.s_addr
, s1
),
1551 inet_fmt(ip
->ip_dst
.s_addr
, s2
));
1553 printf("from %s to %s via group %s (mxhop=%d)\n",
1554 inet_fmt(base
.qhdr
.tr_dst
, s1
), inet_fmt(base
.qhdr
.tr_src
, s2
),
1555 inet_fmt(igmp
->igmp_group
.s_addr
, s3
), igmp
->igmp_code
);
1560 r
= base
.resps
+ base
.len
- 1;
1562 * Some routers will return error messages without
1563 * filling in their addresses. We fill in the address
1566 if (r
->tr_outaddr
== 0)
1567 r
->tr_outaddr
= recvaddr
.sin_addr
.s_addr
;
1570 * If there was a previous trace, it see if this is a
1571 * statistics candidate.
1573 if (m
&& base
.len
== m
->base
.len
&&
1574 !(pc
= path_changed(&m
->base
, &base
))) {
1576 * Some mtrace responders send multiple copies of the same
1577 * reply. Skip this packet if it's got the same query-id
1580 if (m
->lastqid
== qid
) {
1581 printf("Skipping duplicate reply\n");
1589 bcopy(&base
, m
->new, sizeof(base
));
1591 printf("Results after %d seconds:\n\n",
1592 (int)((m
->new->qtime
- m
->base
.qtime
) >> 16));
1593 fixup_stats(&m
->base
, m
->prev
, m
->new, m
->bugs
);
1594 print_stats(&m
->base
, m
->prev
, m
->new, m
->bugs
, m
->names
);
1596 m
->new = &m
->incr
[(m
->nresp
& 1)];
1602 m
= (struct mtrace
*)malloc(sizeof(struct mtrace
));
1604 fprintf(stderr
, "Out of memory!\n");
1607 bzero(m
, sizeof(struct mtrace
));
1608 m
->next
= remembered
;
1610 bcopy(&tr
, &m
->last
, sizeof(tr
));
1613 /* Either it's a hop-by-hop in progress, or the path changed. */
1615 printf("[Path Changed...]\n");
1616 bzero(m
->bugs
, sizeof(m
->bugs
));
1618 bcopy(&base
, &m
->base
, sizeof(base
));
1620 m
->new = &m
->incr
[0];
1624 print_host(base
.qhdr
.tr_dst
);
1626 print_trace(1, &base
, m
->names
);
1627 VAL_TO_MASK(smask
, r
->tr_smask
);
1628 if ((r
->tr_inaddr
& smask
) == (base
.qhdr
.tr_src
& smask
)) {
1629 printf("%3d ", -(base
.len
+1));
1630 print_host(base
.qhdr
.tr_src
);
1632 } else if (r
->tr_rmtaddr
!= 0) {
1633 printf("%3d ", -(base
.len
+1));
1634 print_host(r
->tr_rmtaddr
);
1635 printf(" %s\n", r
->tr_rflags
== TR_OLD_ROUTER
?
1636 "doesn't support mtrace"
1637 : "is the next hop");
1644 print_host(u_int32 addr
)
1646 return print_host2(addr
, 0);
1650 * On some routers, one interface has a name and the other doesn't.
1651 * We always print the address of the outgoing interface, but can
1652 * sometimes get the name from the incoming interface. This might be
1653 * confusing but should be slightly more helpful than just a "?".
1656 print_host2(u_int32 addr1
, u_int32 addr2
)
1661 printf("%s", inet_fmt(addr1
, s1
));
1664 name
= inet_name(addr1
);
1665 if (*name
== '?' && *(name
+ 1) == '\0' && addr2
!= 0)
1666 name
= inet_name(addr2
);
1667 printf("%s (%s)", name
, inet_fmt(addr1
, s1
));
1672 * Print responses as received (reverse path from dst to src)
1675 print_trace(int idx
, struct resp_buf
*buf
, char **names
)
1684 r
= buf
->resps
+ i
- 1;
1686 for (; i
<= buf
->len
; ++i
, ++r
) {
1687 if (idx
> 0) printf("%3d ", -i
);
1688 name
= print_host2(r
->tr_outaddr
, r
->tr_inaddr
);
1689 if (r
->tr_rflags
!= TR_NO_RTE
)
1690 printf(" %s thresh^ %d", proto_type(r
->tr_rproto
), r
->tr_fttl
);
1692 hop
= t_diff(ntohl(r
->tr_qarr
), buf
->qtime
);
1694 printf(" %d%s", hop
, ms
);
1696 printf(" %s", flag_type(r
->tr_rflags
));
1697 if (i
> 1 && r
->tr_outaddr
!= (r
-1)->tr_rmtaddr
) {
1699 print_host((r
-1)->tr_rmtaddr
);
1701 if (r
->tr_rflags
!= TR_NO_RTE
) {
1702 if (r
->tr_smask
<= 1) /* MASK_TO_VAL() returns 1 for default */
1703 printf(" [default]");
1707 VAL_TO_MASK(smask
, r
->tr_smask
);
1708 printf(" [%s]", inet_fmts(buf
->qhdr
.tr_src
& smask
,
1715 names
[i
-1]=malloc(strlen(name
) + 1);
1716 strcpy(names
[i
-1], name
);
1721 * See what kind of router is the next hop
1724 what_kind(struct resp_buf
*buf
, char *why
)
1728 int hops
= buf
->len
;
1729 struct tr_resp
*r
= buf
->resps
+ hops
- 1;
1730 u_int32 next
= r
->tr_rmtaddr
;
1732 retval
= send_recv(next
, IGMP_DVMRP
, DVMRP_ASK_NEIGHBORS2
, 1, &incr
[0], NULL
);
1735 u_int32 version
= ntohl(incr
[0].igmp
.igmp_group
.s_addr
);
1736 u_int32
*p
= (u_int32
*)incr
[0].ndata
;
1737 u_int32
*ep
= p
+ (incr
[0].len
>> 2);
1738 char *type
= "version ";
1741 switch (version
& 0xFF) {
1743 type
= "proteon/mrouted ";
1751 printf(" [%s%d.%d] %s\n",
1752 type
, version
& 0xFF, (version
>> 8) & 0xFF,
1754 VAL_TO_MASK(smask
, r
->tr_smask
);
1756 u_int32 laddr
= *p
++;
1757 int flags
= (ntohl(*p
) & 0xFF00) >> 8;
1758 int n
= ntohl(*p
++) & 0xFF;
1759 if (!(flags
& (DVMRP_NF_DOWN
| DVMRP_NF_DISABLED
)) &&
1760 (laddr
& smask
) == (qsrc
& smask
)) {
1761 printf("%3d ", -(hops
+2));
1770 printf(" %s\n", why
);
1778 if (*hop
> -1000 && *hop
< 10000)
1781 if (*hop
> -1000 && *hop
< 10000)
1787 * Calculate and print one line of packet loss and packet rate statistics.
1788 * Checks for count of all ones from mrouted 2.3 that doesn't have counters.
1795 stat_line(struct tr_resp
*r
, struct tr_resp
*s
, int have_next
, int *rst
)
1797 int timediff
= (ntohl(s
->tr_qarr
) - ntohl(r
->tr_qarr
)) >> 16;
1800 int v_out
= ntohl(s
->tr_vifout
) - ntohl(r
->tr_vifout
);
1801 int g_out
= ntohl(s
->tr_pktcnt
) - ntohl(r
->tr_pktcnt
);
1803 char v_str
[8], g_str
[8];
1804 int vhave
= NEITHER
;
1805 int ghave
= NEITHER
;
1806 int gmissing
= NEITHER
;
1810 if (timediff
== 0) {
1812 /* Might be 32 bits of int seconds instead of 16int+16frac */
1813 timediff
= ntohl(s
->tr_qarr
) - ntohl(r
->tr_qarr
);
1814 if (timediff
== 0 || abs(timediff
- statint
) > statint
)
1817 v_pps
= v_out
/ timediff
;
1818 g_pps
= g_out
/ timediff
;
1820 #define STATS_MISSING(x) ((x) == 0xFFFFFFFF)
1822 if (!STATS_MISSING(s
->tr_vifout
) && !STATS_MISSING(r
->tr_vifout
))
1824 if (STATS_MISSING(s
->tr_pktcnt
) || STATS_MISSING(r
->tr_pktcnt
))
1826 if (!(*rst
& BUG_NOPRINT
))
1831 if (!STATS_MISSING(s
->tr_vifin
) && !STATS_MISSING(r
->tr_vifin
))
1833 if (STATS_MISSING(s
->tr_pktcnt
) || STATS_MISSING(r
->tr_pktcnt
))
1835 if (!(*rst
& BUG_NOPRINT
))
1840 * Stats can be missing for any number of reasons:
1841 * - The hop may not be capable of collecting stats
1842 * - Traffic may be getting dropped at the previous hop
1843 * and so this hop may not have any state
1845 * We need a stronger heuristic to tell between these
1846 * two cases; in case 1 we don't want to print the stats
1847 * and in case 2 we want to print 100% loss. We used to
1848 * err on the side of not printing, which is less useful
1849 * than printing 100% loss and dealing with it.
1853 * If both hops report as missing, then it's likely that there's just
1854 * no traffic flowing.
1856 * If just one hop is missing, then we really don't have it.
1858 if (gmissing
!= BOTH
)
1862 whochar
= have_next
? '^' : ' ';
1865 v_lost
= v_out
- (ntohl(s
->tr_vifin
) - ntohl(r
->tr_vifin
));
1866 if (v_out
) v_pct
= v_lost
* 100 / v_out
;
1868 if (-20 < v_pct
&& v_pct
< 101 && v_out
> 10)
1869 sprintf(v_str
, "%3d%%", v_pct
);
1870 else if (v_pct
< -900 && v_out
> 10)
1871 sprintf(v_str
, "%3dx", (int)(-v_pct
/ 100. + 1.));
1872 else if (v_pct
<= -20 && v_out
> 10)
1873 sprintf(v_str
, "%1.1fx", -v_pct
/ 100. + 1.);
1875 memcpy(v_str
, " -- ", 5);
1878 printf("%6d/%-5d=%s", v_lost
, v_out
, v_str
);
1881 printf("%4d pps", v_pps
);
1882 if (v_pps
&& badtime
)
1888 v_out
= ntohl(s
->tr_vifin
) - ntohl(r
->tr_vifin
);
1889 v_pps
= v_out
/ timediff
;
1895 printf(" %c%-5d ", whochar
, v_out
);
1897 printf(" %c", whochar
);
1898 printf("%4d pps", v_pps
);
1899 if (v_pps
&& badtime
)
1905 if (ghave
!= NEITHER
) {
1915 whochar
= have_next
? '^' : ' ';
1918 g_lost
= g_out
- (ntohl(s
->tr_pktcnt
) - ntohl(r
->tr_pktcnt
));
1919 if (g_out
) g_pct
= g_lost
* 100 / g_out
;
1921 if (-20 < g_pct
&& g_pct
< 101 && g_out
> 10)
1922 sprintf(g_str
, "%3d%%", g_pct
);
1923 else if (g_pct
< -900 && g_out
> 10)
1924 sprintf(g_str
, "%3dx", (int)(-g_pct
/ 100. + 1.));
1925 else if (g_pct
<= -20 && g_out
> 10)
1926 sprintf(g_str
, "%1.1fx", -g_pct
/ 100. + 1.);
1928 memcpy(g_str
, " -- ", 5);
1930 printf("%s%6d/%-5d=%s%4d pps",
1931 tunstats
? "" : " ", g_lost
, g_out
, g_str
, g_pps
);
1932 if (g_pps
&& badtime
)
1939 g_out
= ntohl(s
->tr_pktcnt
) - ntohl(r
->tr_pktcnt
);
1940 g_pps
= g_out
/ timediff
;
1946 printf("%s ?/%-5d %4d pps",
1947 tunstats
? "" : " ", g_out
, g_pps
);
1961 printf("\t\t\t\tv_in: %ld ", (long)ntohl(s
->tr_vifin
));
1962 printf("v_out: %ld ", (long)ntohl(s
->tr_vifout
));
1963 printf("pkts: %ld\n", (long)ntohl(s
->tr_pktcnt
));
1964 printf("\t\t\t\tv_in: %ld ", (long)ntohl(r
->tr_vifin
));
1965 printf("v_out: %ld ", (long)ntohl(r
->tr_vifout
));
1966 printf("pkts: %ld\n", (long)ntohl(r
->tr_pktcnt
));
1967 printf("\t\t\t\tv_in: %ld ",
1968 (long)(ntohl(s
->tr_vifin
) - ntohl(r
->tr_vifin
)));
1969 printf("v_out: %ld ",
1970 (long)(ntohl(s
->tr_vifout
) - ntohl(r
->tr_vifout
)));
1971 printf("pkts: %ld ", (long)(ntohl(s
->tr_pktcnt
) - ntohl(r
->tr_pktcnt
)));
1972 printf("time: %d\n", timediff
);
1973 printf("\t\t\t\treset: %x hoptime: %x\n", *rst
, ntohl(s
->tr_qarr
));
1978 * A fixup to check if any pktcnt has been reset, and to fix the
1979 * byteorder bugs in mrouted 3.6 on little-endian machines.
1981 * XXX Since periodic traffic sources are likely to have their
1982 * pktcnt periodically reset, should we save old values when
1983 * the reset occurs to keep slightly better statistics over
1984 * the long term? (e.g. SAP)
1987 fixup_stats(struct resp_buf
*base
, struct resp_buf
*prev
, struct resp_buf
*new,
1990 int rno
= base
->len
;
1991 struct tr_resp
*b
= base
->resps
+ rno
;
1992 struct tr_resp
*p
= prev
->resps
+ rno
;
1993 struct tr_resp
*n
= new->resps
+ rno
;
1994 int *r
= bugs
+ rno
;
1998 /* Check for byte-swappers. Only check on the first trace,
1999 * since long-running traces can wrap around and falsely trigger. */
2000 while (--rno
>= 0) {
2002 u_int32 nvifout
= ntohl(n
->tr_vifout
);
2003 u_int32 pvifout
= ntohl(p
->tr_vifout
);
2006 #ifdef TEST_ONLY /*XXX this is still buggy, so disable it for release */
2007 if ((*r
& BUG_SWAP
) ||
2009 (nvifout
- pvifout
) > (byteswap(nvifout
) - byteswap(pvifout
)))) {
2010 if (1 || debug
> 2) {
2011 printf("ip %s swaps; b %08x p %08x n %08x\n",
2012 inet_fmt(n
->tr_inaddr
, s1
),
2013 ntohl(b
->tr_vifout
), pvifout
, nvifout
);
2015 /* This host sends byteswapped reports; swap 'em */
2016 if (!(*r
& BUG_SWAP
)) {
2018 b
->tr_qarr
= byteswap(b
->tr_qarr
);
2019 b
->tr_vifin
= byteswap(b
->tr_vifin
);
2020 b
->tr_vifout
= byteswap(b
->tr_vifout
);
2021 b
->tr_pktcnt
= byteswap(b
->tr_pktcnt
);
2024 n
->tr_qarr
= byteswap(n
->tr_qarr
);
2025 n
->tr_vifin
= byteswap(n
->tr_vifin
);
2026 n
->tr_vifout
= byteswap(n
->tr_vifout
);
2027 n
->tr_pktcnt
= byteswap(n
->tr_pktcnt
);
2031 * A missing parenthesis in mrouted 3.5-3.8's prune.c
2032 * causes extremely bogus time diff's.
2033 * One half of the time calculation was
2034 * inside an htonl() and one half wasn't. Therefore, on
2035 * a little-endian machine, both halves of the calculation
2036 * would get added together in the little end. Thus, the
2037 * low-order 2 bytes are either 0000 (no overflow) or
2038 * 0100 (overflow from the addition).
2040 * Odds are against these particular bit patterns
2041 * happening in both prev and new for actual time values.
2043 if ((*r
& BUG_BOGUSTIME
) || (((ntohl(n
->tr_qarr
) & 0xfeff) == 0x0000) &&
2044 ((ntohl(p
->tr_qarr
) & 0xfeff) == 0x0000))) {
2045 *r
|= BUG_BOGUSTIME
;
2046 n
->tr_qarr
= new->rtime
;
2047 p
->tr_qarr
= prev
->rtime
;
2048 b
->tr_qarr
= base
->rtime
;
2053 b
= base
->resps
+ rno
;
2054 p
= prev
->resps
+ rno
;
2055 n
= new->resps
+ rno
;
2058 while (--rno
>= 0) {
2061 * This hop has reset if:
2062 * - There were statistics in the base AND previous pass, AND
2063 * - There are less packets this time than the first time and
2064 * we didn't reset last time, OR
2065 * - There are less packets this time than last time, OR
2066 * - There are no statistics on this pass.
2068 * The "and we didn't reset last time" is necessary in the
2069 * first branch of the OR because if the base is large and
2070 * we reset last time but the constant-resetter-avoidance
2071 * code kicked in so we delayed the copy of prev to base,
2072 * new could still be below base so we trigger the
2073 * constant-resetter code even though it was really only
2076 res
= ((b
->tr_pktcnt
!= 0xFFFFFFFF) && (p
->tr_pktcnt
!= 0xFFFFFFFF) &&
2077 ((!(*r
& BUG_RESET
) && ntohl(n
->tr_pktcnt
) < ntohl(b
->tr_pktcnt
)) ||
2078 (ntohl(n
->tr_pktcnt
) < ntohl(p
->tr_pktcnt
)) ||
2079 (n
->tr_pktcnt
== 0xFFFFFFFF)));
2081 printf("\t\tip=%s, r=%d, res=%d\n", inet_fmt(b
->tr_inaddr
, s1
), *r
, res
);
2083 printf("\t\tbase=%u, prev=%u, new=%u\n", ntohl(b
->tr_pktcnt
),
2084 ntohl(p
->tr_pktcnt
), ntohl(n
->tr_pktcnt
));
2086 if (*r
& BUG_RESET
) {
2087 if (res
|| (*r
& BUG_RESET2X
)) {
2089 * This router appears to be a 3.4 with that nasty ol'
2090 * neighbor version bug, which causes it to constantly
2091 * reset. Just nuke the statistics for this node, and
2092 * don't even bother giving it the benefit of the
2093 * doubt from now on.
2095 p
->tr_pktcnt
= b
->tr_pktcnt
= n
->tr_pktcnt
;
2099 * This is simply the situation that the original
2100 * fixup_stats was meant to deal with -- that a
2101 * 3.3 or 3.4 router deleted a cache entry while
2102 * traffic was still active.
2112 if (cleanup
== 0) return;
2115 * If some hop reset its counters and didn't continue to
2116 * reset, then we pretend that the previous
2117 * trace was the first one.
2120 b
= base
->resps
+ rno
;
2121 p
= prev
->resps
+ rno
;
2123 while (--rno
>= 0) (--b
)->tr_pktcnt
= (--p
)->tr_pktcnt
;
2124 base
->qtime
= prev
->qtime
;
2125 base
->rtime
= prev
->rtime
;
2129 * Check per-source losses along path and compare with threshold.
2132 check_thresh(int thresh
, struct resp_buf
*base
, struct resp_buf
*prev
,
2133 struct resp_buf
*new)
2135 int rno
= base
->len
- 1;
2136 struct tr_resp
*b
= base
->resps
+ rno
;
2137 struct tr_resp
*p
= prev
->resps
+ rno
;
2138 struct tr_resp
*n
= new->resps
+ rno
;
2142 if ((n
->tr_inaddr
!= b
->tr_inaddr
) ||
2143 (n
->tr_outaddr
!= b
->tr_outaddr
) ||
2144 (n
->tr_rmtaddr
!= b
->tr_rmtaddr
))
2145 return 1; /* Route changed */
2147 if (rno
-- < 1) break;
2148 g_out
= ntohl(n
->tr_pktcnt
) - ntohl(p
->tr_pktcnt
);
2150 g_lost
= g_out
- (ntohl(n
->tr_pktcnt
) - ntohl(p
->tr_pktcnt
));
2151 if (g_out
&& ((g_lost
* 100 + (g_out
>> 1))/ g_out
) > thresh
) {
2159 * Print responses with statistics for forward path (from src to dst)
2162 print_stats(struct resp_buf
*base
, struct resp_buf
*prev
, struct resp_buf
*new,
2163 int *bugs
, char **names
)
2168 int rno
= base
->len
- 1;
2169 struct tr_resp
*b
= base
->resps
+ rno
;
2170 struct tr_resp
*p
= prev
->resps
+ rno
;
2171 struct tr_resp
*n
= new->resps
+ rno
;
2172 int *r
= bugs
+ rno
;
2173 u_long resptime
= new->rtime
;
2174 u_long qarrtime
= ntohl(n
->tr_qarr
);
2175 u_int ttl
= MaX(1, n
->tr_fttl
) + 1;
2176 int first
= (base
== prev
);
2178 VAL_TO_MASK(smask
, b
->tr_smask
);
2179 printf(" Source Response Dest ");
2181 printf("Packet Statistics For Only For Traffic\n");
2183 printf("Overall Packet Statistics For Traffic From\n");
2184 inet_fmt(base
->qhdr
.tr_src
, s1
);
2185 printf("%-15s %-15s ",
2186 ((b
->tr_inaddr
& smask
) == (base
->qhdr
.tr_src
& smask
)) ?
2188 inet_fmt(base
->qhdr
.tr_raddr
, s2
));
2189 inet_fmt(base
->igmp
.igmp_group
.s_addr
, s2
);
2191 printf("All Multicast Traffic From %s\n", s1
);
2193 printf("Packet %s To %s\n", s1
, s2
);
2194 rtt
= t_diff(resptime
, new->qtime
);
2196 printf(" %c __/ rtt%5d%s ",
2197 (first
&& !verbose
) ? 'v' : '|', rtt
, ms
);
2199 printf("Lost/Sent = Pct Rate To %s\n", s2
);
2201 printf(" Rate Lost/Sent = Pct Rate\n");
2202 if (!first
|| verbose
) {
2203 hop
= t_diff(resptime
, qarrtime
);
2205 printf(" v / hop%5d%s ", hop
, ms
);
2207 printf("--------------------- --------------------\n");
2209 printf("------- ---------------------\n");
2211 if ((b
->tr_inaddr
& smask
) != (base
->qhdr
.tr_src
& smask
) &&
2212 b
->tr_rmtaddr
!= 0) {
2213 printf("%-15s %-14s is the previous hop\n", inet_fmt(b
->tr_rmtaddr
, s1
),
2214 inet_name(b
->tr_rmtaddr
));
2218 printf("\t\t\t\tv_in: %ld ", (long)ntohl(n
->tr_vifin
));
2219 printf("v_out: %ld ", (long)ntohl(n
->tr_vifout
));
2220 printf("pkts: %ld\n", (long)ntohl(n
->tr_pktcnt
));
2221 printf("\t\t\t\tv_in: %ld ", (long)ntohl(b
->tr_vifin
));
2222 printf("v_out: %ld ", (long)ntohl(b
->tr_vifout
));
2223 printf("pkts: %ld\n", (long)ntohl(b
->tr_pktcnt
));
2224 printf("\t\t\t\tv_in: %ld ",
2225 (long)(ntohl(n
->tr_vifin
) - ntohl(b
->tr_vifin
)));
2226 printf("v_out: %ld ",
2227 (long)(ntohl(n
->tr_vifout
) - ntohl(b
->tr_vifout
)));
2228 printf("pkts: %ld\n",
2229 (long)(ntohl(n
->tr_pktcnt
) - ntohl(b
->tr_pktcnt
)));
2230 printf("\t\t\t\treset: %x hoptime: %lx\n", *r
, (long)ntohl(n
->tr_qarr
));
2234 if ((n
->tr_inaddr
!= b
->tr_inaddr
) ||
2235 (n
->tr_outaddr
!= b
->tr_outaddr
) ||
2236 (n
->tr_rmtaddr
!= b
->tr_rmtaddr
))
2237 return 1; /* Route changed */
2239 if ((n
->tr_inaddr
!= n
->tr_outaddr
) && n
->tr_inaddr
)
2240 printf("%-15s\n", inet_fmt(n
->tr_inaddr
, s1
));
2241 printf("%-15s %-14s %s%s\n", inet_fmt(n
->tr_outaddr
, s1
), names
[rno
],
2242 flag_type(n
->tr_rflags
),
2243 (*r
& BUG_NOPRINT
) ? " [reset counters]" : "");
2245 if (rno
-- < 1) break;
2247 printf(" %c ^ ttl%5d ", (first
&& !verbose
) ? 'v' : '|',
2249 stat_line(p
, n
, TRUE
, r
);
2250 if (!first
|| verbose
) {
2251 resptime
= qarrtime
;
2252 qarrtime
= ntohl((n
-1)->tr_qarr
);
2253 hop
= t_diff(resptime
, qarrtime
);
2255 printf(" v | hop%5d%s", hop
, ms
);
2259 stat_line(b
, n
, TRUE
, r
);
2263 ttl
= MaX(ttl
, MaX(1, n
->tr_fttl
) + base
->len
- rno
);
2266 printf(" %c \\__ ttl%5d ", (first
&& !verbose
) ? 'v' : '|',
2268 stat_line(p
, n
, FALSE
, r
);
2269 if (!first
|| verbose
) {
2270 hop
= t_diff(qarrtime
, new->qtime
);
2272 printf(" v \\ hop%5d%s", hop
, ms
);
2276 stat_line(b
, n
, FALSE
, r
);
2278 printf("%-15s %s\n", inet_fmt(base
->qhdr
.tr_dst
, s1
),
2279 !passive
? inet_fmt(lcl_addr
, s2
) : " * * * ");
2280 printf(" Receiver Query Source\n\n");
2285 * Determine whether or not the path has changed.
2288 path_changed(struct resp_buf
*base
, struct resp_buf
*new)
2290 int rno
= base
->len
- 1;
2291 struct tr_resp
*b
= base
->resps
+ rno
;
2292 struct tr_resp
*n
= new->resps
+ rno
;
2294 while (rno
-- >= 0) {
2295 if ((n
->tr_inaddr
!= b
->tr_inaddr
) ||
2296 (n
->tr_outaddr
!= b
->tr_outaddr
) ||
2297 (n
->tr_rmtaddr
!= b
->tr_rmtaddr
))
2298 return 1; /* Route changed */
2299 if ((b
->tr_rflags
== TR_NO_RTE
) &&
2300 (n
->tr_rflags
!= TR_NO_RTE
))
2301 return 1; /* Route got longer? */
2309 /***************************************************************************
2311 ***************************************************************************/
2314 main(int argc
, char **argv
)
2317 struct sockaddr_in addr
;
2318 int addrlen
= sizeof(addr
);
2321 struct resp_buf
*prev
, *new;
2325 int hops
, nexthop
, tries
;
2326 u_int32 lastout
= 0;
2335 errx(1, "must be root");
2338 * We might get spawned by vat with the audio device open.
2339 * Close everything but stdin, stdout, stderr.
2341 for (i
= 3; i
< 255; i
++)
2348 if (argc
== 0) usage();
2350 while (argc
> 0 && *argv
[0] == '-') {
2351 char *p
= *argv
++; argc
--;
2359 } else if (argc
> 0) arg
= argv
[0];
2361 case 'd': /* Unlisted debug print option */
2362 if (arg
&& isdigit(*arg
)) {
2364 if (debug
< 0) debug
= 0;
2365 if (debug
> 3) debug
= 3;
2366 if (arg
== argv
[0]) argv
++, argc
--;
2370 case 'M': /* Use multicast for reponse */
2373 case 'U': /* Use unicast for response */
2376 case 'L': /* Trace w/ loss threshold */
2377 if (arg
&& isdigit(*arg
)) {
2378 lossthresh
= atoi(arg
);
2382 if (arg
== argv
[0]) argv
++, argc
--;
2387 case 'O': /* Don't use IP options */
2390 case 'P': /* Just watch the path */
2394 case 'Q': /* (undoc.) always use this QID */
2395 if (arg
&& isdigit(*arg
)) {
2396 staticqid
= atoi(arg
);
2399 if (arg
== argv
[0]) argv
++, argc
--;
2404 case 'T': /* Print confusing tunnel stats */
2407 case 'W': /* Cisco's "weak" mtrace */
2410 case 'V': /* Print version and exit */
2411 fprintf(stderr
, "mtrace version 5.2\n");
2414 case 'l': /* Loop updating stats indefinitely */
2417 case 'n': /* Don't reverse map host addresses */
2420 case 'p': /* Passive listen for traces */
2423 case 'v': /* Verbosity */
2426 case 's': /* Short form, don't wait for stats */
2429 case 'w': /* Time to wait for packet arrival */
2430 if (arg
&& isdigit(*arg
)) {
2431 timeout
= atoi(arg
);
2432 if (timeout
< 1) timeout
= 1;
2433 if (arg
== argv
[0]) argv
++, argc
--;
2437 case 'f': /* first hop */
2438 if (arg
&& isdigit(*arg
)) {
2440 if (qno
> MAXHOPS
) qno
= MAXHOPS
;
2441 else if (qno
< 1) qno
= 0;
2442 if (arg
== argv
[0]) argv
++, argc
--;
2447 case 'm': /* Max number of hops to trace */
2448 if (arg
&& isdigit(*arg
)) {
2450 if (qno
> MAXHOPS
) qno
= MAXHOPS
;
2451 else if (qno
< 1) qno
= 0;
2452 if (arg
== argv
[0]) argv
++, argc
--;
2456 case 'q': /* Number of query retries */
2457 if (arg
&& isdigit(*arg
)) {
2458 nqueries
= atoi(arg
);
2459 if (nqueries
< 1) nqueries
= 1;
2460 if (arg
== argv
[0]) argv
++, argc
--;
2464 case 'g': /* Last-hop gateway (dest of query) */
2465 if (arg
&& (gwy
= host_addr(arg
))) {
2466 if (arg
== argv
[0]) argv
++, argc
--;
2470 case 't': /* TTL for query packet */
2471 if (arg
&& isdigit(*arg
)) {
2473 if (qttl
< 1) qttl
= 1;
2475 if (arg
== argv
[0]) argv
++, argc
--;
2479 case 'e': /* Extra hops past non-responder */
2480 if (arg
&& isdigit(*arg
)) {
2481 extrahops
= atoi(arg
);
2482 if (extrahops
< 0) extrahops
= 0;
2483 if (arg
== argv
[0]) argv
++, argc
--;
2487 case 'r': /* Dest for response packet */
2488 if (arg
&& (raddr
= host_addr(arg
))) {
2489 if (arg
== argv
[0]) argv
++, argc
--;
2493 case 'i': /* Local interface address */
2494 if (arg
&& (lcl_addr
= host_addr(arg
))) {
2495 if (arg
== argv
[0]) argv
++, argc
--;
2499 case 'S': /* Stat accumulation interval */
2500 if (arg
&& isdigit(*arg
)) {
2501 statint
= atoi(arg
);
2502 if (statint
< 1) statint
= 1;
2503 if (arg
== argv
[0]) argv
++, argc
--;
2513 if (argc
> 0 && (qsrc
= host_addr(argv
[0]))) { /* Source of path */
2514 if (IN_MULTICAST(ntohl(qsrc
))) {
2516 /* Should probably rewrite arg parsing at some point, as
2517 * this makes "mtrace -g foo 224.1.2.3 224.2.3.4" valid!... */
2525 if (argc
> 0 && (qdst
= host_addr(argv
[0]))) { /* Dest of path */
2527 if (argc
> 0 && (qgrp
= host_addr(argv
[0]))) { /* Path via group */
2530 if (IN_MULTICAST(ntohl(qdst
))) {
2531 u_int32 temp
= qdst
;
2534 if (IN_MULTICAST(ntohl(qdst
))) usage();
2535 } else if (qgrp
&& !IN_MULTICAST(ntohl(qgrp
))) usage();
2550 checkforsolarisbug();
2554 * Set useful defaults for as many parameters as possible.
2557 defgrp
= 0; /* Default to no group */
2558 query_cast
= htonl(0xE0000002); /* All routers multicast addr */
2559 resp_cast
= htonl(0xE0000120); /* Mtrace response multicast addr */
2563 if (printstats
&& numstats
!= 0 && !tunstats
) {
2564 /* Stats are useless without a group */
2566 "WARNING: no multicast group specified, so no statistics printed");
2572 "WARNING: group was specified so not performing \"weak\" mtrace");
2576 * Get default local address for multicasts to use in setting defaults.
2578 addr
.sin_family
= AF_INET
;
2579 #if (defined(BSD) && (BSD >= 199103))
2580 addr
.sin_len
= sizeof(addr
);
2582 addr
.sin_addr
.s_addr
= qgrp
? qgrp
: query_cast
;
2583 addr
.sin_port
= htons(2000); /* Any port above 1024 will do */
2586 * Note that getsockname() can return 0 on some systems
2587 * (notably SunOS 5.x, x < 6). This is taken care of in
2588 * get_netmask(). If the default multicast interface (set
2589 * with the route for 224.0.0.0) is not the same as the
2590 * hostname, mtrace -i [if_addr] will have to be used.
2592 if (((udp
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) ||
2593 (connect(udp
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) ||
2594 getsockname(udp
, (struct sockaddr
*) &addr
, &addrlen
) < 0)
2595 err(-1, "determining local address");
2599 * SunOS 5.X prior to SunOS 2.6, getsockname returns 0 for udp socket.
2600 * This call to sysinfo will return the hostname.
2601 * If the default multicast interfface (set with the route
2602 * for 224.0.0.0) is not the same as the hostname,
2603 * mtrace -i [if_addr] will have to be used.
2605 if (addr
.sin_addr
.s_addr
== 0) {
2606 char myhostname
[MAXHOSTNAMELEN
];
2610 error
= sysinfo(SI_HOSTNAME
, myhostname
, sizeof(myhostname
));
2612 err(1, "getting my hostname");
2614 hp
= gethostbyname(myhostname
);
2615 if (hp
== NULL
|| hp
->h_addrtype
!= AF_INET
||
2616 hp
->h_length
!= sizeof(addr
.sin_addr
))
2617 err(1, "finding IP address for my hostname");
2619 memcpy((char *)&addr
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
2624 * Default destination for path to be queried is the local host.
2625 * When gateway specified, default destination is that gateway
2626 * and default source is local host.
2629 qdst
= lcl_addr
? lcl_addr
: addr
.sin_addr
.s_addr
;
2630 dst_netmask
= get_netmask(udp
, &qdst
);
2631 if (gwy
&& (gwy
& dst_netmask
) != (qdst
& dst_netmask
) &&
2632 !IN_MULTICAST(ntohl(gwy
)))
2635 if (qsrc
== 0 && gwy
)
2636 qsrc
= lcl_addr
? lcl_addr
: addr
.sin_addr
.s_addr
;
2640 dst_netmask
= get_netmask(udp
, &qdst
);
2642 if (lcl_addr
== 0) lcl_addr
= addr
.sin_addr
.s_addr
;
2645 * Initialize the seed for random query identifiers.
2647 gettimeofday(&tv
, 0);
2648 seed
= tv
.tv_usec
^ lcl_addr
;
2654 * Protect against unicast queries to mrouted versions that might crash.
2655 * Also use the obsolete "can mtrace" neighbor bit to warn about
2656 * older implementations.
2658 if (gwy
&& !IN_MULTICAST(ntohl(gwy
)))
2659 if (send_recv(gwy
, IGMP_DVMRP
, DVMRP_ASK_NEIGHBORS2
, 1, &incr
[0], NULL
)) {
2660 int flags
= ntohl(incr
[0].igmp
.igmp_group
.s_addr
);
2661 int version
= flags
& 0xFFFF;
2662 int info
= (flags
& 0xFF0000) >> 16;
2664 if (version
== 0x0303 || version
== 0x0503) {
2665 printf("Don't use -g to address an mrouted 3.%d, it might crash\n",
2666 (version
>> 8) & 0xFF);
2669 if ((info
& 0x08) == 0) {
2672 printf(" probably doesn't support mtrace, trying anyway...\n");
2676 printf("Mtrace from %s to %s via group %s\n",
2677 inet_fmt(qsrc
, s1
), inet_fmt(qdst
, s2
), inet_fmt(qgrp
, s3
));
2679 if ((qdst
& dst_netmask
) == (qsrc
& dst_netmask
))
2680 fprintf(stderr
, "mtrace: Source & receiver appear to be directly connected\n");
2683 * If the response is to be a multicast address, make sure we
2684 * are listening on that multicast address.
2687 if (IN_MULTICAST(ntohl(raddr
))) k_join(raddr
, lcl_addr
);
2688 } else k_join(resp_cast
, lcl_addr
);
2690 memset(&base
, 0, sizeof(base
));
2693 * If the destination is on the local net, the last-hop router can
2694 * be found by multicast to the all-routers multicast group.
2695 * Otherwise, use the group address that is the subject of the
2696 * query since by definition the last-hop router will be a member.
2697 * Set default TTLs for local remote multicasts.
2700 if ((qdst
& dst_netmask
) == (lcl_addr
& dst_netmask
)) tdst
= query_cast
;
2703 if (tdst
== 0 && qgrp
== 0)
2704 errx(1, "mtrace: weak mtrace requires -g if destination is not local.\n");
2706 if (IN_MULTICAST(ntohl(tdst
))) {
2707 k_set_loop(1); /* If I am running on a router, I need to hear this */
2708 if (tdst
== query_cast
) k_set_ttl(qttl
? qttl
: 1);
2709 else k_set_ttl(qttl
? qttl
: MULTICAST_TTL1
);
2713 * Try a query at the requested number of hops or MAXHOPS if unspecified.
2718 printf("Querying full reverse path... ");
2724 printf("Querying full reverse path, starting at hop %d...", qno
);
2726 printf("Querying reverse path, maximum %d hops... ", qno
);
2733 recvlen
= send_recv(tdst
, IGMP_MTRACE
, hops
, tries
, &base
, mtrace_callback
);
2736 * If the initial query was successful, print it. Otherwise, if
2737 * the query max hop count is the default of zero, loop starting
2738 * from one until there is no response for extrahops more hops. The
2739 * extra hops allow getting past an mtrace-capable mrouter that can't
2740 * send multicast packets because all phyints are disabled.
2746 print_trace(1, &base
, names
);
2747 r
= base
.resps
+ base
.len
- 1;
2748 if (r
->tr_rflags
== TR_OLD_ROUTER
|| r
->tr_rflags
== TR_NO_SPACE
||
2749 (qno
!= 0 && r
->tr_rmtaddr
!= 0 && !fflag
)) {
2750 printf("%3d ", -(base
.len
+1));
2751 what_kind(&base
, r
->tr_rflags
== TR_OLD_ROUTER
?
2752 "doesn't support mtrace"
2753 : "is the next hop");
2756 nexthop
= hops
= qno
;
2759 VAL_TO_MASK(smask
, r
->tr_smask
);
2760 if ((r
->tr_inaddr
& smask
) == (qsrc
& smask
)) {
2761 printf("%3d ", -(base
.len
+1));
2766 } else if (qno
== 0) {
2768 printf("switching to hop-by-hop:\n 0 ");
2772 for (hops
= 1, nexthop
= 1; hops
<= MAXHOPS
; ++hops
) {
2773 printf("%3d ", -hops
);
2777 * After a successful first hop, try switching to the unicast
2778 * address of the last-hop router instead of multicasting the
2779 * trace query. This should be safe for mrouted versions 3.3
2780 * and 3.5 because there is a long route timeout with metric
2781 * infinity before a route disappears. Switching to unicast
2782 * reduces the amount of multicast traffic and avoids a bug
2783 * with duplicate suppression in mrouted 3.5.
2785 if (hops
== 2 && gwy
== 0 && lastout
!= 0 &&
2786 (recvlen
= send_recv(lastout
, IGMP_MTRACE
, hops
, 1, &base
, mtrace_callback
)))
2788 else recvlen
= send_recv(tdst
, IGMP_MTRACE
, hops
, nqueries
, &base
, mtrace_callback
);
2791 /*if (hops == 1) break;*/
2792 if (hops
== nexthop
) {
2795 } else if (what_kind(&base
, "didn't respond")) {
2796 /* the ask_neighbors determined that the
2797 * not-responding router is the first-hop. */
2802 } else if (hops
< nexthop
+ extrahops
) {
2805 printf("...giving up\n");
2810 if (base
.len
== hops
&&
2811 (hops
== 1 || (base
.resps
+nexthop
-2)->tr_outaddr
== lastout
)) {
2812 if (hops
== nexthop
) {
2813 print_trace(-hops
, &base
, names
);
2815 printf("\nResuming...\n");
2816 print_trace(nexthop
, &base
, names
);
2819 if (base
.len
< hops
) {
2821 * A shorter trace than requested means a fatal error
2822 * occurred along the path, or that the route changed
2825 * If the trace is longer than the last one we received,
2826 * then we are resuming from a skipped router (but there
2827 * is still probably a problem).
2829 * If the trace is shorter than the last one we
2830 * received, then the route must have changed (and
2831 * there is still probably a problem).
2833 if (nexthop
<= base
.len
) {
2834 printf("\nResuming...\n");
2835 print_trace(nexthop
, &base
, names
);
2836 } else if (nexthop
> base
.len
+ 1) {
2838 printf("\nRoute must have changed...\n");
2839 print_trace(1, &base
, names
);
2843 * The last hop address is not the same as it was.
2844 * If we didn't know the last hop then we just
2845 * got the first response from a hop-by-hop trace;
2846 * if we did know the last hop then
2847 * the route probably changed underneath us.
2851 printf("\nRoute must have changed...\n");
2853 printf("\nResuming...\n");
2854 print_trace(1, &base
, names
);
2858 r
= base
.resps
+ base
.len
- 1;
2859 lastout
= r
->tr_outaddr
;
2861 if (base
.len
< hops
||
2862 r
->tr_rmtaddr
== 0 ||
2863 (r
->tr_rflags
& 0x80)) {
2864 VAL_TO_MASK(smask
, r
->tr_smask
);
2865 if (r
->tr_rmtaddr
) {
2866 if (hops
!= nexthop
) {
2867 printf("\n%3d ", -(base
.len
+1));
2869 what_kind(&base
, r
->tr_rflags
== TR_OLD_ROUTER
?
2870 "doesn't support mtrace" :
2871 "would be the next hop");
2872 /* XXX could do segmented trace if TR_NO_SPACE */
2873 } else if (r
->tr_rflags
== TR_NO_ERR
&&
2874 (r
->tr_inaddr
& smask
) == (qsrc
& smask
)) {
2875 printf("%3d ", -(hops
+ 1));
2886 if (base
.rtime
== 0) {
2887 printf("Timed out receiving responses\n");
2888 if (IN_MULTICAST(ntohl(tdst
))){
2889 if (tdst
== query_cast
)
2890 printf("Perhaps no local router has a route for source %s\n",
2891 inet_fmt(qsrc
, s1
));
2893 printf("Perhaps receiver %s is not a member of group %s,\n\
2894 or no router local to it has a route for source %s,\n\
2895 or multicast at ttl %d doesn't reach its last-hop router for that source\n",
2896 inet_fmt(qdst
, s2
), inet_fmt(qgrp
, s3
), inet_fmt(qsrc
, s1
),
2897 qttl
? qttl
: MULTICAST_TTL1
);
2902 printf("Round trip time %d ms; ", t_diff(base
.rtime
, base
.qtime
));
2904 struct tr_resp
*n
= base
.resps
+ base
.len
- 1;
2905 u_int ttl
= n
->tr_fttl
+ 1;
2910 ttl
= MaX(ttl
, MaX(1, n
->tr_fttl
) + base
.len
- rno
);
2912 printf("total ttl of %d required.\n\n",ttl
);
2916 * Use the saved response which was the longest one received,
2917 * and make additional probes after delay to measure loss.
2919 raddr
= base
.qhdr
.tr_raddr
;
2920 rttl
= TR_GETTTL(base
.qhdr
.tr_rttlqid
);
2921 gettimeofday(&tv
, 0);
2922 waittime
= statint
- (((tv
.tv_sec
+ JAN_1970
) & 0xFFFF) - (base
.qtime
>> 16));
2924 new = &incr
[numstats
&1];
2927 * Zero out bug-avoidance counters
2929 memset(bugs
, 0, sizeof(bugs
));
2932 printf("Monitoring path..");
2934 while (numstats
--) {
2935 if (waittime
< 1) printf("\n");
2937 if (printstats
&& (lossthresh
== 0 || printed
)) {
2938 printf("Waiting to accumulate statistics...");
2943 sleep((unsigned)waittime
);
2946 rno
= hopbyhop
? base
.len
: qno
? qno
: MAXHOPS
;
2947 recvlen
= send_recv(tdst
, IGMP_MTRACE
, rno
, nqueries
, new, mtrace_callback
);
2950 printf("Timed out.\n");
2958 if (base
.len
!= new->len
|| path_changed(&base
, new)) {
2959 printf("%s", base
.len
== new->len
? "Route changed" :
2960 "Trace length doesn't match");
2962 printf(" after %d seconds",
2963 (int)((new->qtime
- base
.qtime
) >> 16));
2966 print_trace(1, new, names
);
2968 bcopy(new, &base
, sizeof(base
));
2969 nexthop
= hops
= new->len
;
2970 printf("Continuing with hop-by-hop...\n");
2975 if (new->igmp
.igmp_group
.s_addr
!= qgrp
||
2976 new->qhdr
.tr_src
!= qsrc
|| new->qhdr
.tr_dst
!= qdst
)
2977 printf("\nWARNING: trace modified en route; statistics may be incorrect\n");
2978 fixup_stats(&base
, prev
, new, bugs
);
2979 if ((lossthresh
== 0) || check_thresh(lossthresh
, &base
, prev
, new)) {
2980 printf("Results after %d seconds",
2981 (int)((new->qtime
- base
.qtime
) >> 16));
2983 printf(" (this trace %d seconds)",
2984 (int)((new->qtime
- prev
->qtime
) >> 16));
2987 struct tm
*qr
= localtime(&t
);
2989 printf(" qid 0x%06x at %2d:%02d:%02d",
2990 TR_GETQID(base
.qhdr
.tr_rttlqid
),
2991 qr
->tm_hour
, qr
->tm_min
, qr
->tm_sec
);
2995 if (print_stats(&base
, prev
, new, bugs
, names
)) {
2996 printf("This should have been detected earlier, but ");
2997 printf("Route changed:\n");
2998 goto printandcontinue
;
3003 new = &incr
[numstats
&1];
3008 * If the response was multicast back, leave the group
3011 if (IN_MULTICAST(ntohl(raddr
))) k_leave(raddr
, lcl_addr
);
3012 } else k_leave(resp_cast
, lcl_addr
);
3020 fprintf(stderr
, "%s\n%s\n%s\n",
3021 "usage: mtrace [-MUOPTWVlnpvs] [-e extra_hops] [-f first_hop] [-i if_addr]",
3022 " [-g gateway] [-m max_hops] [-q nqueries] [-r resp_dest]",
3023 " [-S statint] [-t ttl] [-w wait] source [receiver] [group]");
3028 check_vif_state(void)
3030 dolog(LOG_WARNING
, errno
, "sendto");
3034 * Log errors and other messages to stderr, according to the severity
3035 * of the message and the current debug level. For errors of severity
3036 * LOG_ERR or worse, terminate the program.
3039 dolog(int severity
, int syserr
, char *format
, ...)
3044 va_start(ap
, format
);
3047 case 0: if (severity
> LOG_WARNING
) return;
3048 case 1: if (severity
> LOG_NOTICE
) return;
3049 case 2: if (severity
> LOG_INFO
) return;
3052 if (severity
== LOG_WARNING
)
3053 strcpy(fmt
, "warning - ");
3054 strncat(fmt
, format
, sizeof(fmt
)-strlen(fmt
));
3055 fmt
[sizeof(fmt
)-1]='\0';
3056 vfprintf(stderr
, fmt
, ap
);
3058 fprintf(stderr
, "\n");
3059 else if (syserr
< sys_nerr
)
3060 fprintf(stderr
, ": %s\n", sys_errlist
[syserr
]);
3062 fprintf(stderr
, ": errno %d\n", syserr
);
3064 if (severity
<= LOG_ERR
) exit(1);