File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mrouted / mtrace.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:10:48 2012 UTC (12 years, 4 months ago) by misho
Branches: mrouted, MAIN
CVS tags: v3_9_6p0, v3_9_6, v3_9_5, HEAD
mrouted

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>