1: /*
2: * mtrace.c
3: *
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
8: *
9: * mtrace source
10: *
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.
14: *
15: *
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.
21: *
22: * Copyright (c) 1998-2001.
23: * The University of Southern California/Information Sciences Institute.
24: * All rights reserved.
25: *
26: * Redistribution and use in source and binary forms, with or without
27: * modification, are permitted provided that the following conditions
28: * are met:
29: * 1. Redistributions of source code must retain the above copyright
30: * notice, this list of conditions and the following disclaimer.
31: * 2. Redistributions in binary form must reproduce the above copyright
32: * notice, this list of conditions and the following disclaimer in the
33: * documentation and/or other materials provided with the distribution.
34: * 3. Neither the name of the project nor the names of its contributors
35: * may be used to endorse or promote products derived from this software
36: * without specific prior written permission.
37: *
38: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
39: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
42: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48: * SUCH DAMAGE.
49: */
50:
51: #include "defs.h"
52: #include <arpa/inet.h>
53: #include <ctype.h>
54: #include <err.h>
55: #include <ifaddrs.h>
56: #include <memory.h>
57: #include <netdb.h>
58: #include <poll.h>
59: #include <stdarg.h>
60: #include <string.h>
61: #include <sys/ioctl.h>
62: #ifdef SUNOS5
63: #include <sys/systeminfo.h>
64: #endif
65: #include <sys/time.h>
66:
67: #define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */
68: #define DEFAULT_RETRIES 3 /* How many times to try */
69: #define MAXHOPS UNREACHABLE /* Don't need more hops than max metric */
70: #define UNICAST_TTL 255 /* TTL for unicast response */
71: #define MULTICAST_TTL1 64 /* Default TTL for multicast query/response */
72: #define MULTICAST_TTL_INC 32 /* TTL increment for increase after timeout */
73: #define MULTICAST_TTL_MAX 192 /* Maximum TTL allowed (protect low-BW links */
74:
75: struct resp_buf {
76: u_long qtime; /* Time query was issued */
77: u_long rtime; /* Time response was received */
78: int len; /* Number of reports or length of data */
79: struct igmp igmp; /* IGMP header */
80: union {
81: struct {
82: struct tr_query q; /* Query/response header */
83: struct tr_resp r[MAXHOPS]; /* Per-hop reports */
84: } t;
85: char d[MAX_DVMRP_DATA_LEN]; /* Neighbor data */
86: } u;
87: } base, incr[2];
88:
89: #define qhdr u.t.q
90: #define resps u.t.r
91: #define ndata u.d
92:
93: char names[MAXHOPS][40];
94: int reset[MAXHOPS]; /* To get around 3.4 bug, ... */
95: int swaps[MAXHOPS]; /* To get around 3.6 bug, ... */
96:
97: int timeout = DEFAULT_TIMEOUT;
98: int nqueries = DEFAULT_RETRIES;
99: int numeric = FALSE;
100: int debug = 0;
101: int passive = FALSE;
102: int multicast = FALSE;
103: int statint = 10;
104: int verbose = 0;
105:
106: u_int32_t defgrp; /* Default group if not specified */
107: u_int32_t query_cast; /* All routers multicast addr */
108: u_int32_t resp_cast; /* Mtrace response multicast addr */
109:
110: u_int32_t lcl_addr = 0; /* This host address, in NET order */
111: u_int32_t dst_netmask; /* netmask to go with qdst */
112:
113: /*
114: * Query/response parameters, all initialized to zero and set later
115: * to default values or from options.
116: */
117: u_int32_t qsrc = 0; /* Source address in the query */
118: u_int32_t qgrp = 0; /* Group address in the query */
119: u_int32_t qdst = 0; /* Destination (receiver) address in query */
120: u_char qno = 0; /* Max number of hops to query */
121: u_int32_t raddr = 0; /* Address where response should be sent */
122: int qttl = 0; /* TTL for the query packet */
123: u_char rttl = 0; /* TTL for the response packet */
124: u_int32_t gwy = 0; /* User-supplied last-hop router address */
125: u_int32_t tdst = 0; /* Address where trace is sent (last-hop) */
126:
127: vifi_t numvifs; /* to keep loader happy */
128: /* (see kern.c) */
129:
130: char * inet_name(u_int32_t addr);
131: u_int32_t host_addr(char *name);
132: /* u_int is promoted u_char */
133: char * proto_type(u_int type);
134: char * flag_type(u_int type);
135:
136: u_int32_t get_netmask(int s, u_int32_t dst);
137: int get_ttl(struct resp_buf *buf);
138: int t_diff(u_long a, u_long b);
139: u_long fixtime(u_long time);
140: int send_recv(u_int32_t dst, int type, int code,
141: int tries, struct resp_buf *save);
142: char * print_host(u_int32_t addr);
143: char * print_host2(u_int32_t addr1, u_int32_t addr2);
144: void print_trace(int index, struct resp_buf *buf);
145: int what_kind(struct resp_buf *buf, char *why);
146: char * scale(int *hop);
147: void stat_line(struct tr_resp *r, struct tr_resp *s,
148: int have_next, int *res);
149: void fixup_stats(struct resp_buf *base,
150: struct resp_buf *prev, struct resp_buf *new);
151: int print_stats(struct resp_buf *base,
152: struct resp_buf *prev, struct resp_buf *new);
153: void check_vif_state(void);
154: u_long byteswap(u_long v);
155:
156:
157: char *inet_name(u_int32_t addr)
158: {
159: struct hostent *e;
160:
161: e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
162:
163: return e ? e->h_name : "?";
164: }
165:
166: u_int32_t host_addr(char *name)
167: {
168: struct hostent *e = (struct hostent *)0;
169: u_int32_t addr;
170: int i, dots = 3;
171: char buf[40];
172: char *ip = name;
173: char *op = buf;
174:
175: /*
176: * Undo BSD's favor -- take fewer than 4 octets as net/subnet address
177: * if the name is all numeric.
178: */
179: for (i = sizeof(buf) - 7; i > 0; --i) {
180: if (*ip == '.') --dots;
181: else if (*ip == '\0') break;
182: else if (!isdigit((int)*ip)) dots = 0; /* Not numeric, don't add zeroes */
183: *op++ = *ip++;
184: }
185: for (i = 0; i < dots; ++i) {
186: *op++ = '.';
187: *op++ = '0';
188: }
189: *op = '\0';
190:
191: if (dots <= 0) e = gethostbyname(name);
192: if (e) memcpy((char *)&addr, e->h_addr_list[0], e->h_length);
193: else {
194: addr = inet_addr(buf);
195: if (addr == INADDR_NONE) {
196: addr = 0;
197: printf("Could not parse %s as host name or address\n", name);
198: }
199: }
200: return addr;
201: }
202:
203:
204: char *proto_type(u_int type)
205: {
206: static char buf[80];
207:
208: switch (type) {
209: case PROTO_DVMRP:
210: return ("DVMRP");
211: case PROTO_MOSPF:
212: return ("MOSPF");
213: case PROTO_PIM:
214: return ("PIM");
215: case PROTO_CBT:
216: return ("CBT");
217: default:
218: (void) snprintf(buf, sizeof buf, "Unknown protocol code %d", type);
219: return (buf);
220: }
221: }
222:
223:
224: char *flag_type(u_int type)
225: {
226: static char buf[80];
227:
228: switch (type) {
229: case TR_NO_ERR:
230: return ("");
231: case TR_WRONG_IF:
232: return ("Wrong interface");
233: case TR_PRUNED:
234: return ("Prune sent upstream");
235: case TR_OPRUNED:
236: return ("Output pruned");
237: case TR_SCOPED:
238: return ("Hit scope boundary");
239: case TR_NO_RTE:
240: return ("No route");
241: case TR_OLD_ROUTER:
242: return ("Next router no mtrace");
243: case TR_NO_FWD:
244: return ("Not forwarding");
245: case TR_NO_SPACE:
246: return ("No space in packet");
247: default:
248: (void) snprintf(buf, sizeof buf, "Unknown error code %d", type);
249: return (buf);
250: }
251: }
252:
253: /*
254: * If destination is on a local net, get the netmask, else set the
255: * netmask to all ones. There are two side effects: if the local
256: * address was not explicitly set, and if the destination is on a
257: * local net, use that one; in either case, verify that the local
258: * address is valid.
259: */
260: u_int32_t get_netmask(int UNUSED s, u_int32_t dst)
261: {
262: u_int32_t if_addr, if_mask;
263: u_int32_t retval = 0xFFFFFFFF;
264: int found = FALSE;
265: struct ifaddrs *ifap, *ifa;
266:
267: if (getifaddrs(&ifap) != 0) {
268: perror("getifaddrs");
269: return (retval);
270: }
271: for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
272: if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET)
273: continue;
274: if_addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
275: if_mask = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr.s_addr;
276: if ((dst & if_mask) == (if_addr & if_mask)) {
277: retval = if_mask;
278: if (lcl_addr == 0)
279: lcl_addr = if_addr;
280: }
281: if (lcl_addr == if_addr)
282: found = TRUE;
283: }
284: if (!found && lcl_addr != 0) {
285: printf("Interface address is not valid\n");
286: exit(1);
287: }
288: freeifaddrs(ifap);
289: return (retval);
290: }
291:
292:
293: int get_ttl(struct resp_buf *buf)
294: {
295: int rno;
296: struct tr_resp *b;
297: u_int ttl;
298:
299: if (buf && (rno = buf->len) > 0) {
300: b = buf->resps + rno - 1;
301: ttl = b->tr_fttl;
302:
303: while (--rno > 0) {
304: --b;
305: if (ttl < b->tr_fttl) ttl = b->tr_fttl;
306: else ++ttl;
307: }
308: ttl += MULTICAST_TTL_INC;
309: if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1;
310: if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX;
311: return (ttl);
312: } else return(MULTICAST_TTL1);
313: }
314:
315: /*
316: * Calculate the difference between two 32-bit NTP timestamps and return
317: * the result in milliseconds.
318: */
319: int t_diff(u_long a, u_long b)
320: {
321: int d = a - b;
322:
323: return ((d * 125) >> 13);
324: }
325:
326: /*
327: * Fixup for incorrect time format in 3.3 mrouted.
328: * This is possible because (JAN_1970 mod 64K) is quite close to 32K,
329: * so correct and incorrect times will be far apart.
330: */
331: u_long fixtime(u_long time)
332: {
333: if (abs((int)(time-base.qtime)) > 0x3FFFFFFF)
334: time = ((time & 0xFFFF0000) + (JAN_1970 << 16)) +
335: ((time & 0xFFFF) << 14) / 15625;
336: return (time);
337: }
338:
339: /*
340: * Swap bytes for poor little-endian machines that don't byte-swap
341: */
342: u_long byteswap(u_long v)
343: {
344: return ((v << 24) | ((v & 0xff00) << 8) |
345: ((v >> 8) & 0xff00) | (v >> 24));
346: }
347:
348: int send_recv(u_int32_t dst, int type, int code, int tries, struct resp_buf *save)
349: {
350: struct timeval tq, tr, tv;
351: struct ip *ip;
352: struct igmp *igmp;
353: struct tr_query *query, *rquery;
354: size_t ipdatalen, iphdrlen;
355: u_int32_t local, group;
356: int datalen;
357: struct pollfd pfd[1];
358: int count, len, i;
359: size_t igmpdatalen;
360: ssize_t recvlen;
361: socklen_t dummy = 0;
362:
363: if (type == IGMP_MTRACE) {
364: group = qgrp;
365: datalen = sizeof(struct tr_query);
366: } else {
367: group = htonl(MROUTED_LEVEL);
368: datalen = 0;
369: }
370: if (IN_MULTICAST(ntohl(dst))) local = lcl_addr;
371: else local = INADDR_ANY;
372:
373: /*
374: * If the reply address was not explicitly specified, start off
375: * with the unicast address of this host. Then, if there is no
376: * response after trying half the tries with unicast, switch to
377: * the standard multicast reply address. If the TTL was also not
378: * specified, set a multicast TTL and if needed increase it for the
379: * last quarter of the tries.
380: */
381: query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
382: query->tr_raddr = raddr ? raddr : multicast ? resp_cast : lcl_addr;
383: query->tr_rttl = rttl ? rttl :
384: IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL;
385: query->tr_src = qsrc;
386: query->tr_dst = qdst;
387:
388: for (i = tries ; i > 0; --i) {
389: if (tries == nqueries && raddr == 0) {
390: if (i == ((nqueries + 1) >> 1)) {
391: query->tr_raddr = resp_cast;
392: if (rttl == 0) query->tr_rttl = get_ttl(save);
393: }
394: if (i <= ((nqueries + 3) >> 2) && rttl == 0) {
395: query->tr_rttl += MULTICAST_TTL_INC;
396: if (query->tr_rttl > MULTICAST_TTL_MAX)
397: query->tr_rttl = MULTICAST_TTL_MAX;
398: }
399: }
400:
401: /*
402: * Change the qid for each request sent to avoid being confused
403: * by duplicate responses
404: */
405: #ifdef SYSV
406: query->tr_qid = ((u_int32_t)lrand48() >> 8);
407: #else
408: query->tr_qid = ((u_int32_t)random() >> 8);
409: #endif
410:
411: /*
412: * Set timer to calculate delays, then send query
413: */
414: gettimeofday(&tq, 0);
415: send_igmp(local, dst, type, code, group, datalen);
416:
417: /*
418: * Wait for response, discarding false alarms
419: */
420: pfd[0].fd = igmp_socket;
421: pfd[0].events = POLLIN;
422: while (TRUE) {
423: gettimeofday(&tv, 0);
424: tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec;
425: tv.tv_usec = tq.tv_usec - tv.tv_usec;
426: if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec;
427: if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0;
428:
429: count = poll(pfd, 1, tv.tv_sec * 1000);
430:
431: if (count < 0) {
432: if (errno != EINTR) perror("poll");
433: continue;
434: } else if (count == 0) {
435: printf("* ");
436: fflush(stdout);
437: break;
438: }
439:
440: gettimeofday(&tr, 0);
441: recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
442: 0, (struct sockaddr *)0, &dummy);
443:
444: if (recvlen <= 0) {
445: if (recvlen && errno != EINTR) perror("recvfrom");
446: continue;
447: }
448:
449: if ((size_t)recvlen < sizeof(struct ip)) {
450: fprintf(stderr, "packet too short (%zd bytes) for IP header", recvlen);
451: continue;
452: }
453: ip = (struct ip *) recv_buf;
454: if (ip->ip_p == 0) /* ignore cache creation requests */
455: continue;
456:
457: iphdrlen = ip->ip_hl << 2;
458: ipdatalen = ntohs(ip->ip_len) - iphdrlen;
459: if (iphdrlen + ipdatalen != (size_t)recvlen) {
460: fprintf(stderr, "packet shorter (%zd bytes) than hdr+data len (%zu+%zu)\n",
461: recvlen, iphdrlen, ipdatalen);
462: continue;
463: }
464:
465: igmp = (struct igmp *) (recv_buf + iphdrlen);
466: if (ipdatalen < IGMP_MINLEN) {
467: fprintf(stderr,
468: "IP data field too short (%zu bytes) for IGMP from %s\n",
469: ipdatalen, inet_fmt(ip->ip_src.s_addr, s1, sizeof(s1)));
470: continue;
471: }
472: igmpdatalen = ipdatalen - IGMP_MINLEN;
473:
474: switch (igmp->igmp_type) {
475:
476: case IGMP_DVMRP:
477: if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue;
478: len = igmpdatalen;
479: /*
480: * Accept DVMRP_NEIGHBORS2 response if it comes from the
481: * address queried or if that address is one of the local
482: * addresses in the response.
483: */
484: if (ip->ip_src.s_addr != dst) {
485: u_int32_t *p = (u_int32_t *)(igmp + 1);
486: u_int32_t *ep = p + (len >> 2);
487: while (p < ep) {
488: u_int32_t laddr = *p++;
489: int n = ntohl(*p++) & 0xFF;
490: if (laddr == dst) {
491: ep = p + 1; /* ensure p < ep after loop */
492: break;
493: }
494: p += n;
495: }
496: if (p >= ep) continue;
497: }
498: break;
499:
500: case IGMP_MTRACE: /* For backward compatibility with 3.3 */
501: case IGMP_MTRACE_RESP:
502: if (igmpdatalen <= QLEN) continue;
503: if ((igmpdatalen - QLEN) % RLEN) {
504: printf("packet with incorrect datalen\n");
505: continue;
506: }
507:
508: /*
509: * Ignore responses that don't match query.
510: */
511: rquery = (struct tr_query *)(igmp + 1);
512: if (rquery->tr_qid != query->tr_qid) continue;
513: if (rquery->tr_src != qsrc) continue;
514: if (rquery->tr_dst != qdst) continue;
515: len = (igmpdatalen - QLEN) / RLEN;
516:
517: /*
518: * Ignore trace queries passing through this node when
519: * mtrace is run on an mrouter that is in the path
520: * (needed only because IGMP_MTRACE is accepted above
521: * for backward compatibility with multicast release 3.3).
522: */
523: if (igmp->igmp_type == IGMP_MTRACE) {
524: struct tr_resp *r = (struct tr_resp *)(rquery+1) + len - 1;
525: u_int32_t smask;
526:
527: VAL_TO_MASK(smask, r->tr_smask);
528: if (len < code && (r->tr_inaddr & smask) != (qsrc & smask)
529: && r->tr_rmtaddr != 0 && !(r->tr_rflags & 0x80))
530: continue;
531: }
532:
533: /*
534: * A match, we'll keep this one.
535: */
536: if (len > code) {
537: fprintf(stderr,
538: "Num hops received (%d) exceeds request (%d)\n",
539: len, code);
540: }
541: rquery->tr_raddr = query->tr_raddr; /* Insure these are */
542: rquery->tr_rttl = query->tr_rttl; /* as we sent them */
543: break;
544:
545: default:
546: continue;
547: }
548:
549: /*
550: * Most of the sanity checking done at this point.
551: * Return this packet we have been waiting for.
552: */
553: if (save) {
554: save->qtime = ((tq.tv_sec + JAN_1970) << 16) +
555: (tq.tv_usec << 10) / 15625;
556: save->rtime = ((tr.tv_sec + JAN_1970) << 16) +
557: (tr.tv_usec << 10) / 15625;
558: save->len = len;
559: bcopy((char *)igmp, (char *)&save->igmp, ipdatalen);
560: }
561: return recvlen;
562: }
563: }
564: return 0;
565: }
566:
567: /*
568: * Most of this code is duplicated elsewhere. I'm not sure if
569: * the duplication is absolutely required or not.
570: *
571: * Ideally, this would keep track of ongoing statistics
572: * collection and print out statistics. (& keep track
573: * of h-b-h traces and only print the longest) For now,
574: * it just snoops on what traces it can.
575: */
576: void passive_mode(void)
577: {
578: struct timeval tr;
579: struct ip *ip;
580: struct igmp *igmp;
581: struct tr_resp *r;
582: size_t ipdatalen, iphdrlen, igmpdatalen;
583: size_t len;
584: ssize_t recvlen;
585: socklen_t dummy = 0;
586: u_int32_t smask;
587:
588: if (raddr) {
589: if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, INADDR_ANY);
590: } else k_join(htonl(0xE0000120), INADDR_ANY);
591:
592: while (1) {
593: recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
594: 0, (struct sockaddr *)0, &dummy);
595: gettimeofday(&tr,0);
596:
597: if (recvlen <= 0) {
598: if (recvlen && errno != EINTR) perror("recvfrom");
599: continue;
600: }
601:
602: if ((size_t)recvlen < sizeof(struct ip)) {
603: fprintf(stderr, "packet too short (%zd bytes) for IP header", recvlen);
604: continue;
605: }
606: ip = (struct ip *) recv_buf;
607: if (ip->ip_p == 0) /* ignore cache creation requests */
608: continue;
609:
610: iphdrlen = ip->ip_hl << 2;
611: ipdatalen = ntohs(ip->ip_len) - iphdrlen;
612: if (iphdrlen + ipdatalen != (size_t)recvlen) {
613: fprintf(stderr, "packet shorter (%zd bytes) than hdr+data len (%zu+%zu)\n",
614: recvlen, iphdrlen, ipdatalen);
615: continue;
616: }
617:
618: igmp = (struct igmp *) (recv_buf + iphdrlen);
619: if (ipdatalen < IGMP_MINLEN) {
620: fprintf(stderr, "IP data field too short (%zu bytes) for IGMP from %s\n",
621: ipdatalen, inet_fmt(ip->ip_src.s_addr, s1, sizeof(s1)));
622: continue;
623: }
624: igmpdatalen = ipdatalen - IGMP_MINLEN;
625:
626: switch (igmp->igmp_type) {
627:
628: case IGMP_MTRACE: /* For backward compatibility with 3.3 */
629: case IGMP_MTRACE_RESP:
630: if (igmpdatalen < QLEN) continue;
631: if ((igmpdatalen - QLEN) % RLEN) {
632: printf("packet with incorrect datalen\n");
633: continue;
634: }
635:
636: len = (igmpdatalen - QLEN)/RLEN;
637:
638: break;
639:
640: default:
641: continue;
642: }
643:
644: base.qtime = ((tr.tv_sec + JAN_1970) << 16) +
645: (tr.tv_usec << 10) / 15625;
646: base.rtime = ((tr.tv_sec + JAN_1970) << 16) +
647: (tr.tv_usec << 10) / 15625;
648: base.len = len;
649: bcopy((char *)igmp, (char *)&base.igmp, ipdatalen);
650: /*
651: * If the user specified which traces to monitor,
652: * only accept traces that correspond to the
653: * request
654: */
655: if ((qsrc != 0 && qsrc != base.qhdr.tr_src) ||
656: (qdst != 0 && qdst != base.qhdr.tr_dst) ||
657: (qgrp != 0 && qgrp != igmp->igmp_group.s_addr))
658: continue;
659:
660: printf("Mtrace from %s to %s via group %s (mxhop=%d)\n",
661: inet_fmt(base.qhdr.tr_dst, s1, sizeof(s1)), inet_fmt(base.qhdr.tr_src, s2, sizeof(s2)),
662: inet_fmt(igmp->igmp_group.s_addr, s3, sizeof(s3)), igmp->igmp_code);
663: if (len == 0)
664: continue;
665: printf(" 0 ");
666: print_host(base.qhdr.tr_dst);
667: printf("\n");
668: print_trace(1, &base);
669: r = base.resps + base.len - 1;
670: VAL_TO_MASK(smask, r->tr_smask);
671: if ((r->tr_inaddr & smask) == (base.qhdr.tr_src & smask)) {
672: printf("%3d ", -(base.len+1));
673: print_host(base.qhdr.tr_src);
674: printf("\n");
675: } else if (r->tr_rmtaddr != 0) {
676: printf("%3d ", -(base.len+1));
677: what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
678: "doesn't support mtrace"
679: : "is the next hop");
680: }
681: printf("\n");
682: }
683: }
684:
685: char *print_host(u_int32_t addr)
686: {
687: return print_host2(addr, 0);
688: }
689:
690: /*
691: * On some routers, one interface has a name and the other doesn't.
692: * We always print the address of the outgoing interface, but can
693: * sometimes get the name from the incoming interface. This might be
694: * confusing but should be slightly more helpful than just a "?".
695: */
696: char *print_host2(u_int32_t addr1, u_int32_t addr2)
697: {
698: char *name;
699:
700: if (numeric) {
701: printf("%s", inet_fmt(addr1, s1, sizeof(s1)));
702: return ("");
703: }
704: name = inet_name(addr1);
705: if (*name == '?' && *(name + 1) == '\0' && addr2 != 0)
706: name = inet_name(addr2);
707: printf("%s (%s)", name, inet_fmt(addr1, s1, sizeof(s1)));
708: return (name);
709: }
710:
711: /*
712: * Print responses as received (reverse path from dst to src)
713: */
714: void print_trace(int index, struct resp_buf *buf)
715: {
716: struct tr_resp *r;
717: char *name;
718: int i;
719: int hop;
720: char *ms;
721:
722: i = abs(index);
723: r = buf->resps + i - 1;
724:
725: for (; i <= buf->len; ++i, ++r) {
726: if (index > 0) printf("%3d ", -i);
727: name = print_host2(r->tr_outaddr, r->tr_inaddr);
728: printf(" %s thresh^ %d", proto_type(r->tr_rproto), r->tr_fttl);
729: if (verbose) {
730: hop = t_diff(fixtime(ntohl(r->tr_qarr)), buf->qtime);
731: ms = scale(&hop);
732: printf(" %d%s", hop, ms);
733: }
734: printf(" %s\n", flag_type(r->tr_rflags));
735: memcpy(names[i-1], name, sizeof(names[0]) - 1);
736: names[i-1][sizeof(names[0])-1] = '\0';
737: }
738: }
739:
740: /*
741: * See what kind of router is the next hop
742: */
743: int what_kind(struct resp_buf *buf, char *why)
744: {
745: u_int32_t smask;
746: int retval;
747: int hops = buf->len;
748: struct tr_resp *r = buf->resps + hops - 1;
749: u_int32_t next = r->tr_rmtaddr;
750:
751: retval = send_recv(next, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0]);
752: print_host(next);
753: if (retval) {
754: u_int32_t version = ntohl(incr[0].igmp.igmp_group.s_addr);
755: u_int32_t *p = (u_int32_t *)incr[0].ndata;
756: u_int32_t *ep = p + (incr[0].len >> 2);
757: char *type = "";
758: retval = 0;
759: switch (version & 0xFF) {
760: case 1:
761: type = "proteon/mrouted ";
762: retval = 1;
763: break;
764:
765: case 2:
766: case 3:
767: if (((version >> 8) & 0xFF) < 3) retval = 1;
768: /* Fall through */
769: case 4:
770: type = "mrouted ";
771: break;
772:
773: case 10:
774: type = "cisco ";
775: }
776: printf(" [%s%d.%d] %s\n",
777: type, version & 0xFF, (version >> 8) & 0xFF,
778: why);
779: VAL_TO_MASK(smask, r->tr_smask);
780: while (p < ep) {
781: u_int32_t laddr = *p++;
782: int flags = (ntohl(*p) & 0xFF00) >> 8;
783: int n = ntohl(*p++) & 0xFF;
784: if (!(flags & (DVMRP_NF_DOWN | DVMRP_NF_DISABLED)) &&
785: (laddr & smask) == (qsrc & smask)) {
786: printf("%3d ", -(hops+2));
787: print_host(qsrc);
788: printf("\n");
789: return 1;
790: }
791: p += n;
792: }
793: return retval;
794: }
795: printf(" %s\n", why);
796: return 0;
797: }
798:
799:
800: char *scale(int *hop)
801: {
802: if (*hop > -1000 && *hop < 10000) return (" ms");
803: *hop /= 1000;
804: if (*hop > -1000 && *hop < 10000) return (" s ");
805: return ("s ");
806: }
807:
808: /*
809: * Calculate and print one line of packet loss and packet rate statistics.
810: * Checks for count of all ones from mrouted 2.3 that doesn't have counters.
811: */
812: #define NEITHER 0
813: #define INS 1
814: #define OUTS 2
815: #define BOTH 3
816: void stat_line(struct tr_resp *r, struct tr_resp *s, int have_next, int *rst)
817: {
818: int timediff = (fixtime(ntohl(s->tr_qarr)) -
819: fixtime(ntohl(r->tr_qarr))) >> 16;
820: int v_lost, v_pct;
821: int g_lost, g_pct;
822: int v_out = ntohl(s->tr_vifout) - ntohl(r->tr_vifout);
823: int g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt);
824: int v_pps, g_pps;
825: char v_str[8], g_str[8];
826: int have = NEITHER;
827: int res = *rst;
828:
829: if (timediff == 0) timediff = 1;
830: v_pps = v_out / timediff;
831: g_pps = g_out / timediff;
832:
833: if (v_out && ((s->tr_vifout != 0xFFFFFFFF && s->tr_vifout != 0) ||
834: (r->tr_vifout != 0xFFFFFFFF && r->tr_vifout != 0)))
835: have |= OUTS;
836:
837: if (have_next) {
838: --r, --s, --rst;
839: if ((s->tr_vifin != 0xFFFFFFFF && s->tr_vifin != 0) ||
840: (r->tr_vifin != 0xFFFFFFFF && r->tr_vifin != 0))
841: have |= INS;
842: if (*rst)
843: res = 1;
844: }
845:
846: switch (have) {
847: case BOTH:
848: v_lost = v_out - (ntohl(s->tr_vifin) - ntohl(r->tr_vifin));
849: if (v_out) v_pct = (v_lost * 100 + (v_out >> 1)) / v_out;
850: else v_pct = 0;
851: if (-100 < v_pct && v_pct < 101 && v_out > 10)
852: snprintf(v_str, sizeof v_str, "%3d", v_pct);
853: else memcpy(v_str, " --", 4);
854:
855: g_lost = g_out - (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
856: if (g_out) g_pct = (g_lost * 100 + (g_out >> 1))/ g_out;
857: else g_pct = 0;
858: if (-100 < g_pct && g_pct < 101 && g_out > 10)
859: snprintf(g_str, sizeof g_str, "%3d", g_pct);
860: else memcpy(g_str, " --", 4);
861:
862: printf("%6d/%-5d=%s%%%4d pps",
863: v_lost, v_out, v_str, v_pps);
864: if (res)
865: printf("\n");
866: else
867: printf("%6d/%-5d=%s%%%4d pps\n",
868: g_lost, g_out, g_str, g_pps);
869: break;
870:
871: case INS:
872: v_out = ntohl(s->tr_vifin) - ntohl(r->tr_vifin);
873: v_pps = v_out / timediff;
874: /* Fall through */
875:
876: case OUTS:
877: printf(" %-5d %4d pps",
878: v_out, v_pps);
879: if (res)
880: printf("\n");
881: else
882: printf(" %-5d %4d pps\n",
883: g_out, g_pps);
884: break;
885:
886: case NEITHER:
887: printf("\n");
888: break;
889: }
890:
891: if (debug > 2) {
892: printf("\t\t\t\tv_in: %u ", ntohl(s->tr_vifin));
893: printf("v_out: %u ", ntohl(s->tr_vifout));
894: printf("pkts: %u\n", ntohl(s->tr_pktcnt));
895: printf("\t\t\t\tv_in: %u ", ntohl(r->tr_vifin));
896: printf("v_out: %u ", ntohl(r->tr_vifout));
897: printf("pkts: %u\n", ntohl(r->tr_pktcnt));
898: printf("\t\t\t\tv_in: %u ", ntohl(s->tr_vifin)-ntohl(r->tr_vifin));
899: printf("v_out: %u ", ntohl(s->tr_vifout) - ntohl(r->tr_vifout));
900: printf("pkts: %u ", ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
901: printf("time: %d\n", timediff);
902: printf("\t\t\t\tres: %d\n", res);
903: }
904: }
905:
906: /*
907: * A fixup to check if any pktcnt has been reset, and to fix the
908: * byteorder bugs in mrouted 3.6 on little-endian machines.
909: */
910: void fixup_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new)
911: {
912: int rno = base->len;
913: struct tr_resp *b = base->resps + rno;
914: struct tr_resp *p = prev->resps + rno;
915: struct tr_resp *n = new->resps + rno;
916: int *r = reset + rno;
917: int *s = swaps + rno;
918: int res;
919:
920: /* Check for byte-swappers */
921: while (--rno >= 0) {
922: --n; --p; --b; --s;
923: if (*s || abs(ntohl(n->tr_vifout) - ntohl(p->tr_vifout)) > 100000) {
924: /* This host sends byteswapped reports; swap 'em */
925: if (!*s) {
926: *s = 1;
927: b->tr_qarr = byteswap(b->tr_qarr);
928: b->tr_vifin = byteswap(b->tr_vifin);
929: b->tr_vifout = byteswap(b->tr_vifout);
930: b->tr_pktcnt = byteswap(b->tr_pktcnt);
931: }
932:
933: n->tr_qarr = byteswap(n->tr_qarr);
934: n->tr_vifin = byteswap(n->tr_vifin);
935: n->tr_vifout = byteswap(n->tr_vifout);
936: n->tr_pktcnt = byteswap(n->tr_pktcnt);
937: }
938: }
939:
940: rno = base->len;
941: b = base->resps + rno;
942: p = prev->resps + rno;
943: n = new->resps + rno;
944:
945: while (--rno >= 0) {
946: --n; --p; --b; --r;
947: res = ((ntohl(n->tr_pktcnt) < ntohl(b->tr_pktcnt)) ||
948: (ntohl(n->tr_pktcnt) < ntohl(p->tr_pktcnt)));
949: if (debug > 2)
950: printf("\t\tr=%d, res=%d\n", *r, res);
951: if (*r) {
952: if (res || *r > 1) {
953: /*
954: * This router appears to be a 3.4 with that nasty ol'
955: * neighbor version bug, which causes it to constantly
956: * reset. Just nuke the statistics for this node, and
957: * don't even bother giving it the benefit of the
958: * doubt from now on.
959: */
960: p->tr_pktcnt = b->tr_pktcnt = n->tr_pktcnt;
961: r++;
962: } else {
963: /*
964: * This is simply the situation that the original
965: * fixup_stats was meant to deal with -- that a
966: * 3.3 or 3.4 router deleted a cache entry while
967: * traffic was still active.
968: */
969: *r = 0;
970: break;
971: }
972: } else
973: *r = res;
974: }
975:
976: if (rno < 0) return;
977:
978: rno = base->len;
979: b = base->resps + rno;
980: p = prev->resps + rno;
981:
982: while (--rno >= 0) (--b)->tr_pktcnt = (--p)->tr_pktcnt;
983: }
984:
985: /*
986: * Print responses with statistics for forward path (from src to dst)
987: */
988: int print_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new)
989: {
990: int rtt, hop;
991: char *ms;
992: u_int32_t smask;
993: int rno = base->len - 1;
994: struct tr_resp *b = base->resps + rno;
995: struct tr_resp *p = prev->resps + rno;
996: struct tr_resp *n = new->resps + rno;
997: int *r = reset + rno;
998: u_long resptime = new->rtime;
999: u_long qarrtime = fixtime(ntohl(n->tr_qarr));
1000: u_int ttl = n->tr_fttl;
1001: int first = (base == prev);
1002:
1003: VAL_TO_MASK(smask, b->tr_smask);
1004: printf(" Source Response Dest");
1005: printf(" Packet Statistics For Only For Traffic\n");
1006: printf("%-15s %-15s All Multicast Traffic From %s\n",
1007: ((b->tr_inaddr & smask) == (qsrc & smask)) ? s1 : " * * * ",
1008: inet_fmt(base->qhdr.tr_raddr, s2, sizeof(s2)), inet_fmt(qsrc, s1, sizeof(s1)));
1009: rtt = t_diff(resptime, new->qtime);
1010: ms = scale(&rtt);
1011: printf(" %c __/ rtt%5d%s Lost/Sent = Pct Rate To %s\n",
1012: first ? 'v' : '|', rtt, ms, inet_fmt(qgrp, s2, sizeof(s2)));
1013: if (!first) {
1014: hop = t_diff(resptime, qarrtime);
1015: ms = scale(&hop);
1016: printf(" v / hop%5d%s", hop, ms);
1017: printf(" --------------------- --------------------\n");
1018: }
1019: if (debug > 2) {
1020: printf("\t\t\t\tv_in: %u ", ntohl(n->tr_vifin));
1021: printf("v_out: %u ", ntohl(n->tr_vifout));
1022: printf("pkts: %u\n", ntohl(n->tr_pktcnt));
1023: printf("\t\t\t\tv_in: %u ", ntohl(b->tr_vifin));
1024: printf("v_out: %u ", ntohl(b->tr_vifout));
1025: printf("pkts: %u\n", ntohl(b->tr_pktcnt));
1026: printf("\t\t\t\tv_in: %u ", ntohl(n->tr_vifin) - ntohl(b->tr_vifin));
1027: printf("v_out: %u ", ntohl(n->tr_vifout) - ntohl(b->tr_vifout));
1028: printf("pkts: %u\n", ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt));
1029: printf("\t\t\t\treset: %d\n", *r);
1030: }
1031:
1032: while (TRUE) {
1033: if ((n->tr_inaddr != b->tr_inaddr) || (n->tr_inaddr != b->tr_inaddr))
1034: return 1; /* Route changed */
1035:
1036: if ((n->tr_inaddr != n->tr_outaddr))
1037: printf("%-15s\n", inet_fmt(n->tr_inaddr, s1, sizeof(s1)));
1038: printf("%-15s %-14s %s\n", inet_fmt(n->tr_outaddr, s1, sizeof(s1)), names[rno],
1039: flag_type(n->tr_rflags));
1040:
1041: if (rno-- < 1) break;
1042:
1043: printf(" %c ^ ttl%5d ", first ? 'v' : '|', ttl);
1044: stat_line(p, n, TRUE, r);
1045: if (!first) {
1046: resptime = qarrtime;
1047: qarrtime = fixtime(ntohl((n-1)->tr_qarr));
1048: hop = t_diff(resptime, qarrtime);
1049: ms = scale(&hop);
1050: printf(" v | hop%5d%s", hop, ms);
1051: stat_line(b, n, TRUE, r);
1052: }
1053:
1054: --b, --p, --n, --r;
1055: if (ttl < n->tr_fttl) ttl = n->tr_fttl;
1056: else ++ttl;
1057: }
1058:
1059: printf(" %c \\__ ttl%5d ", first ? 'v' : '|', ttl);
1060: stat_line(p, n, FALSE, r);
1061: if (!first) {
1062: hop = t_diff(qarrtime, new->qtime);
1063: ms = scale(&hop);
1064: printf(" v \\ hop%5d%s", hop, ms);
1065: stat_line(b, n, FALSE, r);
1066: }
1067: printf("%-15s %s\n", inet_fmt(qdst, s1, sizeof(s1)), inet_fmt(lcl_addr, s2, sizeof(s2)));
1068: printf(" Receiver Query Source\n\n");
1069: return 0;
1070: }
1071:
1072:
1073: void usage(void)
1074: {
1075: fprintf(stderr, "Usage: mtrace [-lMnpsv] [-g gateway] [-i if_addr] [-m max_hops] [-q nqueries]\n"
1076: " [-r host] [-S stat_int] [-t ttl] [-w waittime] source [receiver]\n"
1077: " [group]\n");
1078: exit(1);
1079: }
1080:
1081: int main(int argc, char *argv[])
1082: {
1083: int udp;
1084: struct sockaddr_in addr;
1085: socklen_t addrlen = sizeof(addr);
1086: ssize_t recvlen;
1087: struct timeval tv;
1088: struct resp_buf *prev, *new;
1089: struct tr_resp *r;
1090: u_int32_t smask;
1091: int rno;
1092: int hops, nexthop, tries;
1093: u_int32_t lastout = 0;
1094: int numstats = 1;
1095: int waittime;
1096: int seed, ch;
1097: uid_t uid;
1098: const char *errstr;
1099:
1100: while ((ch = getopt(argc, argv, "d:g:i:lm:Mnpq:r:sS:t:vw:")) != -1) {
1101: switch (ch) {
1102: case 'd': /* Unlisted debug print option */
1103: debug = strtonum(optarg, 0, 3, &errstr);
1104: if (errstr) {
1105: warnx("debug level %s", errstr);
1106: debug = 0;
1107: }
1108: break;
1109:
1110: case 'g': /* Last-hop gateway (dest of query) */
1111: gwy = host_addr(optarg);
1112: break;
1113:
1114: case 'l': /* Loop updating stats indefinitely */
1115: numstats = 3153600;
1116: break;
1117:
1118: case 'm': /* Max number of hops to trace */
1119: qno = strtonum(optarg, 1, MAXHOPS, &errstr);
1120: if (errstr) {
1121: warnx("max hops %s", errstr);
1122: qno = 0;
1123: }
1124: break;
1125:
1126: case 'M': /* Use multicast for response */
1127: multicast = TRUE;
1128: break;
1129:
1130: case 'n': /* Don't reverse map host addresses */
1131: numeric = TRUE;
1132: break;
1133:
1134: case 'p': /* Passive listen for traces */
1135: passive = TRUE;
1136: break;
1137:
1138: case 'q': /* Number of query retries */
1139: nqueries = strtonum(optarg, 1, 65535, &errstr);
1140: if (errstr) {
1141: warnx("query retries %s", errstr);
1142: qno = 0;
1143: }
1144: break;
1145:
1146: case 'r': /* Dest for response packet */
1147: raddr = host_addr(optarg);
1148: break;
1149:
1150: case 's': /* Short form, don't wait for stats */
1151: numstats = 0;
1152: break;
1153:
1154: case 'S': /* Stat accumulation interval */
1155: statint = strtonum(optarg, 1, 65535, &errstr);
1156: if (errstr) {
1157: warnx("stat accumulation interval %s", errstr);
1158: statint = 10;
1159: }
1160: break;
1161:
1162: case 't': /* TTL for query packet */
1163: qttl = strtonum(optarg, 1, 32767, &errstr);
1164: if (errstr) {
1165: warnx("TTL for query packet %s", errstr);
1166: qttl = 0;
1167: }
1168: rttl = qttl;
1169: break;
1170:
1171: case 'v': /* Verbosity */
1172: verbose = TRUE;
1173: break;
1174:
1175: case 'w': /* Time to wait for packet arrival */
1176: timeout = strtonum(optarg, 1, 65535, &errstr);
1177: if (errstr) {
1178: warnx("TTL for query packet %s", errstr);
1179: timeout = DEFAULT_TIMEOUT;
1180: }
1181: break;
1182:
1183: case 'i': /* Local interface address */
1184: lcl_addr = host_addr(optarg);
1185: break;
1186:
1187: default:
1188: usage();
1189: }
1190: }
1191: argc -= optind;
1192: argv += optind;
1193:
1194: if (geteuid() != 0) {
1195: fprintf(stderr, "mtrace: must be root\n");
1196: exit(1);
1197: }
1198:
1199: init_igmp();
1200:
1201: uid = getuid();
1202: if (setuid(uid) == -1)
1203: err(1, "setuid");
1204:
1205: if (argc > 0 && (qsrc = host_addr(argv[0]))) { /* Source of path */
1206: if (IN_MULTICAST(ntohl(qsrc))) usage();
1207: argv++, argc--;
1208: if (argc > 0 && (qdst = host_addr(argv[0]))) { /* Dest of path */
1209: argv++, argc--;
1210: if (argc > 0 && (qgrp = host_addr(argv[0]))) { /* Path via group */
1211: argv++, argc--;
1212: }
1213: if (IN_MULTICAST(ntohl(qdst))) {
1214: u_int32_t temp = qdst;
1215: qdst = qgrp;
1216: qgrp = temp;
1217: if (IN_MULTICAST(ntohl(qdst))) usage();
1218: } else if (qgrp && !IN_MULTICAST(ntohl(qgrp))) usage();
1219: }
1220: }
1221:
1222: if (passive) {
1223: passive_mode();
1224: return(0);
1225: }
1226:
1227: if (argc > 0 || qsrc == 0) {
1228: usage();
1229: }
1230:
1231: /*
1232: * Set useful defaults for as many parameters as possible.
1233: */
1234:
1235: defgrp = htonl(0xE0020001); /* MBone Audio (224.2.0.1) */
1236: query_cast = htonl(0xE0000002); /* All routers multicast addr */
1237: resp_cast = htonl(0xE0000120); /* Mtrace response multicast addr */
1238: if (qgrp == 0) qgrp = defgrp;
1239:
1240: /*
1241: * Get default local address for multicasts to use in setting defaults.
1242: */
1243: memset(&addr, 0, sizeof addr);
1244: addr.sin_family = AF_INET;
1245: #ifdef HAVE_SA_LEN
1246: addr.sin_len = sizeof(addr);
1247: #endif
1248: addr.sin_addr.s_addr = qgrp;
1249: addr.sin_port = htons(2000); /* Any port above 1024 will do */
1250:
1251: if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ||
1252: (connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) ||
1253: getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
1254: perror("Determining local address");
1255: exit(1);
1256: }
1257:
1258: #ifdef SUNOS5
1259: /*
1260: * SunOS 5.X prior to SunOS 2.6, getsockname returns 0 for udp socket.
1261: * This call to sysinfo will return the hostname.
1262: * If the default multicast interface (set with the route
1263: * for 224.0.0.0) is not the same as the hostname,
1264: * mtrace -i [if_addr] will have to be used.
1265: */
1266: if (addr.sin_addr.s_addr == 0) {
1267: char myhostname[MAXHOSTNAMELEN];
1268: struct hostent *hp;
1269: int error;
1270:
1271: error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
1272: if (error == -1) {
1273: perror("Getting my hostname");
1274: exit(1);
1275: }
1276:
1277: hp = gethostbyname(myhostname);
1278: if (hp == NULL || hp->h_addrtype != AF_INET ||
1279: hp->h_length != sizeof(addr.sin_addr)) {
1280: perror("Finding IP address for my hostname");
1281: exit(1);
1282: }
1283:
1284: memcpy((char *)&addr.sin_addr.s_addr, hp->h_addr, hp->h_length);
1285: }
1286: #endif
1287:
1288: /*
1289: * Default destination for path to be queried is the local host.
1290: */
1291: if (qdst == 0) qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
1292: dst_netmask = get_netmask(udp, qdst);
1293: close(udp);
1294: if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr;
1295:
1296: /*
1297: * Initialize the seed for random query identifiers.
1298: */
1299: gettimeofday(&tv, 0);
1300: seed = tv.tv_usec ^ lcl_addr;
1301: #ifdef SYSV
1302: srand48(seed);
1303: #else
1304: srandom(seed);
1305: #endif
1306:
1307: /*
1308: * Protect against unicast queries to mrouted versions that might crash.
1309: */
1310: if (gwy && !IN_MULTICAST(ntohl(gwy)))
1311: if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0])) {
1312: int version = ntohl(incr[0].igmp.igmp_group.s_addr) & 0xFFFF;
1313: if (version == 0x0303 || version == 0x0503) {
1314: printf("Don't use -g to address an mrouted 3.%d, it might crash\n",
1315: (version >> 8) & 0xFF);
1316: exit(0);
1317: }
1318: }
1319:
1320: printf("Mtrace from %s to %s via group %s\n",
1321: inet_fmt(qsrc, s1, sizeof(s1)), inet_fmt(qdst, s2, sizeof(s2)), inet_fmt(qgrp, s3, sizeof(s3)));
1322:
1323: if ((qdst & dst_netmask) == (qsrc & dst_netmask)) {
1324: printf("Source & receiver are directly connected, no path to trace\n");
1325: exit(0);
1326: }
1327:
1328: /*
1329: * If the response is to be a multicast address, make sure we
1330: * are listening on that multicast address.
1331: */
1332: if (raddr) {
1333: if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr);
1334: } else k_join(resp_cast, lcl_addr);
1335:
1336: /*
1337: * If the destination is on the local net, the last-hop router can
1338: * be found by multicast to the all-routers multicast group.
1339: * Otherwise, use the group address that is the subject of the
1340: * query since by definition the last-hop router will be a member.
1341: * Set default TTLs for local remote multicasts.
1342: */
1343: restart:
1344:
1345: if (gwy == 0) {
1346: if ((qdst & dst_netmask) == (lcl_addr & dst_netmask))
1347: tdst = query_cast;
1348: else
1349: tdst = qgrp;
1350: } else {
1351: tdst = gwy;
1352: }
1353:
1354: if (IN_MULTICAST(ntohl(tdst))) {
1355: k_set_loop(1); /* If I am running on a router, I need to hear this */
1356: if (tdst == query_cast) k_set_ttl(qttl ? qttl : 1);
1357: else k_set_ttl(qttl ? qttl : MULTICAST_TTL1);
1358: }
1359:
1360: /*
1361: * Try a query at the requested number of hops or MAXHOPS if unspecified.
1362: */
1363: if (qno == 0) {
1364: hops = MAXHOPS;
1365: tries = 1;
1366: printf("Querying full reverse path... ");
1367: fflush(stdout);
1368: } else {
1369: hops = qno;
1370: tries = nqueries;
1371: printf("Querying reverse path, maximum %d hops... ", qno);
1372: fflush(stdout);
1373: }
1374: base.rtime = 0;
1375: base.len = 0;
1376:
1377: recvlen = send_recv(tdst, IGMP_MTRACE, hops, tries, &base);
1378:
1379: /*
1380: * If the initial query was successful, print it. Otherwise, if
1381: * the query max hop count is the default of zero, loop starting
1382: * from one until there is no response for four hops. The extra
1383: * hops allow getting past an mtrace-capable mrouter that can't
1384: * send multicast packets because all phyints are disabled.
1385: */
1386: if (recvlen) {
1387: printf("\n 0 ");
1388: print_host(qdst);
1389: printf("\n");
1390: print_trace(1, &base);
1391: r = base.resps + base.len - 1;
1392: if (r->tr_rflags == TR_OLD_ROUTER || r->tr_rflags == TR_NO_SPACE ||
1393: qno != 0) {
1394: printf("%3d ", -(base.len+1));
1395: what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
1396: "doesn't support mtrace"
1397: : "is the next hop");
1398: } else {
1399: VAL_TO_MASK(smask, r->tr_smask);
1400: if ((r->tr_inaddr & smask) == (qsrc & smask)) {
1401: printf("%3d ", -(base.len+1));
1402: print_host(qsrc);
1403: printf("\n");
1404: }
1405: }
1406: } else if (qno == 0) {
1407: printf("switching to hop-by-hop:\n 0 ");
1408: print_host(qdst);
1409: printf("\n");
1410:
1411: for (hops = 1, nexthop = 1; hops <= MAXHOPS; ++hops) {
1412: printf("%3d ", -hops);
1413: fflush(stdout);
1414:
1415: /*
1416: * After a successful first hop, try switching to the unicast
1417: * address of the last-hop router instead of multicasting the
1418: * trace query. This should be safe for mrouted versions 3.3
1419: * and 3.5 because there is a long route timeout with metric
1420: * infinity before a route disappears. Switching to unicast
1421: * reduces the amount of multicast traffic and avoids a bug
1422: * with duplicate suppression in mrouted 3.5.
1423: */
1424: if (hops == 2 && gwy == 0 &&
1425: (recvlen = send_recv(lastout, IGMP_MTRACE, hops, 1, &base)))
1426: tdst = lastout;
1427: else recvlen = send_recv(tdst, IGMP_MTRACE, hops, nqueries, &base);
1428:
1429: if (recvlen == 0) {
1430: if (hops == 1) break;
1431: if (hops == nexthop) {
1432: if (what_kind(&base, "didn't respond")) {
1433: /* the ask_neighbors determined that the
1434: * not-responding router is the first-hop. */
1435: break;
1436: }
1437: } else if (hops < nexthop + 3) {
1438: printf("\n");
1439: } else {
1440: printf("...giving up\n");
1441: break;
1442: }
1443: continue;
1444: }
1445: r = base.resps + base.len - 1;
1446: if (base.len == hops &&
1447: (hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) {
1448: if (hops == nexthop) {
1449: print_trace(-hops, &base);
1450: } else {
1451: printf("\nResuming...\n");
1452: print_trace(nexthop, &base);
1453: }
1454: } else {
1455: if (base.len < hops) {
1456: /*
1457: * A shorter trace than requested means a fatal error
1458: * occurred along the path, or that the route changed
1459: * to a shorter one.
1460: *
1461: * If the trace is longer than the last one we received,
1462: * then we are resuming from a skipped router (but there
1463: * is still probably a problem).
1464: *
1465: * If the trace is shorter than the last one we
1466: * received, then the route must have changed (and
1467: * there is still probably a problem).
1468: */
1469: if (nexthop <= base.len) {
1470: printf("\nResuming...\n");
1471: print_trace(nexthop, &base);
1472: } else if (nexthop > base.len + 1) {
1473: hops = base.len;
1474: printf("\nRoute must have changed...\n");
1475: print_trace(1, &base);
1476: }
1477: } else {
1478: /*
1479: * The last hop address is not the same as it was;
1480: * the route probably changed underneath us.
1481: */
1482: hops = base.len;
1483: printf("\nRoute must have changed...\n");
1484: print_trace(1, &base);
1485: }
1486: }
1487: lastout = r->tr_outaddr;
1488:
1489: if (base.len < hops ||
1490: r->tr_rmtaddr == 0 ||
1491: (r->tr_rflags & 0x80)) {
1492: VAL_TO_MASK(smask, r->tr_smask);
1493: if (r->tr_rmtaddr) {
1494: if (hops != nexthop) {
1495: printf("\n%3d ", -(base.len+1));
1496: }
1497: what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
1498: "doesn't support mtrace" :
1499: "would be the next hop");
1500: /* XXX could do segmented trace if TR_NO_SPACE */
1501: } else if (r->tr_rflags == TR_NO_ERR &&
1502: (r->tr_inaddr & smask) == (qsrc & smask)) {
1503: printf("%3d ", -(hops + 1));
1504: print_host(qsrc);
1505: printf("\n");
1506: }
1507: break;
1508: }
1509:
1510: nexthop = hops + 1;
1511: }
1512: }
1513:
1514: if (base.rtime == 0) {
1515: printf("Timed out receiving responses\n");
1516: if (IN_MULTICAST(ntohl(tdst))) {
1517: if (tdst == query_cast)
1518: printf("Perhaps no local router has a route for source %s\n",
1519: inet_fmt(qsrc, s1, sizeof(s1)));
1520: else
1521: printf("Perhaps receiver %s is not a member of group %s,\n"
1522: "or no router local to it has a route for source %s,\n"
1523: "or multicast at ttl %d doesn't reach its last-hop router for that source\n",
1524: inet_fmt(qdst, s2, sizeof(s2)), inet_fmt(qgrp, s3, sizeof(s3)), inet_fmt(qsrc, s1, sizeof(s1)),
1525: qttl ? qttl : MULTICAST_TTL1);
1526: }
1527: exit(1);
1528: }
1529:
1530: printf("Round trip time %d ms\n\n", t_diff(base.rtime, base.qtime));
1531:
1532: /*
1533: * Use the saved response which was the longest one received,
1534: * and make additional probes after delay to measure loss.
1535: */
1536: raddr = base.qhdr.tr_raddr;
1537: rttl = base.qhdr.tr_rttl;
1538: gettimeofday(&tv, 0);
1539: waittime = statint - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16));
1540: prev = &base;
1541: new = &incr[numstats&1];
1542:
1543: while (numstats--) {
1544: if (waittime < 1) printf("\n");
1545: else {
1546: printf("Waiting to accumulate statistics... ");
1547: fflush(stdout);
1548: sleep((unsigned)waittime);
1549: }
1550: rno = base.len;
1551: recvlen = send_recv(tdst, IGMP_MTRACE, rno, nqueries, new);
1552:
1553: if (recvlen == 0) {
1554: printf("Timed out.\n");
1555: exit(1);
1556: }
1557:
1558: if (rno != new->len) {
1559: printf("Trace length doesn't match:\n");
1560: /*
1561: * XXX Should this trace result be printed, or is that
1562: * too verbose? Perhaps it should just say restarting.
1563: * But if the path is changing quickly, this may be the
1564: * only snapshot of the current path. But, if the path
1565: * is changing that quickly, does the current path really
1566: * matter?
1567: */
1568: print_trace(1, new);
1569: printf("Restarting.\n\n");
1570: numstats++;
1571: goto restart;
1572: }
1573:
1574: printf("Results after %d seconds:\n\n",
1575: (int)((new->qtime - base.qtime) >> 16));
1576: fixup_stats(&base, prev, new);
1577: if (print_stats(&base, prev, new)) {
1578: printf("Route changed:\n");
1579: print_trace(1, new);
1580: printf("Restarting.\n\n");
1581: goto restart;
1582: }
1583: prev = new;
1584: new = &incr[numstats&1];
1585: waittime = statint;
1586: }
1587:
1588: /*
1589: * If the response was multicast back, leave the group
1590: */
1591: if (raddr) {
1592: if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr);
1593: } else k_leave(resp_cast, lcl_addr);
1594:
1595: return (0);
1596: }
1597:
1598: void check_vif_state(void)
1599: {
1600: logit(LOG_WARNING, errno, "sendto");
1601: }
1602:
1603: /*
1604: * Log errors and other messages to stderr, according to the severity
1605: * of the message and the current debug level. For errors of severity
1606: * LOG_ERR or worse, terminate the program.
1607: */
1608: void logit(int severity, int syserr, const char *format, ...)
1609: {
1610: va_list ap;
1611:
1612: switch (debug) {
1613: case 0:
1614: if (severity > LOG_WARNING)
1615: return;
1616: case 1:
1617: if (severity > LOG_NOTICE)
1618: return;
1619: case 2:
1620: if (severity > LOG_INFO)
1621: return;
1622: default:
1623: if (severity == LOG_WARNING)
1624: fprintf(stderr, "warning - ");
1625: va_start(ap, format);
1626: vfprintf(stderr, format, ap);
1627: va_end(ap);
1628: if (syserr == 0)
1629: fprintf(stderr, "\n");
1630: else
1631: fprintf(stderr, ": %s\n", strerror(syserr));
1632: }
1633:
1634: if (severity <= LOG_ERR)
1635: exit(1);
1636: }
1637:
1638: /* dummies */
1639: void accept_probe(u_int32_t UNUSED src, u_int32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen, u_int32_t UNUSED level)
1640: {
1641: }
1642:
1643: void accept_group_report(u_int32_t UNUSED src, u_int32_t UNUSED dst, u_int32_t UNUSED group, int UNUSED r_type)
1644: {
1645: }
1646:
1647: void accept_neighbor_request2(u_int32_t UNUSED src, u_int32_t UNUSED dst)
1648: {
1649: }
1650:
1651: void accept_report(u_int32_t UNUSED src, u_int32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen, u_int32_t UNUSED level)
1652: {
1653: }
1654:
1655: void accept_neighbor_request(u_int32_t UNUSED src, u_int32_t UNUSED dst)
1656: {
1657: }
1658:
1659: void accept_prune(u_int32_t UNUSED src, u_int32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen)
1660: {
1661: }
1662:
1663: void accept_graft(u_int32_t UNUSED src, u_int32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen)
1664: {
1665: }
1666:
1667: void accept_g_ack(u_int32_t UNUSED src, u_int32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen)
1668: {
1669: }
1670:
1671: void add_table_entry(u_int32_t UNUSED origin, u_int32_t UNUSED mcastgrp)
1672: {
1673: }
1674:
1675: void accept_leave_message(u_int32_t UNUSED src, u_int32_t UNUSED dst, u_int32_t UNUSED group)
1676: {
1677: }
1678:
1679: void accept_mtrace(u_int32_t UNUSED src, u_int32_t UNUSED dst, u_int32_t UNUSED group, char UNUSED *data, u_int8_t UNUSED no, size_t UNUSED datalen)
1680: {
1681: }
1682:
1683: void accept_membership_query(u_int32_t UNUSED src, u_int32_t UNUSED dst, u_int32_t UNUSED group, int UNUSED tmo)
1684: {
1685: }
1686:
1687: void accept_neighbors(u_int32_t UNUSED src, u_int32_t UNUSED dst, u_char UNUSED *p, size_t UNUSED datalen, u_int32_t UNUSED level)
1688: {
1689: }
1690:
1691: void accept_neighbors2(u_int32_t UNUSED src, u_int32_t UNUSED dst, u_char UNUSED *p, size_t UNUSED datalen, u_int32_t UNUSED level)
1692: {
1693: }
1694:
1695: void accept_info_request(u_int32_t UNUSED src, u_int32_t UNUSED dst, u_char UNUSED *p, size_t UNUSED datalen)
1696: {
1697: }
1698:
1699: void accept_info_reply(u_int32_t UNUSED src, u_int32_t UNUSED dst, u_char UNUSED *p, size_t UNUSED datalen)
1700: {
1701: }
1702:
1703: /**
1704: * Local Variables:
1705: * version-control: t
1706: * indent-tabs-mode: t
1707: * c-file-style: "ellemtel"
1708: * c-basic-offset: 4
1709: * End:
1710: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>