"Fossies" - the Fresh Open Source Software Archive

Member "mrouted-3.9.8/mtrace.c" (1 Jan 2017, 49013 Bytes) of package /linux/misc/mrouted-3.9.8.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mtrace.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.9.7_vs_3.9.8.

    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     uint32_t qtime;     /* Time query was issued */
   77     uint32_t 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 uint32_t defgrp;            /* Default group if not specified */
  107 uint32_t query_cast;            /* All routers multicast addr */
  108 uint32_t resp_cast;         /* Mtrace response multicast addr */
  109 
  110 uint32_t lcl_addr = 0;          /* This host address, in NET order */
  111 uint32_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 uint32_t qsrc  = 0;     /* Source address in the query */
  118 uint32_t qgrp  = 0;     /* Group address in the query */
  119 uint32_t qdst  = 0;     /* Destination (receiver) address in query */
  120 uint8_t  qno   = 0;     /* Max number of hops to query */
  121 uint32_t raddr = 0;     /* Address where response should be sent */
  122 int      qttl  = 0;     /* TTL for the query packet */
  123 uint8_t  rttl  = 0;     /* TTL for the response packet */
  124 uint32_t gwy   = 0;     /* User-supplied last-hop router address */
  125 uint32_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(uint32_t addr);
  131 uint32_t        host_addr(char *name);
  132 /* uint32_t is promoted uint8_t */
  133 char *          proto_type(uint32_t type);
  134 char *          flag_type(uint32_t type);
  135 
  136 uint32_t        get_netmask(int s, uint32_t dst);
  137 int         get_ttl(struct resp_buf *buf);
  138 int         t_diff(uint32_t a, uint32_t b);
  139 uint32_t        fixtime(uint32_t time);
  140 int         send_recv(uint32_t dst, int type, int code, int tries, struct resp_buf *save);
  141 char *          print_host(uint32_t addr);
  142 char *          print_host2(uint32_t addr1, uint32_t addr2);
  143 void            print_trace(int index, struct resp_buf *buf);
  144 int         what_kind(struct resp_buf *buf, char *why);
  145 char *          scale(int *hop);
  146 void            stat_line(struct tr_resp *r, struct tr_resp *s, int have_next, int *res);
  147 void            fixup_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new);
  148 int         print_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new);
  149 void            check_vif_state(void);
  150 uint32_t        byteswap(uint32_t v);
  151 
  152 
  153 char *inet_name(uint32_t addr)
  154 {
  155     struct hostent *e;
  156 
  157     e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
  158 
  159     return e ? e->h_name : "?";
  160 }
  161 
  162 uint32_t host_addr(char *name)
  163 {
  164     struct hostent *e = (struct hostent *)0;
  165     uint32_t addr;
  166     int i, dots = 3;
  167     char buf[40];
  168     char *ip = name;
  169     char *op = buf;
  170 
  171     /*
  172      * Undo BSD's favor -- take fewer than 4 octets as net/subnet address
  173      * if the name is all numeric.
  174      */
  175     for (i = sizeof(buf) - 7; i > 0; --i) {
  176     if (*ip == '.') --dots;
  177     else if (*ip == '\0') break;
  178     else if (!isdigit((int)*ip)) dots = 0;  /* Not numeric, don't add zeroes */
  179     *op++ = *ip++;
  180     }
  181     for (i = 0; i < dots; ++i) {
  182     *op++ = '.';
  183     *op++ = '0';
  184     }
  185     *op = '\0';
  186 
  187     if (dots <= 0) e = gethostbyname(name);
  188     if (e) memcpy((char *)&addr, e->h_addr_list[0], e->h_length);
  189     else {
  190     addr = inet_addr(buf);
  191     if (addr == INADDR_NONE) {
  192         addr = 0;
  193         printf("Could not parse %s as host name or address\n", name);
  194     }
  195     }
  196     return addr;
  197 }
  198 
  199 
  200 char *proto_type(uint32_t type)
  201 {
  202     static char buf[80];
  203 
  204     switch (type) {
  205     case PROTO_DVMRP:
  206         return ("DVMRP");
  207     case PROTO_MOSPF:
  208         return ("MOSPF");
  209     case PROTO_PIM:
  210         return ("PIM");
  211     case PROTO_CBT:
  212         return ("CBT");
  213     default:
  214         (void) snprintf(buf, sizeof buf, "Unknown protocol code %d", type);
  215         return (buf);
  216     }
  217 }
  218 
  219 
  220 char *flag_type(uint32_t type)
  221 {
  222     static char buf[80];
  223 
  224     switch (type) {
  225     case TR_NO_ERR:
  226         return ("");
  227     case TR_WRONG_IF:
  228         return ("Wrong interface");
  229     case TR_PRUNED:
  230         return ("Prune sent upstream");
  231     case TR_OPRUNED:
  232         return ("Output pruned");
  233     case TR_SCOPED:
  234         return ("Hit scope boundary");
  235     case TR_NO_RTE:
  236         return ("No route");
  237     case TR_OLD_ROUTER:
  238         return ("Next router no mtrace");
  239     case TR_NO_FWD:
  240         return ("Not forwarding");
  241     case TR_NO_SPACE:
  242         return ("No space in packet");
  243     default:
  244         (void) snprintf(buf, sizeof buf, "Unknown error code %d", type);
  245         return (buf);
  246     }
  247 }    
  248 
  249 /*
  250  * If destination is on a local net, get the netmask, else set the
  251  * netmask to all ones.  There are two side effects: if the local
  252  * address was not explicitly set, and if the destination is on a
  253  * local net, use that one; in either case, verify that the local
  254  * address is valid.
  255  */
  256 uint32_t get_netmask(int UNUSED s, uint32_t dst)
  257 {
  258     uint32_t if_addr, if_mask;
  259     uint32_t retval = 0xFFFFFFFF;
  260     int found = FALSE;
  261     struct ifaddrs *ifap, *ifa;
  262 
  263     if (getifaddrs(&ifap) != 0) {
  264     perror("getifaddrs");
  265     return (retval);
  266     }
  267     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
  268     if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) 
  269         continue;
  270     if_addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
  271     if_mask = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr.s_addr;
  272     if ((dst & if_mask) == (if_addr & if_mask)) {
  273         retval = if_mask;
  274         if (lcl_addr == 0)
  275         lcl_addr = if_addr;
  276     }
  277     if (lcl_addr == if_addr)
  278         found = TRUE;
  279     }
  280     if (!found && lcl_addr != 0) {
  281     printf("Interface address is not valid\n");
  282     exit(1);
  283     }
  284     freeifaddrs(ifap);
  285     return (retval);
  286 }
  287 
  288 
  289 int get_ttl(struct resp_buf *buf)
  290 {
  291     int rno;
  292     struct tr_resp *b;
  293     uint32_t ttl;
  294 
  295     if (buf && (rno = buf->len) > 0) {
  296     b = buf->resps + rno - 1;
  297     ttl = b->tr_fttl;
  298 
  299     while (--rno > 0) {
  300         --b;
  301         if (ttl < b->tr_fttl) ttl = b->tr_fttl;
  302         else ++ttl;
  303     }
  304     ttl += MULTICAST_TTL_INC;
  305     if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1;
  306     if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX;
  307     return (ttl);
  308     } else return(MULTICAST_TTL1);
  309 }
  310 
  311 /*
  312  * Calculate the difference between two 32-bit NTP timestamps and return
  313  * the result in milliseconds.
  314  */
  315 int t_diff(uint32_t a, uint32_t b)
  316 {
  317     int d = a - b;
  318 
  319     return ((d * 125) >> 13);
  320 }
  321 
  322 /*
  323  * Fixup for incorrect time format in 3.3 mrouted.
  324  * This is possible because (JAN_1970 mod 64K) is quite close to 32K,
  325  * so correct and incorrect times will be far apart.
  326  */
  327 uint32_t fixtime(uint32_t time)
  328 {
  329     if (abs((int)(time-base.qtime)) > 0x3FFFFFFF)
  330         time = ((time & 0xFFFF0000) + (JAN_1970 << 16)) +
  331         ((time & 0xFFFF) << 14) / 15625;
  332     return (time);
  333 }
  334 
  335 /*
  336  * Swap bytes for poor little-endian machines that don't byte-swap
  337  */
  338 uint32_t byteswap(uint32_t v)
  339 {
  340     return ((v << 24) | ((v & 0xff00) << 8) |
  341         ((v >> 8) & 0xff00) | (v >> 24));
  342 }
  343 
  344 int send_recv(uint32_t dst, int type, int code, int tries, struct resp_buf *save)
  345 {
  346     struct timeval tq, tr, tv;
  347     struct ip *ip;
  348     struct igmp *igmp;
  349     struct tr_query *query, *rquery;
  350     size_t ipdatalen, iphdrlen;
  351     uint32_t local, group;
  352     int datalen;
  353     struct pollfd pfd[1];
  354     int count, len, i;
  355     size_t igmpdatalen;
  356     ssize_t recvlen;
  357     socklen_t dummy = 0;
  358 
  359     if (type == IGMP_MTRACE) {
  360     group = qgrp;
  361     datalen = sizeof(struct tr_query);
  362     } else {
  363     group = htonl(MROUTED_LEVEL);
  364     datalen = 0;
  365     }
  366     if (IN_MULTICAST(ntohl(dst))) local = lcl_addr;
  367     else local = INADDR_ANY;
  368 
  369     /*
  370      * If the reply address was not explicitly specified, start off
  371      * with the unicast address of this host.  Then, if there is no
  372      * response after trying half the tries with unicast, switch to
  373      * the standard multicast reply address.  If the TTL was also not
  374      * specified, set a multicast TTL and if needed increase it for the
  375      * last quarter of the tries.
  376      */
  377     query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
  378     query->tr_raddr = raddr ? raddr : multicast ? resp_cast : lcl_addr;
  379     query->tr_rttl  = rttl ? rttl :
  380     IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL;
  381     query->tr_src   = qsrc;
  382     query->tr_dst   = qdst;
  383 
  384     for (i = tries ; i > 0; --i) {
  385     if (tries == nqueries && raddr == 0) {
  386         if (i == ((nqueries + 1) >> 1)) {
  387         query->tr_raddr = resp_cast;
  388         if (rttl == 0) query->tr_rttl = get_ttl(save);
  389         }
  390         if (i <= ((nqueries + 3) >> 2) && rttl == 0) {
  391         query->tr_rttl += MULTICAST_TTL_INC;
  392         if (query->tr_rttl > MULTICAST_TTL_MAX)
  393             query->tr_rttl = MULTICAST_TTL_MAX;
  394         }
  395     }
  396 
  397     /*
  398      * Change the qid for each request sent to avoid being confused
  399      * by duplicate responses
  400      */
  401     query->tr_qid  = ((uint32_t)rand() >> 8);
  402 
  403     /*
  404      * Set timer to calculate delays, then send query
  405      */
  406     gettimeofday(&tq, 0);
  407     send_igmp(local, dst, type, code, group, datalen);
  408 
  409     /*
  410      * Wait for response, discarding false alarms
  411      */
  412     pfd[0].fd = igmp_socket;
  413     pfd[0].events = POLLIN;
  414     while (TRUE) {
  415         gettimeofday(&tv, 0);
  416         tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec;
  417         tv.tv_usec = tq.tv_usec - tv.tv_usec;
  418         if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec;
  419         if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0;
  420 
  421         count = poll(pfd, 1, tv.tv_sec * 1000);
  422 
  423         if (count < 0) {
  424         if (errno != EINTR) perror("poll");
  425         continue;
  426         } else if (count == 0) {
  427         printf("* ");
  428         fflush(stdout);
  429         break;
  430         }
  431 
  432         gettimeofday(&tr, 0);
  433         recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
  434                    0, (struct sockaddr *)0, &dummy);
  435 
  436         if (recvlen <= 0) {
  437         if (recvlen && errno != EINTR) perror("recvfrom");
  438         continue;
  439         }
  440 
  441         if ((size_t)recvlen < sizeof(struct ip)) {
  442         fprintf(stderr, "packet too short (%zd bytes) for IP header", recvlen);
  443         continue;
  444         }
  445         ip = (struct ip *) recv_buf;
  446         if (ip->ip_p == 0)  /* ignore cache creation requests */
  447         continue;
  448 
  449         iphdrlen = ip->ip_hl << 2;
  450 #ifdef HAVE_IP_HDRINCL_BSD_ORDER
  451         ipdatalen = ip->ip_len - iphdrlen;
  452 #else
  453         ipdatalen = ntohs(ip->ip_len) - iphdrlen;
  454 #endif
  455         if (iphdrlen + ipdatalen != (size_t)recvlen) {
  456         fprintf(stderr, "packet shorter (%zd bytes) than hdr+data len (%zu+%zu)\n",
  457             recvlen, iphdrlen, ipdatalen);
  458         continue;
  459         }
  460 
  461         igmp = (struct igmp *) (recv_buf + iphdrlen);
  462         if (ipdatalen < IGMP_MINLEN) {
  463         fprintf(stderr,
  464             "IP data field too short (%zu bytes) for IGMP from %s\n",
  465             ipdatalen, inet_fmt(ip->ip_src.s_addr, s1, sizeof(s1)));
  466         continue;
  467         }
  468         igmpdatalen = ipdatalen - IGMP_MINLEN;
  469 
  470         switch (igmp->igmp_type) {
  471 
  472         case IGMP_DVMRP:
  473             if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue;
  474             len = igmpdatalen;
  475             /*
  476              * Accept DVMRP_NEIGHBORS2 response if it comes from the
  477              * address queried or if that address is one of the local
  478              * addresses in the response.
  479              */
  480             if (ip->ip_src.s_addr != dst) {
  481             uint32_t *p = (uint32_t *)(igmp + 1);
  482             uint32_t *ep = p + (len >> 2);
  483             while (p < ep) {
  484                 uint32_t laddr = *p++;
  485                 int n = ntohl(*p++) & 0xFF;
  486                 if (laddr == dst) {
  487                 ep = p + 1;     /* ensure p < ep after loop */
  488                 break;
  489                 }
  490                 p += n;
  491             }
  492             if (p >= ep) continue;
  493             }
  494             break;
  495 
  496         case IGMP_MTRACE:       /* For backward compatibility with 3.3 */
  497         case IGMP_MTRACE_RESP:
  498             if (igmpdatalen <= QLEN) continue;
  499             if ((igmpdatalen - QLEN) % RLEN) {
  500             printf("packet with incorrect datalen\n");
  501             continue;
  502             }
  503 
  504             /*
  505              * Ignore responses that don't match query.
  506              */
  507             rquery = (struct tr_query *)(igmp + 1);
  508             if (rquery->tr_qid != query->tr_qid) continue;
  509             if (rquery->tr_src != qsrc) continue;
  510             if (rquery->tr_dst != qdst) continue;
  511             len = (igmpdatalen - QLEN) / RLEN;
  512 
  513             /*
  514              * Ignore trace queries passing through this node when
  515              * mtrace is run on an mrouter that is in the path
  516              * (needed only because IGMP_MTRACE is accepted above
  517              * for backward compatibility with multicast release 3.3).
  518              */
  519             if (igmp->igmp_type == IGMP_MTRACE) {
  520             struct tr_resp *r = (struct tr_resp *)(rquery+1) + len - 1;
  521             uint32_t smask;
  522 
  523             VAL_TO_MASK(smask, r->tr_smask);
  524             if (len < code && (r->tr_inaddr & smask) != (qsrc & smask)
  525                 && r->tr_rmtaddr != 0 && !(r->tr_rflags & 0x80))
  526                 continue;
  527             }
  528 
  529             /*
  530              * A match, we'll keep this one.
  531              */
  532             if (len > code) {
  533             fprintf(stderr,
  534                 "Num hops received (%d) exceeds request (%d)\n",
  535                 len, code);
  536             }
  537             rquery->tr_raddr = query->tr_raddr; /* Insure these are */
  538             rquery->tr_rttl = query->tr_rttl;   /* as we sent them */
  539             break;
  540 
  541         default:
  542             continue;
  543         }
  544 
  545         /*
  546          * Most of the sanity checking done at this point.
  547          * Return this packet we have been waiting for.
  548          */
  549         if (save) {
  550         save->qtime = ((tq.tv_sec + JAN_1970) << 16) +
  551             (tq.tv_usec << 10) / 15625;
  552         save->rtime = ((tr.tv_sec + JAN_1970) << 16) +
  553             (tr.tv_usec << 10) / 15625;
  554         save->len = len;
  555         memmove((char *)&save->igmp, (char *)igmp, ipdatalen);
  556         }
  557         return recvlen;
  558     }
  559     }
  560     return 0;
  561 }
  562 
  563 /*
  564  * Most of this code is duplicated elsewhere.  I'm not sure if
  565  * the duplication is absolutely required or not.
  566  *
  567  * Ideally, this would keep track of ongoing statistics
  568  * collection and print out statistics.  (& keep track
  569  * of h-b-h traces and only print the longest)  For now,
  570  * it just snoops on what traces it can.
  571  */
  572 void passive_mode(void)
  573 {
  574     struct timeval tr;
  575     struct ip *ip;
  576     struct igmp *igmp;
  577     struct tr_resp *r;
  578     size_t ipdatalen, iphdrlen, igmpdatalen;
  579     size_t len;
  580     ssize_t recvlen;
  581     socklen_t dummy = 0;
  582     uint32_t smask;
  583 
  584     if (raddr) {
  585     if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, INADDR_ANY);
  586     } else k_join(htonl(0xE0000120), INADDR_ANY);
  587 
  588     while (1) {
  589     recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
  590                0, (struct sockaddr *)0, &dummy);
  591     gettimeofday(&tr,0);
  592 
  593     if (recvlen <= 0) {
  594         if (recvlen && errno != EINTR) perror("recvfrom");
  595         continue;
  596     }
  597 
  598     if ((size_t)recvlen < sizeof(struct ip)) {
  599         fprintf(stderr, "packet too short (%zd bytes) for IP header", recvlen);
  600         continue;
  601     }
  602     ip = (struct ip *) recv_buf;
  603     if (ip->ip_p == 0)  /* ignore cache creation requests */
  604         continue;
  605 
  606     iphdrlen = ip->ip_hl << 2;
  607 #ifdef HAVE_IP_HDRINCL_BSD_ORDER
  608     ipdatalen = ip->ip_len - iphdrlen;
  609 #else
  610     ipdatalen = ntohs(ip->ip_len) - iphdrlen;
  611 #endif
  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     memmove((char *)&base.igmp, (char *)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(uint32_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(uint32_t addr1, uint32_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     uint32_t smask;
  746     int retval;
  747     int hops = buf->len;
  748     struct tr_resp *r = buf->resps + hops - 1;
  749     uint32_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     uint32_t version = ntohl(incr[0].igmp.igmp_group.s_addr);
  755     uint32_t *p = (uint32_t *)incr[0].ndata;
  756     uint32_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         uint32_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  * Calculate the difference between two unsigned 32-bit counters
  908  */
  909 static uint32_t u_diff(uint32_t u, uint32_t v)
  910 {
  911     return (u >= v ? u - v : v - u);
  912 }
  913 
  914 /*
  915  * A fixup to check if any pktcnt has been reset, and to fix the
  916  * byteorder bugs in mrouted 3.6 on little-endian machines.
  917  */
  918 void fixup_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new)
  919 {
  920     int rno = base->len;
  921     struct tr_resp *b = base->resps + rno;
  922     struct tr_resp *p = prev->resps + rno;
  923     struct tr_resp *n = new->resps + rno;
  924     int *r = reset + rno;
  925     int *s = swaps + rno;
  926     int res;
  927 
  928     /* Check for byte-swappers */
  929     while (--rno >= 0) {
  930     --n; --p; --b; --s;
  931     if (*s || u_diff(ntohl(n->tr_vifout), ntohl(p->tr_vifout)) > 100000) {
  932         /* This host sends byteswapped reports; swap 'em */
  933         if (!*s) {
  934         *s = 1;
  935         b->tr_qarr = byteswap(b->tr_qarr);
  936         b->tr_vifin = byteswap(b->tr_vifin);
  937         b->tr_vifout = byteswap(b->tr_vifout);
  938         b->tr_pktcnt = byteswap(b->tr_pktcnt);
  939         }
  940 
  941         n->tr_qarr = byteswap(n->tr_qarr);
  942         n->tr_vifin = byteswap(n->tr_vifin);
  943         n->tr_vifout = byteswap(n->tr_vifout);
  944         n->tr_pktcnt = byteswap(n->tr_pktcnt);
  945     }
  946     }
  947 
  948     rno = base->len;
  949     b = base->resps + rno;
  950     p = prev->resps + rno;
  951     n = new->resps + rno;
  952 
  953     while (--rno >= 0) {
  954     --n; --p; --b; --r;
  955     res = ((ntohl(n->tr_pktcnt) < ntohl(b->tr_pktcnt)) ||
  956            (ntohl(n->tr_pktcnt) < ntohl(p->tr_pktcnt)));
  957     if (debug > 2)
  958             printf("\t\tr=%d, res=%d\n", *r, res);
  959     if (*r) {
  960         if (res || *r > 1) {
  961         /*
  962          * This router appears to be a 3.4 with that nasty ol'
  963          * neighbor version bug, which causes it to constantly
  964          * reset.  Just nuke the statistics for this node, and
  965          * don't even bother giving it the benefit of the
  966          * doubt from now on.
  967          */
  968         p->tr_pktcnt = b->tr_pktcnt = n->tr_pktcnt;
  969         r++;
  970         } else {
  971         /*
  972          * This is simply the situation that the original
  973          * fixup_stats was meant to deal with -- that a
  974          * 3.3 or 3.4 router deleted a cache entry while
  975          * traffic was still active.
  976          */
  977         *r = 0;
  978         break;
  979         }
  980     } else
  981         *r = res;
  982     }
  983 
  984     if (rno < 0) return;
  985 
  986     rno = base->len;
  987     b = base->resps + rno;
  988     p = prev->resps + rno;
  989 
  990     while (--rno >= 0) (--b)->tr_pktcnt = (--p)->tr_pktcnt;
  991 }
  992 
  993 /*
  994  * Print responses with statistics for forward path (from src to dst)
  995  */
  996 int print_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new)
  997 {
  998     int rtt, hop;
  999     char *ms;
 1000     uint32_t smask;
 1001     int rno = base->len - 1;
 1002     struct tr_resp *b = base->resps + rno;
 1003     struct tr_resp *p = prev->resps + rno;
 1004     struct tr_resp *n = new->resps + rno;
 1005     int *r = reset + rno;
 1006     uint32_t resptime = new->rtime;
 1007     uint32_t qarrtime = fixtime(ntohl(n->tr_qarr));
 1008     uint32_t ttl = n->tr_fttl;
 1009     int first = (base == prev);
 1010 
 1011     VAL_TO_MASK(smask, b->tr_smask);
 1012     printf("  Source        Response Dest");
 1013     printf("    Packet Statistics For     Only For Traffic\n");
 1014     printf("%-15s %-15s  All Multicast Traffic     From %s\n",
 1015        ((b->tr_inaddr & smask) == (qsrc & smask)) ? s1 : "   * * *       ",
 1016        inet_fmt(base->qhdr.tr_raddr, s2, sizeof(s2)), inet_fmt(qsrc, s1, sizeof(s1)));
 1017     rtt = t_diff(resptime, new->qtime);
 1018     ms = scale(&rtt);
 1019     printf("     %c       __/  rtt%5d%s    Lost/Sent = Pct  Rate       To %s\n",
 1020        first ? 'v' : '|', rtt, ms, inet_fmt(qgrp, s2, sizeof(s2)));
 1021     if (!first) {
 1022     hop = t_diff(resptime, qarrtime);
 1023     ms = scale(&hop);
 1024     printf("     v      /     hop%5d%s", hop, ms);
 1025     printf("    ---------------------     --------------------\n");
 1026     }
 1027     if (debug > 2) {
 1028     printf("\t\t\t\tv_in: %u ", ntohl(n->tr_vifin));
 1029     printf("v_out: %u ", ntohl(n->tr_vifout));
 1030     printf("pkts: %u\n", ntohl(n->tr_pktcnt));
 1031     printf("\t\t\t\tv_in: %u ", ntohl(b->tr_vifin));
 1032     printf("v_out: %u ", ntohl(b->tr_vifout));
 1033     printf("pkts: %u\n", ntohl(b->tr_pktcnt));
 1034     printf("\t\t\t\tv_in: %u ", ntohl(n->tr_vifin) - ntohl(b->tr_vifin));
 1035     printf("v_out: %u ", ntohl(n->tr_vifout) - ntohl(b->tr_vifout));
 1036     printf("pkts: %u\n", ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt));
 1037     printf("\t\t\t\treset: %d\n", *r);
 1038     }
 1039 
 1040     while (TRUE) {
 1041     if ((n->tr_inaddr != b->tr_inaddr) || (n->tr_inaddr != b->tr_inaddr))
 1042         return 1;       /* Route changed */
 1043 
 1044     if ((n->tr_inaddr != n->tr_outaddr))
 1045         printf("%-15s\n", inet_fmt(n->tr_inaddr, s1, sizeof(s1)));
 1046     printf("%-15s %-14s %s\n", inet_fmt(n->tr_outaddr, s1, sizeof(s1)), names[rno],
 1047            flag_type(n->tr_rflags));
 1048 
 1049     if (rno-- < 1) break;
 1050 
 1051     printf("     %c     ^      ttl%5d   ", first ? 'v' : '|', ttl);
 1052     stat_line(p, n, TRUE, r);
 1053     if (!first) {
 1054         resptime = qarrtime;
 1055         qarrtime = fixtime(ntohl((n-1)->tr_qarr));
 1056         hop = t_diff(resptime, qarrtime);
 1057         ms = scale(&hop);
 1058         printf("     v     |      hop%5d%s", hop, ms);
 1059         stat_line(b, n, TRUE, r);
 1060     }
 1061 
 1062     --b, --p, --n, --r;
 1063     if (ttl < n->tr_fttl) ttl = n->tr_fttl;
 1064     else ++ttl;
 1065     }
 1066        
 1067     printf("     %c      \\__   ttl%5d   ", first ? 'v' : '|', ttl);
 1068     stat_line(p, n, FALSE, r);
 1069     if (!first) {
 1070     hop = t_diff(qarrtime, new->qtime);
 1071     ms = scale(&hop);
 1072     printf("     v         \\  hop%5d%s", hop, ms);
 1073     stat_line(b, n, FALSE, r);
 1074     }
 1075     printf("%-15s %s\n", inet_fmt(qdst, s1, sizeof(s1)), inet_fmt(lcl_addr, s2, sizeof(s2)));
 1076     printf("  Receiver      Query Source\n\n");
 1077     return 0;
 1078 }
 1079 
 1080 
 1081 void usage(void)
 1082 {
 1083     fprintf(stderr, "Usage: mtrace [-lMnpsv] [-g gateway] [-i if_addr] [-m max_hops] [-q nqueries]\n"
 1084         "              [-r host] [-S stat_int] [-t ttl] [-w waittime] source [receiver]\n"
 1085         "              [group]\n");
 1086     exit(1);
 1087 }
 1088 
 1089 int main(int argc, char *argv[])
 1090 {
 1091     int udp;
 1092     struct sockaddr_in addr;
 1093     socklen_t addrlen = sizeof(addr);
 1094     ssize_t recvlen;
 1095     struct timeval tv;
 1096     struct resp_buf *prev, *new;
 1097     struct tr_resp *r;
 1098     uint32_t smask;
 1099     int rno;
 1100     int hops, nexthop, tries;
 1101     uint32_t lastout = 0;
 1102     int numstats = 1;
 1103     int waittime;
 1104     int seed, ch;
 1105     uid_t uid;
 1106     const char *errstr;
 1107 
 1108     while ((ch = getopt(argc, argv, "d:g:i:lm:Mnpq:r:sS:t:vw:")) != -1) {
 1109         switch (ch) {
 1110             case 'd':           /* Unlisted debug print option */
 1111                 debug = strtonum(optarg, 0, 3, &errstr);
 1112                 if (errstr) {
 1113                     warnx("debug level %s", errstr);
 1114                     debug = 0;
 1115                 }
 1116                 break;
 1117 
 1118             case 'g':           /* Last-hop gateway (dest of query) */
 1119         gwy = host_addr(optarg);
 1120                 break;
 1121 
 1122             case 'l':           /* Loop updating stats indefinitely */
 1123         numstats = 3153600;
 1124         break;
 1125 
 1126             case 'm':           /* Max number of hops to trace */
 1127                 qno = strtonum(optarg, 1, MAXHOPS, &errstr);
 1128                 if (errstr) {
 1129                     warnx("max hops %s", errstr);
 1130                     qno = 0;
 1131                 }
 1132                 break;
 1133 
 1134             case 'M':           /* Use multicast for response */
 1135         multicast = TRUE;
 1136         break;
 1137 
 1138             case 'n':           /* Don't reverse map host addresses */
 1139         numeric = TRUE;
 1140         break;
 1141 
 1142             case 'p':           /* Passive listen for traces */
 1143         passive = TRUE;
 1144         break;
 1145 
 1146             case 'q':           /* Number of query retries */
 1147                 nqueries = strtonum(optarg, 1, 65535, &errstr);
 1148                 if (errstr) {
 1149                     warnx("query retries %s", errstr);
 1150                     qno = 0;
 1151                 }
 1152                 break;
 1153 
 1154             case 'r':           /* Dest for response packet */
 1155         raddr = host_addr(optarg);
 1156                 break;
 1157 
 1158             case 's':           /* Short form, don't wait for stats */
 1159         numstats = 0;
 1160         break;
 1161 
 1162             case 'S':           /* Stat accumulation interval */
 1163                 statint = strtonum(optarg, 1, 65535, &errstr);
 1164                 if (errstr) {
 1165                     warnx("stat accumulation interval %s", errstr);
 1166                     statint = 10;
 1167                 }
 1168                 break;
 1169 
 1170             case 't':           /* TTL for query packet */
 1171                 qttl = strtonum(optarg, 1, 32767, &errstr);
 1172                 if (errstr) {
 1173                     warnx("TTL for query packet %s", errstr);
 1174                     qttl = 0;
 1175                 }
 1176                 rttl = qttl;
 1177                 break;
 1178 
 1179             case 'v':           /* Verbosity */
 1180         verbose = TRUE;
 1181         break;
 1182 
 1183             case 'w':           /* Time to wait for packet arrival */
 1184                 timeout = strtonum(optarg, 1, 65535, &errstr);
 1185                 if (errstr) {
 1186                     warnx("TTL for query packet %s", errstr);
 1187                     timeout = DEFAULT_TIMEOUT;
 1188                 }
 1189                 break;
 1190 
 1191             case 'i':           /* Local interface address */
 1192         lcl_addr = host_addr(optarg);
 1193                 break;
 1194 
 1195             default:
 1196         usage();
 1197         }
 1198     }
 1199     argc -= optind;
 1200     argv += optind;
 1201 
 1202     if (geteuid() != 0) {
 1203         fprintf(stderr, "mtrace: must be root\n");
 1204         exit(1);
 1205     }
 1206 
 1207     init_igmp();
 1208 
 1209     uid = getuid();
 1210     if (setuid(uid) == -1)
 1211     err(1, "setuid");
 1212 
 1213     if (argc > 0 && (qsrc = host_addr(argv[0]))) {          /* Source of path */
 1214     if (IN_MULTICAST(ntohl(qsrc))) usage();
 1215     argv++, argc--;
 1216     if (argc > 0 && (qdst = host_addr(argv[0]))) {      /* Dest of path */
 1217         argv++, argc--;
 1218         if (argc > 0 && (qgrp = host_addr(argv[0]))) {  /* Path via group */
 1219         argv++, argc--;
 1220         }
 1221         if (IN_MULTICAST(ntohl(qdst))) {
 1222         uint32_t temp = qdst;
 1223         qdst = qgrp;
 1224         qgrp = temp;
 1225         if (IN_MULTICAST(ntohl(qdst))) usage();
 1226         } else if (qgrp && !IN_MULTICAST(ntohl(qgrp))) usage();
 1227     }
 1228     }
 1229 
 1230     if (passive) {
 1231     passive_mode();
 1232     return(0);
 1233     }
 1234 
 1235     if (argc > 0 || qsrc == 0) {
 1236         usage();
 1237     }
 1238 
 1239     /*
 1240      * Set useful defaults for as many parameters as possible.
 1241      */
 1242 
 1243     defgrp = htonl(0xE0020001);     /* MBone Audio (224.2.0.1) */
 1244     query_cast = htonl(0xE0000002); /* All routers multicast addr */
 1245     resp_cast = htonl(0xE0000120);  /* Mtrace response multicast addr */
 1246     if (qgrp == 0) qgrp = defgrp;
 1247 
 1248     /*
 1249      * Get default local address for multicasts to use in setting defaults.
 1250      */
 1251     memset(&addr, 0, sizeof addr);
 1252     addr.sin_family = AF_INET;
 1253 #ifdef HAVE_SA_LEN
 1254     addr.sin_len = sizeof(addr);
 1255 #endif
 1256     addr.sin_addr.s_addr = qgrp;
 1257     addr.sin_port = htons(2000);    /* Any port above 1024 will do */
 1258 
 1259     if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ||
 1260     (connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) ||
 1261     getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
 1262     perror("Determining local address");
 1263     exit(1);
 1264     }
 1265 
 1266 #ifdef SUNOS5
 1267     /*
 1268      * SunOS 5.X prior to SunOS 2.6, getsockname returns 0 for udp socket.
 1269      * This call to sysinfo will return the hostname.
 1270      * If the default multicast interface (set with the route
 1271      * for 224.0.0.0) is not the same as the hostname,
 1272      * mtrace -i [if_addr] will have to be used.
 1273      */
 1274     if (addr.sin_addr.s_addr == 0) {
 1275     char myhostname[MAXHOSTNAMELEN];
 1276     struct hostent *hp;
 1277     int error;
 1278     
 1279     error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
 1280     if (error == -1) {
 1281         perror("Getting my hostname");
 1282         exit(1);
 1283     }
 1284 
 1285     hp = gethostbyname(myhostname);
 1286     if (hp == NULL || hp->h_addrtype != AF_INET ||
 1287         hp->h_length != sizeof(addr.sin_addr)) {
 1288         perror("Finding IP address for my hostname");
 1289         exit(1);
 1290     }
 1291 
 1292     memcpy((char *)&addr.sin_addr.s_addr, hp->h_addr, hp->h_length);
 1293     }
 1294 #endif
 1295 
 1296     /*
 1297      * Default destination for path to be queried is the local host.
 1298      */
 1299     if (qdst == 0) qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
 1300     dst_netmask = get_netmask(udp, qdst);
 1301     close(udp);
 1302     if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr;
 1303 
 1304     /*
 1305      * Initialize the seed for random query identifiers.
 1306      */
 1307     gettimeofday(&tv, 0);
 1308     seed = tv.tv_usec ^ lcl_addr;
 1309     srand(seed);
 1310 
 1311     /*
 1312      * Protect against unicast queries to mrouted versions that might crash.
 1313      */
 1314     if (gwy && !IN_MULTICAST(ntohl(gwy)))
 1315     if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0])) {
 1316         int version = ntohl(incr[0].igmp.igmp_group.s_addr) & 0xFFFF;
 1317         if (version == 0x0303 || version == 0x0503) {
 1318         printf("Don't use -g to address an mrouted 3.%d, it might crash\n",
 1319                (version >> 8) & 0xFF);
 1320         exit(0);
 1321         }
 1322     }
 1323 
 1324     printf("Mtrace from %s to %s via group %s\n",
 1325        inet_fmt(qsrc, s1, sizeof(s1)), inet_fmt(qdst, s2, sizeof(s2)), inet_fmt(qgrp, s3, sizeof(s3)));
 1326 
 1327     if ((qdst & dst_netmask) == (qsrc & dst_netmask)) {
 1328     printf("Source & receiver are directly connected, no path to trace\n");
 1329     exit(0);
 1330     }
 1331 
 1332     /*
 1333      * If the response is to be a multicast address, make sure we 
 1334      * are listening on that multicast address.
 1335      */
 1336     if (raddr) {
 1337     if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr);
 1338     } else k_join(resp_cast, lcl_addr);
 1339 
 1340     /*
 1341      * If the destination is on the local net, the last-hop router can
 1342      * be found by multicast to the all-routers multicast group.
 1343      * Otherwise, use the group address that is the subject of the
 1344      * query since by definition the last-hop router will be a member.
 1345      * Set default TTLs for local remote multicasts.
 1346      */
 1347   restart:
 1348 
 1349     if (gwy == 0) {
 1350     if ((qdst & dst_netmask) == (lcl_addr & dst_netmask)) 
 1351         tdst = query_cast;
 1352     else
 1353         tdst = qgrp;
 1354     } else {
 1355         tdst = gwy;
 1356     }
 1357 
 1358     if (IN_MULTICAST(ntohl(tdst))) {
 1359     k_set_loop(1);  /* If I am running on a router, I need to hear this */
 1360     if (tdst == query_cast) k_set_ttl(qttl ? qttl : 1);
 1361     else k_set_ttl(qttl ? qttl : MULTICAST_TTL1);
 1362     }
 1363 
 1364     /*
 1365      * Try a query at the requested number of hops or MAXHOPS if unspecified.
 1366      */
 1367     if (qno == 0) {
 1368     hops = MAXHOPS;
 1369     tries = 1;
 1370     printf("Querying full reverse path... ");
 1371     fflush(stdout);
 1372     } else {
 1373     hops = qno;
 1374     tries = nqueries;
 1375     printf("Querying reverse path, maximum %d hops... ", qno);
 1376     fflush(stdout); 
 1377     }
 1378     base.rtime = 0;
 1379     base.len = 0;
 1380 
 1381     recvlen = send_recv(tdst, IGMP_MTRACE, hops, tries, &base);
 1382 
 1383     /*
 1384      * If the initial query was successful, print it.  Otherwise, if
 1385      * the query max hop count is the default of zero, loop starting
 1386      * from one until there is no response for four hops.  The extra
 1387      * hops allow getting past an mtrace-capable mrouter that can't
 1388      * send multicast packets because all phyints are disabled.
 1389      */
 1390     if (recvlen) {
 1391     printf("\n  0  ");
 1392     print_host(qdst);
 1393     printf("\n");
 1394     print_trace(1, &base);
 1395     r = base.resps + base.len - 1;
 1396     if (r->tr_rflags == TR_OLD_ROUTER || r->tr_rflags == TR_NO_SPACE ||
 1397         qno != 0) {
 1398         printf("%3d  ", -(base.len+1));
 1399         what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
 1400               "doesn't support mtrace"
 1401               : "is the next hop");
 1402     } else {
 1403         VAL_TO_MASK(smask, r->tr_smask);
 1404         if ((r->tr_inaddr & smask) == (qsrc & smask)) {
 1405         printf("%3d  ", -(base.len+1));
 1406         print_host(qsrc);
 1407         printf("\n");
 1408         }
 1409     }
 1410     } else if (qno == 0) {
 1411     printf("switching to hop-by-hop:\n  0  ");
 1412     print_host(qdst);
 1413     printf("\n");
 1414 
 1415     for (hops = 1, nexthop = 1; hops <= MAXHOPS; ++hops) {
 1416         printf("%3d  ", -hops);
 1417         fflush(stdout);
 1418 
 1419         /*
 1420          * After a successful first hop, try switching to the unicast
 1421          * address of the last-hop router instead of multicasting the
 1422          * trace query.  This should be safe for mrouted versions 3.3
 1423          * and 3.5 because there is a long route timeout with metric
 1424          * infinity before a route disappears.  Switching to unicast
 1425          * reduces the amount of multicast traffic and avoids a bug
 1426          * with duplicate suppression in mrouted 3.5.
 1427          */
 1428         if (hops == 2 && gwy == 0 &&
 1429         (recvlen = send_recv(lastout, IGMP_MTRACE, hops, 1, &base)))
 1430         tdst = lastout;
 1431         else recvlen = send_recv(tdst, IGMP_MTRACE, hops, nqueries, &base);
 1432 
 1433         if (recvlen == 0) {
 1434         if (hops == 1) break;
 1435         if (hops == nexthop) {
 1436             if (what_kind(&base, "didn't respond")) {
 1437             /* the ask_neighbors determined that the
 1438              * not-responding router is the first-hop. */
 1439             break;
 1440             }
 1441         } else if (hops < nexthop + 3) {
 1442             printf("\n");
 1443         } else {
 1444             printf("...giving up\n");
 1445             break;
 1446         }
 1447         continue;
 1448         }
 1449         r = base.resps + base.len - 1;
 1450         if (base.len == hops &&
 1451         (hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) {
 1452             if (hops == nexthop) {
 1453             print_trace(-hops, &base);
 1454         } else {
 1455             printf("\nResuming...\n");
 1456             print_trace(nexthop, &base);
 1457         }
 1458         } else {
 1459         if (base.len < hops) {
 1460             /*
 1461              * A shorter trace than requested means a fatal error
 1462              * occurred along the path, or that the route changed
 1463              * to a shorter one.
 1464              *
 1465              * If the trace is longer than the last one we received,
 1466              * then we are resuming from a skipped router (but there
 1467              * is still probably a problem).
 1468              *
 1469              * If the trace is shorter than the last one we
 1470              * received, then the route must have changed (and
 1471              * there is still probably a problem).
 1472              */
 1473             if (nexthop <= base.len) {
 1474             printf("\nResuming...\n");
 1475             print_trace(nexthop, &base);
 1476             } else if (nexthop > base.len + 1) {
 1477             hops = base.len;
 1478             printf("\nRoute must have changed...\n");
 1479             print_trace(1, &base);
 1480             }
 1481         } else {
 1482             /*
 1483              * The last hop address is not the same as it was;
 1484              * the route probably changed underneath us.
 1485              */
 1486             hops = base.len;
 1487             printf("\nRoute must have changed...\n");
 1488             print_trace(1, &base);
 1489         }
 1490         }
 1491         lastout = r->tr_outaddr;
 1492 
 1493         if (base.len < hops ||
 1494         r->tr_rmtaddr == 0 ||
 1495         (r->tr_rflags & 0x80)) {
 1496         VAL_TO_MASK(smask, r->tr_smask);
 1497         if (r->tr_rmtaddr) {
 1498             if (hops != nexthop) {
 1499             printf("\n%3d  ", -(base.len+1));
 1500             }
 1501             what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
 1502                   "doesn't support mtrace" :
 1503                   "would be the next hop");
 1504             /* XXX could do segmented trace if TR_NO_SPACE */
 1505         } else if (r->tr_rflags == TR_NO_ERR &&
 1506                (r->tr_inaddr & smask) == (qsrc & smask)) {
 1507             printf("%3d  ", -(hops + 1));
 1508             print_host(qsrc);
 1509             printf("\n");
 1510         }
 1511         break;
 1512         }
 1513 
 1514         nexthop = hops + 1;
 1515     }
 1516     }
 1517 
 1518     if (base.rtime == 0) {
 1519     printf("Timed out receiving responses\n");
 1520     if (IN_MULTICAST(ntohl(tdst))) {
 1521             if (tdst == query_cast)
 1522                 printf("Perhaps no local router has a route for source %s\n",
 1523                        inet_fmt(qsrc, s1, sizeof(s1)));
 1524             else
 1525                 printf("Perhaps receiver %s is not a member of group %s,\n"
 1526                "or no router local to it has a route for source %s,\n"
 1527                "or multicast at ttl %d doesn't reach its last-hop router for that source\n",
 1528                        inet_fmt(qdst, s2, sizeof(s2)), inet_fmt(qgrp, s3, sizeof(s3)), inet_fmt(qsrc, s1, sizeof(s1)),
 1529                        qttl ? qttl : MULTICAST_TTL1);
 1530         }
 1531     exit(1);
 1532     }
 1533 
 1534     printf("Round trip time %d ms\n\n", t_diff(base.rtime, base.qtime));
 1535 
 1536     /*
 1537      * Use the saved response which was the longest one received,
 1538      * and make additional probes after delay to measure loss.
 1539      */
 1540     raddr = base.qhdr.tr_raddr;
 1541     rttl = base.qhdr.tr_rttl;
 1542     gettimeofday(&tv, 0);
 1543     waittime = statint - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16));
 1544     prev = &base;
 1545     new = &incr[numstats&1];
 1546 
 1547     while (numstats--) {
 1548     if (waittime < 1) printf("\n");
 1549     else {
 1550         printf("Waiting to accumulate statistics... ");
 1551         fflush(stdout);
 1552         sleep((unsigned)waittime);
 1553     }
 1554     rno = base.len;
 1555     recvlen = send_recv(tdst, IGMP_MTRACE, rno, nqueries, new);
 1556 
 1557     if (recvlen == 0) {
 1558         printf("Timed out.\n");
 1559         exit(1);
 1560     }
 1561 
 1562     if (rno != new->len) {
 1563         printf("Trace length doesn't match:\n");
 1564         /*
 1565          * XXX Should this trace result be printed, or is that
 1566          * too verbose?  Perhaps it should just say restarting.
 1567          * But if the path is changing quickly, this may be the
 1568          * only snapshot of the current path.  But, if the path
 1569          * is changing that quickly, does the current path really
 1570          * matter?
 1571          */
 1572         print_trace(1, new);
 1573         printf("Restarting.\n\n");
 1574         numstats++;
 1575         goto restart;
 1576     }
 1577 
 1578     printf("Results after %d seconds:\n\n",
 1579            (int)((new->qtime - base.qtime) >> 16));
 1580     fixup_stats(&base, prev, new);
 1581     if (print_stats(&base, prev, new)) {
 1582         printf("Route changed:\n");
 1583         print_trace(1, new);
 1584         printf("Restarting.\n\n");
 1585         goto restart;
 1586     }
 1587     prev = new;
 1588     new = &incr[numstats&1];
 1589     waittime = statint;
 1590     }
 1591 
 1592     /*
 1593      * If the response was multicast back, leave the group
 1594      */
 1595     if (raddr) {
 1596     if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr);
 1597     } else k_leave(resp_cast, lcl_addr);
 1598 
 1599     return (0);
 1600 }
 1601 
 1602 void check_vif_state(void)
 1603 {
 1604     logit(LOG_WARNING, errno, "sendto");
 1605 }
 1606 
 1607 /*
 1608  * Log errors and other messages to stderr, according to the severity
 1609  * of the message and the current debug level.  For errors of severity
 1610  * LOG_ERR or worse, terminate the program.
 1611  */
 1612 void logit(int severity, int syserr, const char *format, ...)
 1613 {
 1614     va_list ap;
 1615 
 1616     switch (debug) {
 1617     case 0:
 1618         if (severity > LOG_WARNING)
 1619         return;
 1620     case 1:
 1621         if (severity > LOG_NOTICE)
 1622         return;
 1623     case 2:
 1624         if (severity > LOG_INFO)
 1625         return;
 1626     default:
 1627         if (severity == LOG_WARNING)
 1628         fprintf(stderr, "warning - ");
 1629         va_start(ap, format);
 1630         vfprintf(stderr, format, ap);
 1631         va_end(ap);
 1632         if (syserr == 0)
 1633         fprintf(stderr, "\n");
 1634         else
 1635         fprintf(stderr, ": %s\n", strerror(syserr));
 1636     }
 1637 
 1638     if (severity <= LOG_ERR)
 1639     exit(1);
 1640 }
 1641 
 1642 /* dummies */
 1643 void accept_probe(uint32_t UNUSED src, uint32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen, uint32_t UNUSED level)
 1644 {
 1645 }
 1646 
 1647 void accept_group_report(uint32_t UNUSED src, uint32_t UNUSED dst, uint32_t UNUSED group, int UNUSED r_type)
 1648 {
 1649 }
 1650 
 1651 void accept_neighbor_request2(uint32_t UNUSED src, uint32_t UNUSED dst)
 1652 {
 1653 }
 1654 
 1655 void accept_report(uint32_t UNUSED src, uint32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen, uint32_t UNUSED level)
 1656 {
 1657 }
 1658 
 1659 void accept_neighbor_request(uint32_t UNUSED src, uint32_t UNUSED dst)
 1660 {
 1661 }
 1662 
 1663 void accept_prune(uint32_t UNUSED src, uint32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen)
 1664 {
 1665 }
 1666 
 1667 void accept_graft(uint32_t UNUSED src, uint32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen)
 1668 {
 1669 }
 1670 
 1671 void accept_g_ack(uint32_t UNUSED src, uint32_t UNUSED dst, char UNUSED *p, size_t UNUSED datalen)
 1672 {
 1673 }
 1674 
 1675 void add_table_entry(uint32_t UNUSED origin, uint32_t UNUSED mcastgrp)
 1676 {
 1677 }
 1678 
 1679 void accept_leave_message(uint32_t UNUSED src, uint32_t UNUSED dst, uint32_t UNUSED group)
 1680 {
 1681 }
 1682 
 1683 void accept_mtrace(uint32_t UNUSED src, uint32_t UNUSED dst, uint32_t UNUSED group, char UNUSED *data, uint8_t UNUSED no, size_t UNUSED datalen)
 1684 {
 1685 }
 1686 
 1687 void accept_membership_query(uint32_t UNUSED src, uint32_t UNUSED dst, uint32_t UNUSED group, int UNUSED tmo)
 1688 {
 1689 }
 1690 
 1691 void accept_neighbors(uint32_t UNUSED src, uint32_t UNUSED dst, uint8_t UNUSED *p, size_t UNUSED datalen, uint32_t UNUSED level)
 1692 {
 1693 }
 1694 
 1695 void accept_neighbors2(uint32_t UNUSED src, uint32_t UNUSED dst, uint8_t UNUSED *p, size_t UNUSED datalen, uint32_t UNUSED level)
 1696 {
 1697 }
 1698 
 1699 void accept_info_request(uint32_t UNUSED src, uint32_t UNUSED dst, uint8_t UNUSED *p, size_t UNUSED datalen)
 1700 {
 1701 }
 1702 
 1703 void accept_info_reply(uint32_t UNUSED src, uint32_t UNUSED dst, uint8_t UNUSED *p, size_t UNUSED datalen)
 1704 {
 1705 }
 1706 
 1707 /**
 1708  * Local Variables:
 1709  *  version-control: t
 1710  *  indent-tabs-mode: t
 1711  *  c-file-style: "ellemtel"
 1712  *  c-basic-offset: 4
 1713  * End:
 1714  */