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