summaryrefslogtreecommitdiff
path: root/gnu
diff options
context:
space:
mode:
authoritojun <itojun@NetBSD.org>2002-10-01 03:50:07 +0000
committeritojun <itojun@NetBSD.org>2002-10-01 03:50:07 +0000
commitc3cbda0efb3b329d205eb2c08351e4ecb1c7ae42 (patch)
tree4f7eb5fad5747f7638560fff0688ac41df4277f8 /gnu
parentd0a9c54d7d09e2584ad11b5eceef59d07b717e3d (diff)
move mbone tools out of basesrc, as they use part of mrouted
Diffstat (limited to 'gnu')
-rw-r--r--gnu/dist/map-mbone/map-mbone.891
-rw-r--r--gnu/dist/map-mbone/mapper.c974
-rw-r--r--gnu/dist/mrinfo/mrinfo.885
-rw-r--r--gnu/dist/mrinfo/mrinfo.c600
-rw-r--r--gnu/dist/mtrace/mtrace.8516
-rw-r--r--gnu/dist/mtrace/mtrace.c1758
-rw-r--r--gnu/usr.sbin/Makefile4
-rw-r--r--gnu/usr.sbin/map-mbone/Makefile13
-rw-r--r--gnu/usr.sbin/mrinfo/Makefile15
-rw-r--r--gnu/usr.sbin/mtrace/Makefile16
10 files changed, 4070 insertions, 2 deletions
diff --git a/gnu/dist/map-mbone/map-mbone.8 b/gnu/dist/map-mbone/map-mbone.8
new file mode 100644
index 00000000000..51ced7d0827
--- /dev/null
+++ b/gnu/dist/map-mbone/map-mbone.8
@@ -0,0 +1,91 @@
+.\" $NetBSD: map-mbone.8,v 1.1 2002/10/01 03:50:08 itojun Exp $
+.\"
+.TH MAP-MBONE 8
+.UC 5
+.SH NAME
+map-mbone \- Multicast connection mapper
+.SH SYNOPSIS
+.B /usr/sbin/map-mbone
+[
+.B \-d
+.I debug_level
+] [
+.B \-f
+] [
+.B \-g
+] [
+.B \-n
+] [
+.B \-r
+.I retry_count
+] [
+.B \-t
+.I timeout_count
+] [
+.B starting_router
+]
+.SH DESCRIPTION
+.I map-mbone
+attempts to display all multicast routers that are reachable from the multicast
+.I starting_router.
+If not specified on the command line, the default multicast
+.I starting_router
+is the localhost.
+.PP
+.I map-mbone
+traverses neighboring multicast routers by sending the ASK_NEIGHBORS IGMP
+message to the multicast starting_router. If this multicast router responds,
+the version number and a list of their neighboring multicast router addresses is
+part of that response. If the responding router has recent multicast version
+number, then
+.I map-mbone
+requests additional information such as metrics, thresholds, and flags from the
+multicast router. For each new occurrence of neighboring multicast router in
+the reply and provided the flooding option has been selected, then
+.I map-mbone
+asks each of this multicast router for a list of neighbors. This search
+for unique routers will continue until no new neighboring multicast routers
+are reported.
+.br
+.ne 5
+.SH INVOCATION
+.PP
+"\-d" option sets the debug level. When the debug level is greater than the
+default value of 0, addition debugging messages are printed. Regardless of
+the debug level, an error condition, will always write an error message and will
+cause
+.I map-mbone
+to terminate.
+Non-zero debug levels have the following effects:
+.IP "level 1"
+packet warnings are printed to stderr.
+.IP "level 2"
+all level 1 messages plus notifications down networks are printed to stderr.
+.IP "level 3"
+all level 2 messages plus notifications of all packet
+timeouts are printed to stderr.
+.PP
+"\-f" option sets flooding option. Flooding allows the recursive search
+of neighboring multicast routers and is enable by default when starting_router
+is not used.
+.PP
+"\-g" option sets graphing in GraphEd format.
+.PP
+"\-n" option disables the DNS lookup for the multicast routers names.
+.PP
+"\-r retry_count" sets the neighbor query retry limit. Default is 1 retry.
+.PP
+"\-t timeout_count" sets the number of seconds to wait for a neighbor query
+reply before retrying. Default timeout is 2 seconds.
+.PP
+.SH IMPORTANT NOTE
+.I map-mbone
+must be run as root.
+.PP
+.SH SEE ALSO
+.BR mrouted (8) ,
+.BR mrinfo (8) ,
+.BR mtrace (8)
+.PP
+.SH AUTHOR
+Pavel Curtis
diff --git a/gnu/dist/map-mbone/mapper.c b/gnu/dist/map-mbone/mapper.c
new file mode 100644
index 00000000000..3e7314d49f5
--- /dev/null
+++ b/gnu/dist/map-mbone/mapper.c
@@ -0,0 +1,974 @@
+/* $NetBSD: mapper.c,v 1.1 2002/10/01 03:50:09 itojun Exp $ */
+
+/* Mapper for connections between MRouteD multicast routers.
+ * Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
+ */
+
+/*
+ * Copyright (c) 1992, 2001 Xerox Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither name of the Xerox, PARC, nor the names of its contributors may be used
+ * to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XEROX CORPORATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <poll.h>
+#include "defs.h"
+#include <arpa/inet.h>
+#include <stdarg.h>
+
+#define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 1 /* How many times to ask each router */
+
+
+/* All IP addresses are stored in the data structure in NET order. */
+
+typedef struct neighbor {
+ struct neighbor *next;
+ u_int32_t addr; /* IP address in NET order */
+ u_char metric; /* TTL cost of forwarding */
+ u_char threshold; /* TTL threshold to forward */
+ u_short flags; /* flags on connection */
+#define NF_PRESENT 0x8000 /* True if flags are meaningful */
+} Neighbor;
+
+typedef struct interface {
+ struct interface *next;
+ u_int32_t addr; /* IP address of the interface in NET order */
+ Neighbor *neighbors; /* List of neighbors' IP addresses */
+} Interface;
+
+typedef struct node {
+ u_int32_t addr; /* IP address of this entry in NET order */
+ u_int32_t version; /* which mrouted version is running */
+ int tries; /* How many requests sent? -1 for aliases */
+ union {
+ struct node *alias; /* If alias, to what? */
+ struct interface *interfaces; /* Else, neighbor data */
+ } u;
+ struct node *left, *right;
+} Node;
+
+
+Node *routers = 0;
+u_int32_t our_addr, target_addr = 0; /* in NET order */
+int debug = 0;
+int retries = DEFAULT_RETRIES;
+int timeout = DEFAULT_TIMEOUT;
+int show_names = TRUE;
+vifi_t numvifs; /* to keep loader happy */
+ /* (see COPY_TABLES macro called in kern.c) */
+
+Node * find_node(u_int32_t addr, Node **ptr);
+Interface * find_interface(u_int32_t addr, Node *node);
+Neighbor * find_neighbor(u_int32_t addr, Node *node);
+int main(int argc, char *argv[]);
+void ask(u_int32_t dst);
+void ask2(u_int32_t dst);
+int retry_requests(Node *node);
+char * inet_name(u_int32_t addr);
+void print_map(Node *node);
+char * graph_name(u_int32_t addr, char *buf);
+void graph_edges(Node *node);
+void elide_aliases(Node *node);
+void graph_map(void);
+int get_number(int *var, int deflt, char ***pargv,
+ int *pargc);
+u_int32_t host_addr(char *name);
+
+void log(int severity, int syserr, const char *format, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+
+Node *find_node(u_int32_t addr, Node **ptr)
+{
+ Node *n = *ptr;
+
+ if (!n) {
+ *ptr = n = (Node *) malloc(sizeof(Node));
+ n->addr = addr;
+ n->version = 0;
+ n->tries = 0;
+ n->u.interfaces = 0;
+ n->left = n->right = 0;
+ return n;
+ } else if (addr == n->addr)
+ return n;
+ else if (addr < n->addr)
+ return find_node(addr, &(n->left));
+ else
+ return find_node(addr, &(n->right));
+}
+
+
+Interface *find_interface(u_int32_t addr, Node *node)
+{
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+ if (ifc->addr == addr)
+ return ifc;
+
+ ifc = (Interface *) malloc(sizeof(Interface));
+ ifc->addr = addr;
+ ifc->next = node->u.interfaces;
+ node->u.interfaces = ifc;
+ ifc->neighbors = 0;
+
+ return ifc;
+}
+
+
+Neighbor *find_neighbor(u_int32_t addr, Node *node)
+{
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+
+ for (nb = ifc->neighbors; nb; nb = nb->next)
+ if (nb->addr == addr)
+ return nb;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level. For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+void
+log(int severity, int syserr, const char *format, ...)
+{
+ va_list ap;
+ char fmt[100];
+
+ switch (debug) {
+ case 0: if (severity > LOG_WARNING) return;
+ case 1: if (severity > LOG_NOTICE ) return;
+ case 2: if (severity > LOG_INFO ) return;
+ default:
+ fmt[0] = '\0';
+ if (severity == LOG_WARNING)
+ strcat(fmt, "warning - ");
+ strncat(fmt, format, 80);
+ format = fmt;
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ": %s\n", strerror(syserr));
+ }
+
+ if (severity <= LOG_ERR)
+ exit(1);
+}
+
+
+/*
+ * Send a neighbors-list request.
+ */
+void ask(u_int32_t dst)
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+void ask2(u_int32_t dst)
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group, int r_type)
+{
+ log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen,
+ u_int32_t level)
+{
+ log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming route report message.
+ */
+void accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
+ u_int32_t level)
+{
+ log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list request message.
+ */
+void accept_neighbor_request(u_int32_t src, u_int32_t dst)
+{
+ if (src != our_addr)
+ log(LOG_INFO, 0,
+ "ignoring spurious DVMRP neighbor request from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+void accept_neighbor_request2(u_int32_t src, u_int32_t dst)
+{
+ if (src != our_addr)
+ log(LOG_INFO, 0,
+ "ignoring spurious DVMRP neighbor request2 from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
+ u_int32_t level)
+{
+ Node *node = find_node(src, &routers);
+
+ if (node->tries == 0) /* Never heard of 'em; must have hit them at */
+ node->tries = 1; /* least once, though...*/
+ else if (node->tries == -1) /* follow alias link */
+ node = node->u.alias;
+
+#define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\
+ a += ((u_int32_t)*p++ << 8), a += *p++)
+
+ /* if node is running a recent mrouted, ask for additional info */
+ if (level != 0) {
+ node->version = level;
+ node->tries = 1;
+ ask2(src);
+ return;
+ }
+
+ if (debug > 3) {
+ int i;
+
+ fprintf(stderr, " datalen = %d\n", datalen);
+ for (i = 0; i < datalen; i++) {
+ if ((i & 0xF) == 0)
+ fprintf(stderr, " ");
+ fprintf(stderr, " %02x", p[i]);
+ if ((i & 0xF) == 0xF)
+ fprintf(stderr, "\n");
+ }
+ if ((datalen & 0xF) != 0xF)
+ fprintf(stderr, "\n");
+ }
+
+ while (datalen > 0) { /* loop through interfaces */
+ u_int32_t ifc_addr;
+ u_char metric, threshold, ncount;
+ Node *ifc_node;
+ Interface *ifc;
+ Neighbor *old_neighbors;
+
+ if (datalen < 4 + 3) {
+ log(LOG_WARNING, 0, "received truncated interface record from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ GET_ADDR(ifc_addr);
+ ifc_addr = htonl(ifc_addr);
+ metric = *p++;
+ threshold = *p++;
+ ncount = *p++;
+ datalen -= 4 + 3;
+
+ /* Fix up any alias information */
+ ifc_node = find_node(ifc_addr, &routers);
+ if (ifc_node->tries == 0) { /* new node */
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ } else if (ifc_node != node
+ && (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
+ /* must merge two hosts' nodes */
+ Interface *ifc_i, *next_ifc_i;
+
+ if (ifc_node->tries == -1) {
+ Node *tmp = ifc_node->u.alias;
+
+ ifc_node->u.alias = node;
+ ifc_node = tmp;
+ }
+
+ /* Merge ifc_node (foo_i) into node (foo_n) */
+
+ if (ifc_node->tries > node->tries)
+ node->tries = ifc_node->tries;
+
+ for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+ Neighbor *nb_i, *next_nb_i, *nb_n;
+ Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+ old_neighbors = ifc_n->neighbors;
+ for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+ next_nb_i = nb_i->next;
+ for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+ if (nb_i->addr == nb_n->addr) {
+ if (nb_i->metric != nb_n->metric
+ || nb_i->threshold != nb_n->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb_i->addr, s1),
+ inet_fmt(node->addr, s2));
+ free(nb_i);
+ break;
+ }
+ if (!nb_n) { /* no match for this neighbor yet */
+ nb_i->next = ifc_n->neighbors;
+ ifc_n->neighbors = nb_i;
+ }
+ }
+
+ next_ifc_i = ifc_i->next;
+ free(ifc_i);
+ }
+
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ }
+
+ ifc = find_interface(ifc_addr, node);
+ old_neighbors = ifc->neighbors;
+
+ /* Add the neighbors for this interface */
+ while (ncount--) {
+ u_int32_t neighbor;
+ Neighbor *nb;
+ Node *n_node;
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ GET_ADDR(neighbor);
+ neighbor = htonl(neighbor);
+ datalen -= 4;
+
+ for (nb = old_neighbors; nb; nb = nb->next)
+ if (nb->addr == neighbor) {
+ if (metric != nb->metric || threshold != nb->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+ goto next_neighbor;
+ }
+
+ nb = (Neighbor *) malloc(sizeof(Neighbor));
+ nb->next = ifc->neighbors;
+ ifc->neighbors = nb;
+ nb->addr = neighbor;
+ nb->metric = metric;
+ nb->threshold = threshold;
+ nb->flags = 0;
+
+ n_node = find_node(neighbor, &routers);
+ if (n_node->tries == 0 && !target_addr) { /* it's a new router */
+ ask(neighbor);
+ n_node->tries = 1;
+ }
+
+ next_neighbor: ;
+ }
+ }
+}
+
+void accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
+ u_int32_t level)
+{
+ Node *node = find_node(src, &routers);
+ u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
+ /* well, only possibly_broken_cisco, but that's too long to type. */
+
+ if (node->tries == 0) /* Never heard of 'em; must have hit them at */
+ node->tries = 1; /* least once, though...*/
+ else if (node->tries == -1) /* follow alias link */
+ node = node->u.alias;
+
+ while (datalen > 0) { /* loop through interfaces */
+ u_int32_t ifc_addr;
+ u_char metric, threshold, ncount, flags;
+ Node *ifc_node;
+ Interface *ifc;
+ Neighbor *old_neighbors;
+
+ if (datalen < 4 + 4) {
+ log(LOG_WARNING, 0, "received truncated interface record from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ ifc_addr = *(u_int32_t*)p;
+ p += 4;
+ metric = *p++;
+ threshold = *p++;
+ flags = *p++;
+ ncount = *p++;
+ datalen -= 4 + 4;
+
+ if (broken_cisco && ncount == 0) /* dumb Ciscos */
+ ncount = 1;
+ if (broken_cisco && ncount > 15) /* dumb Ciscos */
+ ncount = ncount & 0xf;
+
+ /* Fix up any alias information */
+ ifc_node = find_node(ifc_addr, &routers);
+ if (ifc_node->tries == 0) { /* new node */
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ } else if (ifc_node != node
+ && (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
+ /* must merge two hosts' nodes */
+ Interface *ifc_i, *next_ifc_i;
+
+ if (ifc_node->tries == -1) {
+ Node *tmp = ifc_node->u.alias;
+
+ ifc_node->u.alias = node;
+ ifc_node = tmp;
+ }
+
+ /* Merge ifc_node (foo_i) into node (foo_n) */
+
+ if (ifc_node->tries > node->tries)
+ node->tries = ifc_node->tries;
+
+ for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+ Neighbor *nb_i, *next_nb_i, *nb_n;
+ Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+ old_neighbors = ifc_n->neighbors;
+ for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+ next_nb_i = nb_i->next;
+ for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+ if (nb_i->addr == nb_n->addr) {
+ if (nb_i->metric != nb_n->metric
+ || nb_i->threshold != nb_i->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb_i->addr, s1),
+ inet_fmt(node->addr, s2));
+ free(nb_i);
+ break;
+ }
+ if (!nb_n) { /* no match for this neighbor yet */
+ nb_i->next = ifc_n->neighbors;
+ ifc_n->neighbors = nb_i;
+ }
+ }
+
+ next_ifc_i = ifc_i->next;
+ free(ifc_i);
+ }
+
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ }
+
+ ifc = find_interface(ifc_addr, node);
+ old_neighbors = ifc->neighbors;
+
+ /* Add the neighbors for this interface */
+ while (ncount-- && datalen > 0) {
+ u_int32_t neighbor;
+ Neighbor *nb;
+ Node *n_node;
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ neighbor = *(u_int32_t*)p;
+ p += 4;
+ datalen -= 4;
+ if (neighbor == 0)
+ /* make leaf nets point to themselves */
+ neighbor = ifc_addr;
+
+ for (nb = old_neighbors; nb; nb = nb->next)
+ if (nb->addr == neighbor) {
+ if (metric != nb->metric || threshold != nb->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+ goto next_neighbor;
+ }
+
+ nb = (Neighbor *) malloc(sizeof(Neighbor));
+ nb->next = ifc->neighbors;
+ ifc->neighbors = nb;
+ nb->addr = neighbor;
+ nb->metric = metric;
+ nb->threshold = threshold;
+ nb->flags = flags | NF_PRESENT;
+
+ n_node = find_node(neighbor, &routers);
+ if (n_node->tries == 0 && !target_addr) { /* it's a new router */
+ ask(neighbor);
+ n_node->tries = 1;
+ }
+
+ next_neighbor: ;
+ }
+ }
+}
+
+
+void check_vif_state(void)
+{
+ log(LOG_NOTICE, 0, "network marked down...");
+}
+
+
+int retry_requests(Node *node)
+{
+ int result;
+
+ if (node) {
+ result = retry_requests(node->left);
+ if (node->tries > 0 && node->tries < retries) {
+ if (node->version)
+ ask2(node->addr);
+ else
+ ask(node->addr);
+ node->tries++;
+ result = 1;
+ }
+ return retry_requests(node->right) || result;
+ } else
+ return 0;
+}
+
+
+char *inet_name(u_int32_t addr)
+{
+ struct hostent *e;
+
+ e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+ return e ? e->h_name : 0;
+}
+
+
+void print_map(Node *node)
+{
+ if (node) {
+ char *name, *addr;
+
+ print_map(node->left);
+
+ addr = inet_fmt(node->addr, s1);
+ if (!target_addr
+ || (node->tries >= 0 && node->u.interfaces)
+ || (node->tries == -1
+ && node->u.alias->tries >= 0
+ && node->u.alias->u.interfaces)) {
+ if (show_names && (name = inet_name(node->addr)))
+ printf("%s (%s):", addr, name);
+ else
+ printf("%s:", addr);
+ if (node->tries < 0)
+ printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1));
+ else if (!node->u.interfaces)
+ printf(" no response to query\n\n");
+ else {
+ Interface *ifc;
+
+ if (node->version)
+ printf(" <v%d.%d>", node->version & 0xff,
+ (node->version >> 8) & 0xff);
+ printf("\n");
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+ char *ifc_name = inet_fmt(ifc->addr, s1);
+ int ifc_len = strlen(ifc_name);
+ int count = 0;
+
+ printf(" %s:", ifc_name);
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ if (count > 0)
+ printf("%*s", ifc_len + 5, "");
+ printf(" %s", inet_fmt(nb->addr, s1));
+ if (show_names && (name = inet_name(nb->addr)))
+ printf(" (%s)", name);
+ printf(" [%d/%d", nb->metric, nb->threshold);
+ if (nb->flags) {
+ u_short flags = nb->flags;
+ if (flags & DVMRP_NF_TUNNEL)
+ printf("/tunnel");
+ if (flags & DVMRP_NF_SRCRT)
+ printf("/srcrt");
+ if (flags & DVMRP_NF_QUERIER)
+ printf("/querier");
+ if (flags & DVMRP_NF_DISABLED)
+ printf("/disabled");
+ if (flags & DVMRP_NF_DOWN)
+ printf("/down");
+ }
+ printf("]\n");
+ count++;
+ }
+ }
+ printf("\n");
+ }
+ }
+ print_map(node->right);
+ }
+}
+
+
+char *graph_name(u_int32_t addr, char *buf)
+{
+ char *name;
+
+ if (show_names && (name = inet_name(addr)))
+ strcpy(buf, name);
+ else
+ inet_fmt(addr, buf);
+
+ return buf;
+}
+
+
+void graph_edges(Node *node)
+{
+ Interface *ifc;
+ Neighbor *nb;
+ char name[100];
+
+ if (node) {
+ graph_edges(node->left);
+ if (node->tries >= 0) {
+ printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n",
+ (int) node->addr,
+ node->addr & 0xFF, (node->addr >> 8) & 0xFF,
+ graph_name(node->addr, name),
+ node->u.interfaces ? "" : "*");
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ Node *nb_node = find_node(nb->addr, &routers);
+ Neighbor *nb2;
+
+ if (nb_node->tries < 0)
+ nb_node = nb_node->u.alias;
+
+ if (node != nb_node &&
+ (!(nb2 = find_neighbor(node->addr, nb_node))
+ || node->addr < nb_node->addr)) {
+ printf(" %d \"%d/%d",
+ nb_node->addr, nb->metric, nb->threshold);
+ if (nb2 && (nb2->metric != nb->metric
+ || nb2->threshold != nb->threshold))
+ printf(",%d/%d", nb2->metric, nb2->threshold);
+ if (nb->flags & NF_PRESENT)
+ printf("%s%s",
+ nb->flags & DVMRP_NF_SRCRT ? "" :
+ nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
+ nb->flags & DVMRP_NF_DOWN ? "D" : "");
+ printf("\"\n");
+ }
+ }
+ printf(" ;\n");
+ }
+ graph_edges(node->right);
+ }
+}
+
+void elide_aliases(Node *node)
+{
+ if (node) {
+ elide_aliases(node->left);
+ if (node->tries >= 0) {
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ Node *nb_node = find_node(nb->addr, &routers);
+
+ if (nb_node->tries < 0)
+ nb->addr = nb_node->u.alias->addr;
+ }
+ }
+ }
+ elide_aliases(node->right);
+ }
+}
+
+void graph_map(void)
+{
+ time_t now = time(0);
+ char *nowstr = ctime(&now);
+
+ nowstr[24] = '\0'; /* Kill the newline at the end */
+ elide_aliases(routers);
+ printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
+ nowstr);
+ graph_edges(routers);
+ printf("END\n");
+}
+
+
+int get_number(int *var, int deflt, char ***pargv, int *pargc)
+{
+ if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
+ if (*pargc > 1 && isdigit((*pargv)[1][0])) {
+ (*pargv)++, (*pargc)--;
+ *var = atoi((*pargv)[0]);
+ return 1;
+ } else if (deflt >= 0) {
+ *var = deflt;
+ return 1;
+ } else
+ return 0;
+ } else { /* Get value from the rest of this argument */
+ if (isdigit((*pargv)[0][2])) {
+ *var = atoi((*pargv)[0] + 2);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+
+u_int32_t host_addr(char *name)
+{
+ struct hostent *e = gethostbyname(name);
+ int addr;
+
+ if (e)
+ memcpy(&addr, e->h_addr_list[0], e->h_length);
+ else {
+ addr = inet_addr(name);
+ if (addr == -1)
+ addr = 0;
+ }
+
+ return addr;
+}
+
+
+int main(int argc, char **argv)
+{
+ int flood = FALSE, graph = FALSE;
+ struct pollfd set[1];
+
+ setlinebuf(stderr);
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "must be root\n");
+ exit(1);
+ }
+
+ argv++, argc--;
+ while (argc > 0 && argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'd':
+ if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+ goto usage;
+ break;
+ case 'f':
+ flood = TRUE;
+ break;
+ case 'g':
+ graph = TRUE;
+ break;
+ case 'n':
+ show_names = FALSE;
+ break;
+ case 'r':
+ if (!get_number(&retries, -1, &argv, &argc))
+ goto usage;
+ break;
+ case 't':
+ if (!get_number(&timeout, -1, &argv, &argc))
+ goto usage;
+ break;
+ default:
+ goto usage;
+ }
+ argv++, argc--;
+ }
+
+ if (argc > 1) {
+ usage:
+ fprintf(stderr,
+ "Usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n",
+ "[-r retries] [-d [debug-level]] [router]");
+ fprintf(stderr, "\t-f Flood the routing graph with queries\n");
+ fprintf(stderr, "\t (True by default unless `router' is given)\n");
+ fprintf(stderr, "\t-g Generate output in GraphEd format\n");
+ fprintf(stderr, "\t-n Don't look up DNS names for routers\n");
+ exit(1);
+ } else if (argc == 1 && !(target_addr = host_addr(argv[0]))) {
+ fprintf(stderr, "Unknown host: %s\n", argv[0]);
+ exit(2);
+ }
+
+ if (debug)
+ fprintf(stderr, "Debug level %u\n", debug);
+
+ init_igmp();
+
+ { /* Find a good local address for us. */
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ addr.sin_len = sizeof addr;
+#endif
+ addr.sin_addr.s_addr = dvmrp_group;
+ addr.sin_port = htons(2000); /* any port over 1024 will do... */
+ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+ || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+ || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
+ perror("Determining local address");
+ exit(1);
+ }
+ close(udp);
+ our_addr = addr.sin_addr.s_addr;
+ }
+
+ /* Send initial seed message to all local routers */
+ ask(target_addr ? target_addr : allhosts_group);
+
+ if (target_addr) {
+ Node *n = find_node(target_addr, &routers);
+
+ n->tries = 1;
+
+ if (flood)
+ target_addr = 0;
+ }
+
+ /* Main receive loop */
+ set[0].fd = igmp_socket;
+ set[0].events = POLLIN;
+ for(;;) {
+ int count, recvlen, dummy = 0;
+
+ count = poll(set, 1, timeout * 1000);
+
+ if (count < 0) {
+ if (errno != EINTR)
+ perror("poll");
+ continue;
+ } else if (count == 0) {
+ log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+ if (retry_requests(routers))
+ continue;
+ else
+ break;
+ }
+
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ if (recvlen >= 0)
+ accept_igmp(recvlen);
+ else if (errno != EINTR)
+ perror("recvfrom");
+ }
+
+ printf("\n");
+
+ if (graph)
+ graph_map();
+ else {
+ if (!target_addr)
+ printf("Multicast Router Connectivity:\n\n");
+ print_map(routers);
+ }
+
+ exit(0);
+}
+
+/* dummies */
+void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
+{
+}
+void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
+{
+}
+void accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
+ u_int no, int datalen)
+{
+}
+void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group,
+ int tmo)
+{
+}
+void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
+{
+}
+void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
+{
+}
diff --git a/gnu/dist/mrinfo/mrinfo.8 b/gnu/dist/mrinfo/mrinfo.8
new file mode 100644
index 00000000000..dd1fb13e986
--- /dev/null
+++ b/gnu/dist/mrinfo/mrinfo.8
@@ -0,0 +1,85 @@
+.\" $NetBSD: mrinfo.8,v 1.1 2002/10/01 03:50:09 itojun Exp $
+.\"
+.TH MRINFO 8
+.UC 5
+.SH NAME
+mrinfo \- Displays configuration info from a multicast router
+.SH SYNOPSIS
+.B /usr/sbin/mrinfo
+[
+.B \-d
+.I debug_level
+] [
+.B \-r
+.I retry_count
+] [
+.B \-t
+.I timeout_count
+]
+.B multicast_router
+
+.SH DESCRIPTION
+.I mrinfo
+attempts to display the configuration information from the multicast router
+.I multicast_router.
+.PP
+.I mrinfo
+uses the ASK_NEIGHBORS IGMP message to the specified multicast router. If this
+multicast router responds, the version number and a list of their neighboring
+multicast router addresses is part of that response. If the responding router
+has a recent multicast version number, then
+.I mrinfo
+requests additional information such as metrics, thresholds, and flags from the
+multicast router. Once the specified multicast router responds, the
+configuration is displayed to the standard output.
+.br
+.ne 5
+.SH INVOCATION
+.PP
+"\-d" option sets the debug level. When the debug level is greater than the
+default value of 0, addition debugging messages are printed. Regardless of
+the debug level, an error condition, will always write an error message and will
+cause
+.I mrinfo
+to terminate.
+Non-zero debug levels have the following effects:
+.IP "level 1"
+packet warnings are printed to stderr.
+.IP "level 2"
+all level 1 messages plus notifications down networks are printed to stderr.
+.IP "level 3"
+all level 2 messages plus notifications of all packet
+timeouts are printed to stderr.
+.PP
+"\-r retry_count" sets the neighbor query retry limit. Default is 3 retry.
+.PP
+"\-t timeout_count" sets the number of seconds to wait for a neighbor query
+reply. Default timeout is 4 seconds.
+.PP
+.SH SAMPLE OUTPUT
+.nf
+.I mrinfo mbone.phony.dom.net
+127.148.176.10 (mbone.phony.dom.net) [version 3.3]:
+ 127.148.176.10 -\*[Gt] 0.0.0.0 (?) [1/1/querier]
+ 127.148.176.10 -\*[Gt] 127.0.8.4 (mbone2.phony.dom.net) [1/45/tunnel]
+ 127.148.176.10 -\*[Gt] 105.1.41.9 (momoney.com) [1/32/tunnel/down]
+ 127.148.176.10 -\*[Gt] 143.192.152.119 (mbone.dipu.edu) [1/32/tunnel]
+.fi
+.PP
+For each neighbor of the queried multicast router, the IP of the queried router
+is displayed, followed by the IP and name of the neighbor. In square brackets
+the metric (cost of connection), the treashold (multicast ttl) is displayed. If
+the queried multicast router has a newer version number, the type (tunnel,
+srcrt) and status (disabled, down) of the connection is displayed.
+.PP
+.SH IMPORTANT NOTE
+.I mrinfo
+must be run as root.
+.PP
+.SH SEE ALSO
+.BR mrouted (8) ,
+.BR map-mbone (8) ,
+.BR mtrace (8)
+.PP
+.SH AUTHOR
+Van Jacobson
diff --git a/gnu/dist/mrinfo/mrinfo.c b/gnu/dist/mrinfo/mrinfo.c
new file mode 100644
index 00000000000..eb157af5568
--- /dev/null
+++ b/gnu/dist/mrinfo/mrinfo.c
@@ -0,0 +1,600 @@
+/* $NetBSD: mrinfo.c,v 1.1 2002/10/01 03:50:10 itojun Exp $ */
+
+/*
+ * This tool requests configuration info from a multicast router
+ * and prints the reply (if any). Invoke it as:
+ *
+ * mrinfo router-name-or-address
+ *
+ * Written Wed Mar 24 1993 by Van Jacobson (adapted from the
+ * multicast mapper written by Pavel Curtis).
+ *
+ * The lawyers insist we include the following UC copyright notice.
+ * The mapper from which this is derived contained a Xerox copyright
+ * notice which follows the UC one. Try not to get depressed noting
+ * that the legal gibberish is larger than the program.
+ *
+ * Copyright (c) 1993 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ---------------------------------
+ * Copyright (c) 1992, 2001 Xerox Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+
+ * Neither name of the Xerox, PARC, nor the names of its contributors may be used
+ * to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XEROX CORPORATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char rcsid[] =
+ "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
+#else
+__RCSID("$NetBSD: mrinfo.c,v 1.1 2002/10/01 03:50:10 itojun Exp $");
+#endif
+#endif
+
+#include <string.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <poll.h>
+#include "defs.h"
+#include <arpa/inet.h>
+#include <stdarg.h>
+
+#define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 3 /* How many times to ask each router */
+
+u_int32_t our_addr, target_addr = 0; /* in NET order */
+int debug = 0;
+int nflag = 0;
+int retries = DEFAULT_RETRIES;
+int timeout = DEFAULT_TIMEOUT;
+int target_level = 0;
+vifi_t numvifs; /* to keep loader happy */
+ /* (see COPY_TABLES macro called in kern.c) */
+
+char * inet_name(u_int32_t addr);
+void ask(u_int32_t dst);
+void ask2(u_int32_t dst);
+int get_number(int *var, int deflt, char ***pargv,
+ int *pargc);
+u_int32_t host_addr(char *name);
+void usage(void);
+
+/* to shut up -Wstrict-prototypes */
+int main(int argc, char *argv[]);
+/* log() prototyped in defs.h */
+
+
+char *
+inet_name(u_int32_t addr)
+{
+ struct hostent *e;
+ struct in_addr in;
+
+ if (addr == 0)
+ return "local";
+
+ if (nflag ||
+ (e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) {
+ in.s_addr = addr;
+ return (inet_ntoa(in));
+ }
+ return (e->h_name);
+}
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level. For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+void
+log(int severity, int syserr, const char *format, ...)
+{
+ va_list ap;
+
+ switch (debug) {
+ case 0:
+ if (severity > LOG_WARNING)
+ return;
+ case 1:
+ if (severity > LOG_NOTICE)
+ return;
+ case 2:
+ if (severity > LOG_INFO)
+ return;
+ default:
+ if (severity == LOG_WARNING)
+ fprintf(stderr, "warning - ");
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ": %s\n", strerror(syserr));
+ }
+
+ if (severity <= LOG_ERR)
+ exit(1);
+}
+
+/*
+ * Send a neighbors-list request.
+ */
+void
+ask(u_int32_t dst)
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+void
+ask2(u_int32_t dst)
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void
+accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
+ u_int32_t level)
+{
+ u_char *ep = p + datalen;
+#define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\
+ a += ((u_int32_t)*p++ << 8), a += *p++)
+
+ printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
+ while (p < ep) {
+ u_int32_t laddr;
+ u_char metric;
+ u_char thresh;
+ int ncount;
+
+ GET_ADDR(laddr);
+ laddr = htonl(laddr);
+ metric = *p++;
+ thresh = *p++;
+ ncount = *p++;
+ while (--ncount >= 0) {
+ u_int32_t neighbor;
+ GET_ADDR(neighbor);
+ neighbor = htonl(neighbor);
+ printf(" %s -> ", inet_fmt(laddr, s1));
+ printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
+ inet_name(neighbor), metric, thresh);
+ }
+ }
+}
+
+void
+accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
+ u_int32_t level)
+{
+ u_char *ep = p + datalen;
+ u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
+ /* well, only possibly_broken_cisco, but that's too long to type. */
+
+ printf("%s (%s) [version %d.%d", inet_fmt(src, s1), inet_name(src),
+ level & 0xff, (level >> 8) & 0xff);
+ if ((level >> 16) & NF_LEAF) { printf (",leaf"); }
+ if ((level >> 16) & NF_PRUNE) { printf (",prune"); }
+ if ((level >> 16) & NF_GENID) { printf (",genid"); }
+ if ((level >> 16) & NF_MTRACE) { printf (",mtrace"); }
+ printf ("]:\n");
+
+ while (p < ep) {
+ u_char metric;
+ u_char thresh;
+ u_char flags;
+ int ncount;
+ u_int32_t laddr = *(u_int32_t*)p;
+
+ p += 4;
+ metric = *p++;
+ thresh = *p++;
+ flags = *p++;
+ ncount = *p++;
+ if (broken_cisco && ncount == 0) /* dumb Ciscos */
+ ncount = 1;
+ if (broken_cisco && ncount > 15) /* dumb Ciscos */
+ ncount = ncount & 0xf;
+ while (--ncount >= 0 && p < ep) {
+ u_int32_t neighbor = *(u_int32_t*)p;
+ p += 4;
+ printf(" %s -> ", inet_fmt(laddr, s1));
+ printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
+ inet_name(neighbor), metric, thresh);
+ if (flags & DVMRP_NF_TUNNEL)
+ printf("/tunnel");
+ if (flags & DVMRP_NF_SRCRT)
+ printf("/srcrt");
+ if (flags & DVMRP_NF_PIM)
+ printf("/pim");
+ if (flags & DVMRP_NF_QUERIER)
+ printf("/querier");
+ if (flags & DVMRP_NF_DISABLED)
+ printf("/disabled");
+ if (flags & DVMRP_NF_DOWN)
+ printf("/down");
+ if (flags & DVMRP_NF_LEAF)
+ printf("/leaf");
+ printf("]\n");
+ }
+ }
+}
+
+int
+get_number(int *var, int deflt, char ***pargv, int *pargc)
+{
+ if ((*pargv)[0][2] == '\0') { /* Get the value from the next
+ * argument */
+ if (*pargc > 1 && isdigit((*pargv)[1][0])) {
+ (*pargv)++, (*pargc)--;
+ *var = atoi((*pargv)[0]);
+ return 1;
+ } else if (deflt >= 0) {
+ *var = deflt;
+ return 1;
+ } else
+ return 0;
+ } else { /* Get value from the rest of this argument */
+ if (isdigit((*pargv)[0][2])) {
+ *var = atoi((*pargv)[0] + 2);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: mrinfo [-n] [-t timeout] [-r retries] [router]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int tries;
+ int trynew;
+ struct timeval et;
+ struct hostent *hp;
+ struct hostent bogus;
+ char *host;
+ int curaddr;
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "mrinfo: must be root\n");
+ exit(1);
+ }
+ init_igmp();
+ if (setuid(getuid()) == -1)
+ log(LOG_ERR, errno, "setuid");
+
+ setlinebuf(stderr);
+
+ argv++, argc--;
+ while (argc > 0 && argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'd':
+ if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+ usage();
+ break;
+ case 'n':
+ ++nflag;
+ break;
+ case 'r':
+ if (!get_number(&retries, -1, &argv, &argc))
+ usage();
+ break;
+ case 't':
+ if (!get_number(&timeout, -1, &argv, &argc))
+ usage();
+ break;
+ default:
+ usage();
+ }
+ argv++, argc--;
+ }
+ if (argc > 1)
+ usage();
+ if (argc == 1)
+ host = argv[0];
+ else
+ host = "127.0.0.1";
+
+ if ((target_addr = inet_addr(host)) != -1) {
+ hp = &bogus;
+ hp->h_length = sizeof(target_addr);
+ hp->h_addr_list = (char **)malloc(2 * sizeof(char *));
+ if (hp->h_addr_list == NULL)
+ log(LOG_ERR, errno, "malloc");
+ hp->h_addr_list[0] = malloc(hp->h_length);
+ if (hp->h_addr_list[0] == NULL)
+ log(LOG_ERR, errno, "malloc");
+ memcpy(hp->h_addr_list[0], &target_addr, sizeof(hp->h_addr_list[0]));
+ hp->h_addr_list[1] = NULL;
+ } else
+ hp = gethostbyname(host);
+
+ if (hp == NULL || hp->h_length != sizeof(target_addr)) {
+ fprintf(stderr, "mrinfo: %s: no such host\n", argv[0]);
+ exit(1);
+ }
+ if (debug)
+ fprintf(stderr, "Debug level %u\n", debug);
+
+ /* Check all addresses; mrouters often have unreachable interfaces */
+ for (curaddr = 0; hp->h_addr_list[curaddr] != NULL; curaddr++) {
+ memcpy(&target_addr, hp->h_addr_list[curaddr], sizeof(target_addr));
+ { /* Find a good local address for us. */
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ addr.sin_len = sizeof addr;
+#endif
+ addr.sin_addr.s_addr = target_addr;
+ addr.sin_port = htons(2000); /* any port over 1024 will
+ * do... */
+ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+ || connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0
+ || getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0) {
+ perror("Determining local address");
+ exit(1);
+ }
+ close(udp);
+ our_addr = addr.sin_addr.s_addr;
+ }
+
+ tries = 0;
+ trynew = 1;
+ /*
+ * New strategy: send 'ask2' for two timeouts, then fall back
+ * to 'ask', since it's not very likely that we are going to
+ * find someone who only responds to 'ask' these days
+ */
+ ask2(target_addr);
+
+ gettimeofday(&et, 0);
+ et.tv_sec += timeout;
+
+ /* Main receive loop */
+ for (;;) {
+ struct pollfd set[1];
+ struct timeval tv, now;
+ int count, recvlen, dummy = 0;
+ u_int32_t src, dst, group;
+ struct ip *ip;
+ struct igmp *igmp;
+ int ipdatalen, iphdrlen, igmpdatalen;
+
+ set[0].fd = igmp_socket;
+ set[0].events = POLLIN;
+
+ gettimeofday(&now, 0);
+ tv.tv_sec = et.tv_sec - now.tv_sec;
+ tv.tv_usec = et.tv_usec - now.tv_usec;
+
+ if (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000L;
+ --tv.tv_sec;
+ }
+ if (tv.tv_sec < 0)
+ tv.tv_sec = tv.tv_usec = 0;
+
+ count = poll(set, 1, tv.tv_sec * 1000 + tv.tv_usec / 1000);
+
+ if (count < 0) {
+ if (errno != EINTR)
+ perror("select");
+ continue;
+ } else if (count == 0) {
+ log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+ if (++tries > retries)
+ break;
+ /* If we've tried ASK_NEIGHBORS2 twice with
+ * no response, fall back to ASK_NEIGHBORS
+ */
+ if (tries == 2 && target_level == 0)
+ trynew = 0;
+ if (target_level == 0 && trynew == 0)
+ ask(target_addr);
+ else
+ ask2(target_addr);
+ gettimeofday(&et, 0);
+ et.tv_sec += timeout;
+ continue;
+ }
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ if (recvlen <= 0) {
+ if (recvlen && errno != EINTR)
+ perror("recvfrom");
+ continue;
+ }
+
+ if (recvlen < sizeof(struct ip)) {
+ log(LOG_WARNING, 0,
+ "packet too short (%u bytes) for IP header",
+ recvlen);
+ continue;
+ }
+ ip = (struct ip *) recv_buf;
+ if (ip->ip_p == 0)
+ continue; /* Request to install cache entry */
+ src = ip->ip_src.s_addr;
+ dst = ip->ip_dst.s_addr;
+ iphdrlen = ip->ip_hl << 2;
+ ipdatalen = ip->ip_len;
+ if (iphdrlen + ipdatalen != recvlen) {
+ log(LOG_WARNING, 0,
+ "packet shorter (%u bytes) than hdr+data length (%u+%u)",
+ recvlen, iphdrlen, ipdatalen);
+ continue;
+ }
+ igmp = (struct igmp *) (recv_buf + iphdrlen);
+ group = igmp->igmp_group.s_addr;
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ log(LOG_WARNING, 0,
+ "IP data field too short (%u bytes) for IGMP, from %s",
+ ipdatalen, inet_fmt(src, s1));
+ continue;
+ }
+ if (igmp->igmp_type != IGMP_DVMRP)
+ continue;
+
+ switch (igmp->igmp_code) {
+ case DVMRP_NEIGHBORS:
+ case DVMRP_NEIGHBORS2:
+ if (src != target_addr) {
+ fprintf(stderr, "mrinfo: got reply from %s",
+ inet_fmt(src, s1));
+ fprintf(stderr, " instead of %s\n",
+ inet_fmt(target_addr, s1));
+ /*continue;*/
+ }
+ break;
+ default:
+ continue; /* ignore all other DVMRP messages */
+ }
+
+ switch (igmp->igmp_code) {
+
+ case DVMRP_NEIGHBORS:
+ if (group) {
+ /* knows about DVMRP_NEIGHBORS2 msg */
+ if (target_level == 0) {
+ target_level = ntohl(group);
+ ask2(target_addr);
+ }
+ } else {
+ accept_neighbors(src, dst, (u_char *)(igmp + 1),
+ igmpdatalen, ntohl(group));
+ exit(0);
+ }
+ break;
+
+ case DVMRP_NEIGHBORS2:
+ accept_neighbors2(src, dst, (u_char *)(igmp + 1),
+ igmpdatalen, ntohl(group));
+ exit(0);
+ }
+ }
+ }
+ exit(1);
+}
+
+/* dummies */
+void accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen,
+ u_int32_t level)
+{
+}
+void accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group,
+ int r_type)
+{
+}
+void accept_neighbor_request2(u_int32_t src, u_int32_t dst)
+{
+}
+void accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
+ u_int32_t level)
+{
+}
+void accept_neighbor_request(u_int32_t src, u_int32_t dst)
+{
+}
+void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
+{
+}
+void check_vif_state(void)
+{
+}
+void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
+{
+}
+void accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
+ u_int no, int datalen)
+{
+}
+void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group,
+ int tmo)
+{
+}
+void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
+{
+}
+void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
+{
+}
diff --git a/gnu/dist/mtrace/mtrace.8 b/gnu/dist/mtrace/mtrace.8
new file mode 100644
index 00000000000..bed052416d0
--- /dev/null
+++ b/gnu/dist/mtrace/mtrace.8
@@ -0,0 +1,516 @@
+.\" $NetBSD: mtrace.8,v 1.1 2002/10/01 03:50:10 itojun Exp $
+.\"
+.\" Copyright (c) 1995 by the University of Southern California
+.\" All rights reserved.
+.\"
+.\" Permission to use, copy, modify, and distribute this software and its
+.\" documentation in source and binary forms for non-commercial purposes
+.\" and without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both the copyright notice and
+.\" this permission notice appear in supporting documentation, and that
+.\" any documentation, advertising materials, and other materials related
+.\" to such distribution and use acknowledge that the software was
+.\" developed by the University of Southern California, Information
+.\" Sciences Institute. The name of the University may not be used to
+.\" endorse or promote products derived from this software without
+.\" specific prior written permission.
+.\"
+.\" THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+.\" the suitability of this software for any purpose. THIS SOFTWARE IS
+.\" PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" Other copyrights might apply to parts of this software and are so
+.\" noted when applicable.
+.\"
+.\" This manual page (but not the software) was derived from the
+.\" manual page for the traceroute program which bears the following
+.\" copyright notice:
+.\"
+.\" Copyright (c) 1988 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.TH MTRACE 8 "May 8, 1995"
+.UC 6
+.SH NAME
+mtrace \- print multicast path from a source to a receiver
+.SH SYNOPSIS
+.B mtrace
+[
+.B \-g
+.I gateway
+] [
+.B \-i
+.I if_addr
+] [
+.B \-l
+] [
+.B \-M
+] [
+.B \-m
+.I max_hops
+] [
+.B \-n
+] [
+.B \-p
+] [
+.B \-q
+.I nqueries
+] [
+.B \-r
+.I resp_dest
+] [
+.B \-s
+] [
+.B \-S
+.I stat_int
+] [
+.B \-t
+.I ttl
+] [
+.B \-v
+] [
+.B \-w
+.I waittime
+]
+.I source
+[
+.I receiver
+] [
+.I group
+]
+.SH DESCRIPTION
+Assessing problems in the distribution of IP multicast traffic
+can be difficult.
+.B mtrace
+utilizes a tracing feature implemented in multicast routers
+.RB ( mrouted
+version 3.3 and later) that is
+accessed via an extension to the IGMP protocol. A trace query is
+passed hop-by-hop along the reverse path from the
+.I receiver
+to the
+.IR source ,
+collecting hop addresses, packet counts, and routing error conditions
+along the path, and then the response is returned to the requestor.
+.PP
+The only required parameter is the
+.I source
+host name or address. The default
+.I receiver
+is the host running mtrace, and the default
+.I group
+is "MBone Audio" (224.2.0.1), which is sufficient if packet loss
+statistics for a particular multicast group are not needed. These two
+optional parameters may be specified to test the path to some other
+receiver in a particular group, subject to some constraints as
+detailed below. The two parameters can be distinguished because the
+.I receiver
+is a unicast address and the
+.I group
+is a multicast address.
+.PP
+NOTE: For Solaris 2.4/2.5, if the multicast interface is not the default
+interface, the -i option must be used to set the local address.
+.SH OPTIONS
+.TP 8 8
+.BI \-g\ gwy
+Send the trace query via unicast directly to the multicast router
+.I gwy
+rather than multicasting the query.
+This must be the last-hop router on the path from the intended
+.I source
+to the
+.IR receiver .
+.RS 8
+.TP 12 12
+.I CAUTION!!
+Versions 3.3 and 3.5 of
+.B mrouted
+will crash if a trace query is received via a
+unicast packet and
+.B mrouted
+has no route for the
+.I source
+address. Therefore, do not use the
+.B \-g
+option unless the target
+.B mrouted
+has been verified to be 3.4 or newer than 3.5.
+.RE
+.TP 8 8
+.BI \-i\ addr
+Use
+.I addr
+as the local interface address (on a multi-homed host) for sending the
+trace query and as the default for the
+.I receiver
+and the response destination.
+.TP 8 8
+.B \-l
+Loop indefinitely printing packet rate and loss statistics for the
+multicast path every 10 seconds (see
+.B \-S
+.IR stat_int ).
+.TP 8 8
+.B \-M
+Always send the response using multicast rather than attempting
+unicast first.
+.TP 8 8
+.BI \-m\ n
+Set to
+.I n
+the maximum number of hops that will be traced from the
+.I receiver
+back toward the
+.IR source .
+The default is 32 hops (infinity for the DVMRP routing protocol).
+.TP 8 8
+.B \-n
+Print hop addresses numerically rather than symbolically and numerically
+(saves a nameserver address-to-name lookup for each router found on the
+path).
+.TP 8 8
+.BI \-q\ n
+Set the maximum number of query attempts for any hop to
+.IR n .
+The default is 3.
+.TP 8 8
+.B \-p
+Listen passively for multicast responses from traces initiated by
+others. This works best when run on a multicast router.
+.TP 8 8
+.BI \-r\ host
+Send the trace response to
+.I host
+rather than to the host on which
+.B mtrace
+is being run, or to a multicast address other than the one registered
+for this purpose (224.0.1.32).
+.TP 8 8
+.B \-s
+Print a short form output including only the multicast path and not
+the packet rate and loss statistics.
+.TP 8 8
+.BI \-S\ n
+Change the interval between statistics gathering traces to
+.I n
+seconds (default 10 seconds).
+.TP 8 8
+.BI \-t\ ttl
+Set the
+.I ttl
+(time-to-live, or number of hops) for multicast trace queries and
+responses. The default is 64, except for local queries to the "all
+routers" multicast group which use ttl 1.
+.TP 8 8
+.B \-v
+Verbose mode; show hop times on the initial trace and statistics display.
+.TP 8 8
+.BI \-w\ n
+Set the time to wait for a trace response to
+.I n
+seconds (default 3 seconds).
+.SH USAGE
+.SS How It Works
+The technique used by the
+.B traceroute
+tool to trace unicast network paths will not work for IP multicast
+because ICMP responses are specifically forbidden for multicast traffic.
+Instead, a tracing feature has been built into the multicast routers.
+This technique has the advantage that additional information about
+packet rates and losses can be accumulated while the number of packets
+sent is minimized.
+.PP
+Since multicast uses
+reverse path forwarding, the trace is run backwards from the
+.I receiver
+to the
+.IR source .
+A trace query packet is sent to the last
+hop multicast router (the leaf router for the desired
+.I receiver
+address). The last hop router builds a trace response packet, fills in
+a report for its hop, and forwards the trace packet using unicast to
+the router it believes is the previous hop for packets originating
+from the specified
+.IR source .
+Each router along the path adds its report and forwards the packet.
+When the trace response packet reaches the first hop router (the router
+that is directly connected to the source's net), that router sends the
+completed response to the response destination address specified in
+the trace query.
+.PP
+If some multicast router along the path does not implement the
+multicast traceroute feature or if there is some outage, then no
+response will be returned. To solve this problem, the trace query
+includes a maximum hop count field to limit the number of hops traced
+before the response is returned. That allows a partial path to be
+traced.
+.PP
+The reports inserted by each router contain not only the address of
+the hop, but also the ttl required to forward and some flags to indicate
+routing errors, plus counts of the total number of packets on the
+incoming and outgoing interfaces and those forwarded for the specified
+.IR group .
+Taking differences in these counts for two traces separated in time
+and comparing the output packet counts from one hop with the input
+packet counts of the next hop allows the calculation of packet rate
+and packet loss statistics for each hop to isolate congestion
+problems.
+.SS Finding the Last-Hop Router
+The trace query must be sent to the multicast router which is the
+last hop on the path from the
+.I source
+to the
+.IR receiver .
+If the receiver is on the local subnet (as determined using the subnet
+mask), then the default method is to multicast the trace query to
+all-routers.mcast.net (224.0.0.2) with a ttl of 1. Otherwise, the
+trace query is multicast to the
+.I group
+address since the last hop router will be a member of that group if
+the receiver is. Therefore it is necessary to specify a group that
+the intended receiver has joined. This multicast is sent with a
+default ttl of 64, which may not be sufficient for all cases (changed
+with the
+.B \-t
+option).
+If the last hop router is known, it may also be addressed directly
+using the
+.B \-g
+option). Alternatively, if it is desired to trace a group that the
+receiver has not joined, but it is known that the last-hop router is a
+member of another group, the
+.B \-g
+option may also be used to specify a different multicast address for the
+trace query.
+.PP
+When tracing from a multihomed host or router, the default receiver
+address may not be the desired interface for the path from the source.
+In that case, the desired interface should be specified explicitly as
+the
+.IR receiver .
+.SS Directing the Response
+By default,
+.B mtrace
+first attempts to trace the full reverse path, unless the number of
+hops to trace is explicitly set with the
+.B \-m
+option. If there is no response within a 3 second timeout interval
+(changed with the
+.B \-w
+option), a "*" is printed and the probing switches to hop-by-hop mode.
+Trace queries are issued starting with a maximum hop count of one and
+increasing by one until the full path is traced or no response is
+received. At each hop, multiple probes are sent (default is three,
+changed with
+.B \-q
+option). The first half of the attempts (default is one) are made with
+the unicast address of the host running
+.B mtrace
+as the destination for the response. Since the unicast route may be
+blocked, the remainder of attempts request that the response be
+multicast to mtrace.mcast.net (224.0.1.32) with the ttl set to 32 more
+than what's needed to pass the thresholds seen so far along the path
+to the receiver. For the last quarter of the attempts (default is
+one), the ttl is increased by another 32 each time up to a maximum of
+192. Alternatively, the ttl may be set explicitly with the
+.B \-t
+option and/or the initial unicast attempts can be forced to use
+multicast instead with the
+.B \-M
+option. For each attempt, if no response is received within the
+timeout, a "*" is printed. After the specified number of attempts
+have failed,
+.B mtrace
+will try to query the next hop router with a DVMRP_ASK_NEIGHBORS2
+request (as used by the
+.B mrinfo
+program) to see what kind of router it is.
+.SH EXAMPLES
+The output of
+.B mtrace
+is in two sections. The first section is a short listing of the hops
+in the order they are queried, that is, in the reverse of the order
+from the
+.I source
+to the
+.IR receiver .
+For each hop, a line is printed showing the hop number (counted
+negatively to indicate that this is the reverse path); the multicast
+routing protocol (DVMRP, MOSPF, PIM, etc.); the threshold required to
+forward data (to the previous hop in the listing as indicated by the
+up-arrow character); and the cumulative delay for the query to reach
+that hop (valid only if the clocks are synchronized). This first
+section ends with a line showing the round-trip time which measures
+the interval from when the query is issued until the response is
+received, both derived from the local system clock. A sample use and
+output might be:
+.PP
+.nf
+.ft C
+oak.isi.edu 80# mtrace -l caraway.lcs.mit.edu 224.2.0.3
+Mtrace from 18.26.0.170 to 128.9.160.100 via group 224.2.0.3
+Querying full reverse path...
+ 0 oak.isi.edu (128.9.160.100)
+ -1 cub.isi.edu (128.9.160.153) DVMRP thresh^ 1 3 ms
+ -2 la.dart.net (140.173.128.1) DVMRP thresh^ 1 14 ms
+ -3 dc.dart.net (140.173.64.1) DVMRP thresh^ 1 50 ms
+ -4 bbn.dart.net (140.173.32.1) DVMRP thresh^ 1 63 ms
+ -5 mit.dart.net (140.173.48.2) DVMRP thresh^ 1 71 ms
+ -6 caraway.lcs.mit.edu (18.26.0.170)
+Round trip time 124 ms
+.fi
+.PP
+The second section provides a pictorial view of the path in the
+forward direction with data flow indicated by arrows pointing downward
+and the query path indicated by arrows pointing upward. For each hop,
+both the entry and exit addresses of the router are shown if
+different, along with the initial ttl required on the packet in order
+to be forwarded at this hop and the propagation delay across the hop
+assuming that the routers at both ends have synchronized clocks. The
+right half of this section is composed of several columns of
+statistics in two groups. Within each group, the columns are the
+number of packets lost, the number of packets sent, the percentage
+lost, and the average packet rate at each hop. These statistics are
+calculated from differences between traces and from hop to hop as
+explained above. The first group shows the statistics for all traffic
+flowing out the interface at one hop and in the interface at the next
+hop. The second group shows the statistics only for traffic forwarded
+from the specified
+.I source
+to the specified
+.IR group .
+.PP
+These statistics are shown on one or two lines for each hop. Without
+any options, this second section of the output is printed only once,
+approximately 10 seconds after the initial trace. One line is shown
+for each hop showing the statistics over that 10-second period. If
+the
+.B \-l
+option is given, the second section is repeated every 10 seconds and
+two lines are shown for each hop. The first line shows the statistics
+for the last 10 seconds, and the second line shows the cumulative
+statistics over the period since the initial trace, which is 101
+seconds in the example below. The second section of the output is
+omitted if the
+.B \-s
+option is set.
+.ie t \{\
+.ft C
+. ie \w'i'<>\w'm' \{\" looks like this is not proper Courier font
+(If this example is not properly columned with a fixed-width font, get
+.B groff
+and try again.)
+. \}
+.\}
+.PP
+.ft C
+.nf
+Waiting to accumulate statistics... Results after 101 seconds:
+
+ Source Response Dest Packet Statistics For Only For Traffic
+18.26.0.170 128.9.160.100 All Multicast Traffic From 18.26.0.170
+ | __/ rtt 125 ms Lost/Sent = Pct Rate To 224.2.0.3
+ v / hop 65 ms --------------------- ------------------
+18.26.0.144
+140.173.48.2 mit.dart.net
+ | ^ ttl 1 0/6 = --% 0 pps 0/2 = --% 0 pps
+ v | hop 8 ms 1/52 = 2% 0 pps 0/18 = 0% 0 pps
+140.173.48.1
+140.173.32.1 bbn.dart.net
+ | ^ ttl 2 0/6 = --% 0 pps 0/2 = --% 0 pps
+ v | hop 12 ms 1/52 = 2% 0 pps 0/18 = 0% 0 pps
+140.173.32.2
+140.173.64.1 dc.dart.net
+ | ^ ttl 3 0/271 = 0% 27 pps 0/2 = --% 0 pps
+ v | hop 34 ms -1/2652 = 0% 26 pps 0/18 = 0% 0 pps
+140.173.64.2
+140.173.128.1 la.dart.net
+ | ^ ttl 4 -2/831 = 0% 83 pps 0/2 = --% 0 pps
+ v | hop 11 ms -3/8072 = 0% 79 pps 0/18 = 0% 0 pps
+140.173.128.2
+128.9.160.153 cub.isi.edu
+ | \\__ ttl 5 833 83 pps 2 0 pps
+ v \\ hop -8 ms 8075 79 pps 18 0 pps
+128.9.160.100 128.9.160.100
+ Receiver Query Source
+.fi
+.PP
+Because the packet counts may be changing as the trace query is
+propagating, there may be small errors (off by 1 or 2) in these
+statistics. However, those errors should not accumulate, so the
+cumulative statistics line should increase in accuracy as a new trace
+is run every 10 seconds. There are two sources of larger errors, both
+of which show up as negative losses:
+.LP
+.RS
+.PD 0
+.TP 3
+\(bu
+If the input to a node is from a multi-access network with more than
+one other node attached, then the input count will be (close to) the
+sum of the output counts from all the attached nodes, but the output
+count from the previous hop on the traced path will be only part of
+that. Hence the output count minus the input count will be negative.
+.TP 3
+\(bu
+In release 3.3 of the DVMRP multicast forwarding software for SunOS
+and other systems, a multicast packet generated on a router will be
+counted as having come in an interface even though it did not. This
+creates the negative loss that can be seen in the example above.
+.PD
+.RE
+.LP
+Note that these negative losses may mask positive losses.
+.PP
+In the example, there is also one negative hop time. This simply
+indicates a lack of synchronization between the system clocks across
+that hop. This example also illustrates how the percentage loss is
+shown as two dashes when the number of packets sent is less than 10
+because the percentage would not be statistically valid.
+.PP
+A second example shows a trace to a receiver that is not local; the
+query is sent to the last-hop router with the
+.B \-g
+option. In this example, the trace of the full reverse path resulted
+in no response because there was a node running an old version of
+.B mrouted
+that did not implement the multicast traceroute function, so
+.B mtrace
+switched to hop-by-hop mode. The \*(lqRoute pruned\*(rq error code
+indicates that traffic for group 224.2.143.24 would not be forwarded.
+.PP
+.nf
+.ft C
+oak.isi.edu 108# mtrace -g 140.173.48.2 204.62.246.73 \\
+ butter.lcs.mit.edu 224.2.143.24
+Mtrace from 204.62.246.73 to 18.26.0.151 via group 224.2.143.24
+Querying full reverse path... * switching to hop-by-hop:
+ 0 butter.lcs.mit.edu (18.26.0.151)
+ -1 jam.lcs.mit.edu (18.26.0.144) DVMRP thresh^ 1 33 ms Route pruned
+ -2 bbn.dart.net (140.173.48.1) DVMRP thresh^ 1 36 ms
+ -3 dc.dart.net (140.173.32.2) DVMRP thresh^ 1 44 ms
+ -4 darpa.dart.net (140.173.240.2) DVMRP thresh^ 16 47 ms
+ -5 * * * noc.hpc.org (192.187.8.2) [mrouted 2.2] didn't respond
+Round trip time 95 ms
+.fi
+.SH AUTHOR
+Implemented by Steve Casner based on an initial prototype written by
+Ajit Thyagarajan. The multicast traceroute mechanism was designed by
+Van Jacobson with help from Steve Casner, Steve Deering, Dino
+Farinacci, and Deb Agrawal; it was implemented in
+.B mrouted
+by Ajit Thyagarajan and Bill Fenner. The option syntax and the output
+format of
+.B mtrace
+are modeled after the unicast
+.B traceroute
+program written by Van Jacobson.
+.SH SEE ALSO
+.BR mrouted (8) ,
+.BR mrinfo (8) ,
+.BR map-mbone (8) ,
+.BR traceroute (8)
diff --git a/gnu/dist/mtrace/mtrace.c b/gnu/dist/mtrace/mtrace.c
new file mode 100644
index 00000000000..abec1c42cdf
--- /dev/null
+++ b/gnu/dist/mtrace/mtrace.c
@@ -0,0 +1,1758 @@
+/* $NetBSD: mtrace.c,v 1.1 2002/10/01 03:50:11 itojun Exp $ */
+
+/*
+ * mtrace.c
+ *
+ * This tool traces the branch of a multicast tree from a source to a
+ * receiver for a particular multicast group and gives statistics
+ * about packet rate and loss for each hop along the path. It can
+ * usually be invoked just as
+ *
+ * mtrace source
+ *
+ * to trace the route from that source to the local host for a default
+ * group when only the route is desired and not group-specific packet
+ * counts. See the usage line for more complex forms.
+ *
+ *
+ * Released 4 Apr 1995. This program was adapted by Steve Casner
+ * (USC/ISI) from a prototype written by Ajit Thyagarajan (UDel and
+ * Xerox PARC). It attempts to parallel in command syntax and output
+ * format the unicast traceroute program written by Van Jacobson (LBL)
+ * for the parts where that makes sense.
+ *
+ * Copyright (c) 1995 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for non-commercial purposes
+ * and without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both the copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * any documentation, advertising materials, and other materials related
+ * to such distribution and use acknowledge that the software was
+ * developed by the University of Southern California, Information
+ * Sciences Institute. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * In particular, parts of the prototype version of this program may
+ * have been derived from mrouted programs sources covered by the
+ * license in the accompanying file named "LICENSE".
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: mtrace.c,v 1.1 2002/10/01 03:50:11 itojun Exp $");
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <memory.h>
+#include <netdb.h>
+#include <string.h>
+#include "defs.h"
+
+#include <stdarg.h>
+#ifdef SUNOS5
+#include <sys/systeminfo.h>
+#endif
+
+#define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 3 /* How many times to try */
+#define MAXHOPS UNREACHABLE /* Don't need more hops than max metric */
+#define UNICAST_TTL 255 /* TTL for unicast response */
+#define MULTICAST_TTL1 64 /* Default TTL for multicast query/response */
+#define MULTICAST_TTL_INC 32 /* TTL increment for increase after timeout */
+#define MULTICAST_TTL_MAX 192 /* Maximum TTL allowed (protect low-BW links */
+
+struct resp_buf {
+ u_long qtime; /* Time query was issued */
+ u_long rtime; /* Time response was received */
+ int len; /* Number of reports or length of data */
+ struct igmp igmp; /* IGMP header */
+ union {
+ struct {
+ struct tr_query q; /* Query/response header */
+ struct tr_resp r[MAXHOPS]; /* Per-hop reports */
+ } t;
+ char d[MAX_DVMRP_DATA_LEN]; /* Neighbor data */
+ } u;
+} base, incr[2];
+
+#define qhdr u.t.q
+#define resps u.t.r
+#define ndata u.d
+
+char names[MAXHOPS][40];
+int reset[MAXHOPS]; /* To get around 3.4 bug, ... */
+int swaps[MAXHOPS]; /* To get around 3.6 bug, ... */
+
+int timeout = DEFAULT_TIMEOUT;
+int nqueries = DEFAULT_RETRIES;
+int numeric = FALSE;
+int debug = 0;
+int passive = FALSE;
+int multicast = FALSE;
+int statint = 10;
+int verbose = 0;
+
+u_int32_t defgrp; /* Default group if not specified */
+u_int32_t query_cast; /* All routers multicast addr */
+u_int32_t resp_cast; /* Mtrace response multicast addr */
+
+u_int32_t lcl_addr = 0; /* This host address, in NET order */
+u_int32_t dst_netmask; /* netmask to go with qdst */
+
+/*
+ * Query/response parameters, all initialized to zero and set later
+ * to default values or from options.
+ */
+u_int32_t qsrc = 0; /* Source address in the query */
+u_int32_t qgrp = 0; /* Group address in the query */
+u_int32_t qdst = 0; /* Destination (receiver) address in query */
+u_char qno = 0; /* Max number of hops to query */
+u_int32_t raddr = 0; /* Address where response should be sent */
+int qttl = 0; /* TTL for the query packet */
+u_char rttl = 0; /* TTL for the response packet */
+u_int32_t gwy = 0; /* User-supplied last-hop router address */
+u_int32_t tdst = 0; /* Address where trace is sent (last-hop) */
+
+vifi_t numvifs; /* to keep loader happy */
+ /* (see kern.c) */
+
+u_long byteswap(u_long);
+char * inet_name(u_int32_t addr);
+u_int32_t host_addr(char *name);
+/* u_int is promoted u_char */
+char * proto_type(u_int type);
+char * flag_type(u_int type);
+
+u_int32_t get_netmask(int s, u_int32_t dst);
+int get_ttl(struct resp_buf *buf);
+int t_diff(u_long a, u_long b);
+u_long fixtime(u_long time);
+int send_recv(u_int32_t dst, int type, int code,
+ int tries, struct resp_buf *save);
+char * print_host(u_int32_t addr);
+char * print_host2(u_int32_t addr1, u_int32_t addr2);
+void print_trace(int index, struct resp_buf *buf);
+int what_kind(struct resp_buf *buf, char *why);
+char * scale(int *hop);
+void stat_line(struct tr_resp *r, struct tr_resp *s,
+ int have_next, int *res);
+void fixup_stats(struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new);
+int print_stats(struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new);
+void check_vif_state(void);
+void passive_mode(void);
+
+int main(int argc, char *argv[]);
+/* log() prototyped in defs.h */
+
+
+char *
+inet_name(u_int32_t addr)
+{
+ struct hostent *e;
+
+ e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+ return e ? e->h_name : "?";
+}
+
+
+u_int32_t
+host_addr(char *name)
+{
+ struct hostent *e = (struct hostent *)0;
+ u_int32_t addr;
+ int i, dots = 3;
+ char buf[40];
+ char *ip = name;
+ char *op = buf;
+
+ /*
+ * Undo BSD's favor -- take fewer than 4 octets as net/subnet address
+ * if the name is all numeric.
+ */
+ for (i = sizeof(buf) - 7; i > 0; --i) {
+ if (*ip == '.') --dots;
+ else if (*ip == '\0') break;
+ else if (!isdigit(*ip)) dots = 0; /* Not numeric, don't add zeroes */
+ *op++ = *ip++;
+ }
+ for (i = 0; i < dots; ++i) {
+ *op++ = '.';
+ *op++ = '0';
+ }
+ *op = '\0';
+
+ if (dots <= 0) e = gethostbyname(name);
+ if (e) memcpy((char *)&addr, e->h_addr_list[0], sizeof(addr));
+ else {
+ addr = inet_addr(buf);
+ if (addr == -1) {
+ addr = 0;
+ printf("Could not parse %s as host name or address\n", name);
+ }
+ }
+ return addr;
+}
+
+
+char *
+proto_type(u_int type)
+{
+ static char buf[80];
+
+ switch (type) {
+ case PROTO_DVMRP:
+ return ("DVMRP");
+ case PROTO_MOSPF:
+ return ("MOSPF");
+ case PROTO_PIM:
+ return ("PIM");
+ case PROTO_CBT:
+ return ("CBT");
+ case PROTO_PIM_SPEC:
+ return ("PIM-special");
+ case PROTO_PIM_STAT:
+ return ("PIM-static");
+ case PROTO_DVMRP_STAT:
+ return ("DVMRP-static");
+ case PROTO_PIM_MBGP:
+ return ("PIM/MBGP");
+ default:
+ (void)snprintf(buf, sizeof buf, "Unknown protocol code %d", type);
+ return (buf);
+ }
+}
+
+
+char *
+flag_type(u_int type)
+{
+ static char buf[80];
+
+ switch (type) {
+ case TR_NO_ERR:
+ return ("");
+ case TR_WRONG_IF:
+ return ("Wrong interface");
+ case TR_PRUNED:
+ return ("Prune sent upstream");
+ case TR_OPRUNED:
+ return ("Output pruned");
+ case TR_SCOPED:
+ return ("Hit scope boundary");
+ case TR_NO_RTE:
+ return ("No route");
+ case TR_OLD_ROUTER:
+ return ("Next router no mtrace");
+ case TR_NO_FWD:
+ return ("Not forwarding");
+ case TR_NO_SPACE:
+ return ("No space in packet");
+ case TR_RP_OR_CORE:
+ return ("RP/Core");
+ case TR_RPF_INT:
+ return ("Trace packet on RPT interface");
+ case TR_NO_MULTICAST:
+ return ("Trace packet on non-MC interface");
+ case TR_ADMIN_DENY:
+ return ("Trace admin-denied");
+ default:
+ (void)snprintf(buf, sizeof buf, "Unknown error code %d", type);
+ return (buf);
+ }
+}
+
+/*
+ * If destination is on a local net, get the netmask, else set the
+ * netmask to all ones. There are two side effects: if the local
+ * address was not explicitly set, and if the destination is on a
+ * local net, use that one; in either case, verify that the local
+ * address is valid.
+ */
+
+u_int32_t
+get_netmask(int s, u_int32_t dst)
+{
+ unsigned int i;
+ char ifbuf[5000];
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ u_int32_t if_addr, if_mask;
+ u_int32_t retval = 0xFFFFFFFF;
+ int found = FALSE;
+
+ ifc.ifc_buf = ifbuf;
+ ifc.ifc_len = sizeof(ifbuf);
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
+ perror("ioctl (SIOCGIFCONF)");
+ return (retval);
+ }
+ for (i = 0; i < ifc.ifc_len; ) {
+ ifr = (struct ifreq *)((char *)ifc.ifc_req + i);
+ i += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
+ if_addr = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr;
+ if (ioctl(s, SIOCGIFNETMASK, (char *)ifr) >= 0) {
+ if_mask = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr;
+ if ((dst & if_mask) == (if_addr & if_mask)) {
+ retval = if_mask;
+ if (lcl_addr == 0) lcl_addr = if_addr;
+ }
+ }
+ if (lcl_addr == if_addr) found = TRUE;
+ }
+ if (!found && lcl_addr != 0) {
+ printf("Interface address is not valid\n");
+ exit(1);
+ }
+ return (retval);
+}
+
+
+int
+get_ttl(struct resp_buf *buf)
+{
+ int rno;
+ struct tr_resp *b;
+ u_int ttl;
+
+ if (buf && (rno = buf->len) > 0) {
+ b = buf->resps + rno - 1;
+ ttl = b->tr_fttl;
+
+ while (--rno > 0) {
+ --b;
+ if (ttl < b->tr_fttl) ttl = b->tr_fttl;
+ else ++ttl;
+ }
+ ttl += MULTICAST_TTL_INC;
+ if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1;
+ if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX;
+ return (ttl);
+ } else return(MULTICAST_TTL1);
+}
+
+/*
+ * Calculate the difference between two 32-bit NTP timestamps and return
+ * the result in milliseconds.
+ */
+int
+t_diff(u_long a, u_long b)
+{
+ int d = a - b;
+
+ return ((d * 125) >> 13);
+}
+
+/*
+ * Fixup for incorrect time format in 3.3 mrouted.
+ * This is possible because (JAN_1970 mod 64K) is quite close to 32K,
+ * so correct and incorrect times will be far apart.
+ */
+u_long
+fixtime(u_long time)
+{
+ if (abs((int)(time-base.qtime)) > 0x3FFFFFFF)
+ time = ((time & 0xFFFF0000) + (JAN_1970 << 16)) +
+ ((time & 0xFFFF) << 14) / 15625;
+ return (time);
+}
+
+/*
+ * Swap bytes for poor little-endian machines that don't byte-swap
+ */
+u_long
+byteswap(u_long v)
+{
+ return ((v << 24) | ((v & 0xff00) << 8) |
+ ((v >> 8) & 0xff00) | (v >> 24));
+}
+
+int
+send_recv(u_int32_t dst, int type, int code, int tries, struct resp_buf *save)
+{
+ struct pollfd set[1];
+ struct timeval tq, tr, tv;
+ struct ip *ip;
+ struct igmp *igmp;
+ struct tr_query *query, *rquery;
+ int ipdatalen, iphdrlen, igmpdatalen;
+ u_int32_t local, group;
+ int datalen;
+ int count, recvlen, dummy = 0;
+ int len;
+ int i;
+
+ if (type == IGMP_MTRACE_QUERY) {
+ group = qgrp;
+ datalen = sizeof(struct tr_query);
+ } else {
+ group = htonl(MROUTED_LEVEL);
+ datalen = 0;
+ }
+ if (IN_MULTICAST(ntohl(dst))) local = lcl_addr;
+ else local = INADDR_ANY;
+
+ /*
+ * If the reply address was not explictly specified, start off
+ * with the unicast address of this host. Then, if there is no
+ * response after trying half the tries with unicast, switch to
+ * the standard multicast reply address. If the TTL was also not
+ * specified, set a multicast TTL and if needed increase it for the
+ * last quarter of the tries.
+ */
+ query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ query->tr_raddr = raddr ? raddr : multicast ? resp_cast : lcl_addr;
+ query->tr_rttl = rttl ? rttl :
+ IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL;
+ query->tr_src = qsrc;
+ query->tr_dst = qdst;
+
+ for (i = tries ; i > 0; --i) {
+ if (tries == nqueries && raddr == 0) {
+ if (i == ((nqueries + 1) >> 1)) {
+ query->tr_raddr = resp_cast;
+ if (rttl == 0) query->tr_rttl = get_ttl(save);
+ }
+ if (i <= ((nqueries + 3) >> 2) && rttl == 0) {
+ query->tr_rttl += MULTICAST_TTL_INC;
+ if (query->tr_rttl > MULTICAST_TTL_MAX)
+ query->tr_rttl = MULTICAST_TTL_MAX;
+ }
+ }
+
+ /*
+ * Change the qid for each request sent to avoid being confused
+ * by duplicate responses
+ */
+#ifdef SYSV
+ query->tr_qid = ((u_int32_t)lrand48() >> 8);
+#else
+ query->tr_qid = ((u_int32_t)random() >> 8);
+#endif
+
+ /*
+ * Set timer to calculate delays, then send query
+ */
+ gettimeofday(&tq, 0);
+ send_igmp(local, dst, type, code, group, datalen);
+
+ /*
+ * Wait for response, discarding false alarms
+ */
+ set[0].fd = igmp_socket;
+ set[0].events = POLLIN;
+ while (TRUE) {
+ gettimeofday(&tv, 0);
+ tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec;
+ tv.tv_usec = tq.tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec;
+ if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0;
+
+ count = poll(set, 1, tv.tv_sec * 1000 + tv.tv_usec / 1000);
+
+ if (count < 0) {
+ if (errno != EINTR) perror("select");
+ continue;
+ } else if (count == 0) {
+ printf("* ");
+ fflush(stdout);
+ break;
+ }
+
+ gettimeofday(&tr, 0);
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, (struct sockaddr *)0, &dummy);
+
+ if (recvlen <= 0) {
+ if (recvlen && errno != EINTR) perror("recvfrom");
+ continue;
+ }
+
+ if (recvlen < sizeof(struct ip)) {
+ fprintf(stderr,
+ "packet too short (%u bytes) for IP header", recvlen);
+ continue;
+ }
+ ip = (struct ip *) recv_buf;
+ if (ip->ip_p == 0) /* ignore cache creation requests */
+ continue;
+
+ iphdrlen = ip->ip_hl << 2;
+ ipdatalen = ip->ip_len;
+ if (iphdrlen + ipdatalen != recvlen) {
+ fprintf(stderr,
+ "packet shorter (%u bytes) than hdr+data len (%u+%u)\n",
+ recvlen, iphdrlen, ipdatalen);
+ continue;
+ }
+
+ igmp = (struct igmp *) (recv_buf + iphdrlen);
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ fprintf(stderr,
+ "IP data field too short (%u bytes) for IGMP from %s\n",
+ ipdatalen, inet_fmt(ip->ip_src.s_addr, s1));
+ continue;
+ }
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_DVMRP:
+ if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue;
+ len = igmpdatalen;
+ /*
+ * Accept DVMRP_NEIGHBORS2 response if it comes from the
+ * address queried or if that address is one of the local
+ * addresses in the response.
+ */
+ if (ip->ip_src.s_addr != dst) {
+ u_int32_t *p = (u_int32_t *)(igmp + 1);
+ u_int32_t *ep = p + (len >> 2);
+ while (p < ep) {
+ u_int32_t laddr = *p++;
+ int n = ntohl(*p++) & 0xFF;
+ if (laddr == dst) {
+ ep = p + 1; /* ensure p < ep after loop */
+ break;
+ }
+ p += n;
+ }
+ if (p >= ep) continue;
+ }
+ break;
+
+ case IGMP_MTRACE_QUERY: /* For backward compatibility with 3.3 */
+ case IGMP_MTRACE_REPLY:
+ if (igmpdatalen <= QLEN) continue;
+ if ((igmpdatalen - QLEN)%RLEN) {
+ printf("packet with incorrect datalen\n");
+ continue;
+ }
+
+ /*
+ * Ignore responses that don't match query.
+ */
+ rquery = (struct tr_query *)(igmp + 1);
+ if (rquery->tr_qid != query->tr_qid) continue;
+ if (rquery->tr_src != qsrc) continue;
+ if (rquery->tr_dst != qdst) continue;
+ len = (igmpdatalen - QLEN)/RLEN;
+
+ /*
+ * Ignore trace queries passing through this node when
+ * mtrace is run on an mrouter that is in the path
+ * (needed only because IGMP_MTRACE_QUERY is accepted above
+ * for backward compatibility with multicast release 3.3).
+ */
+ if (igmp->igmp_type == IGMP_MTRACE_QUERY) {
+ struct tr_resp *r = (struct tr_resp *)(rquery+1) + len - 1;
+ u_int32_t smask;
+
+ VAL_TO_MASK(smask, r->tr_smask);
+ if (len < code && (r->tr_inaddr & smask) != (qsrc & smask)
+ && r->tr_rmtaddr != 0 && !(r->tr_rflags & 0x80))
+ continue;
+ }
+
+ /*
+ * A match, we'll keep this one.
+ */
+ if (len > code) {
+ fprintf(stderr,
+ "Num hops received (%d) exceeds request (%d)\n",
+ len, code);
+ }
+ rquery->tr_raddr = query->tr_raddr; /* Insure these are */
+ rquery->tr_rttl = query->tr_rttl; /* as we sent them */
+ break;
+
+ default:
+ continue;
+ }
+
+ /*
+ * Most of the sanity checking done at this point.
+ * Return this packet we have been waiting for.
+ */
+ if (save) {
+ save->qtime = ((tq.tv_sec + JAN_1970) << 16) +
+ (tq.tv_usec << 10) / 15625;
+ save->rtime = ((tr.tv_sec + JAN_1970) << 16) +
+ (tr.tv_usec << 10) / 15625;
+ save->len = len;
+ bcopy((char *)igmp, (char *)&save->igmp, ipdatalen);
+ }
+ return (recvlen);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Most of this code is duplicated elsewhere. I'm not sure if
+ * the duplication is absolutely required or not.
+ *
+ * Ideally, this would keep track of ongoing statistics
+ * collection and print out statistics. (& keep track
+ * of h-b-h traces and only print the longest) For now,
+ * it just snoops on what traces it can.
+ */
+void
+passive_mode(void)
+{
+ struct timeval tr;
+ struct ip *ip;
+ struct igmp *igmp;
+ struct tr_resp *r;
+ int ipdatalen, iphdrlen, igmpdatalen;
+ int len, recvlen, dummy = 0;
+ u_int32_t smask;
+
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, INADDR_ANY);
+ } else k_join(htonl(0xE0000120), INADDR_ANY);
+
+ while (1) {
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, (struct sockaddr *)0, &dummy);
+ gettimeofday(&tr,0);
+
+ if (recvlen <= 0) {
+ if (recvlen && errno != EINTR) perror("recvfrom");
+ continue;
+ }
+
+ if (recvlen < sizeof(struct ip)) {
+ fprintf(stderr,
+ "packet too short (%u bytes) for IP header", recvlen);
+ continue;
+ }
+ ip = (struct ip *) recv_buf;
+ if (ip->ip_p == 0) /* ignore cache creation requests */
+ continue;
+
+ iphdrlen = ip->ip_hl << 2;
+ ipdatalen = ip->ip_len;
+ if (iphdrlen + ipdatalen != recvlen) {
+ fprintf(stderr,
+ "packet shorter (%u bytes) than hdr+data len (%u+%u)\n",
+ recvlen, iphdrlen, ipdatalen);
+ continue;
+ }
+
+ igmp = (struct igmp *) (recv_buf + iphdrlen);
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ fprintf(stderr,
+ "IP data field too short (%u bytes) for IGMP from %s\n",
+ ipdatalen, inet_fmt(ip->ip_src.s_addr, s1));
+ continue;
+ }
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_MTRACE_QUERY: /* For backward compatibility with 3.3 */
+ case IGMP_MTRACE_REPLY:
+ if (igmpdatalen < QLEN) continue;
+ if ((igmpdatalen - QLEN)%RLEN) {
+ printf("packet with incorrect datalen\n");
+ continue;
+ }
+
+ len = (igmpdatalen - QLEN)/RLEN;
+
+ break;
+
+ default:
+ continue;
+ }
+
+ base.qtime = ((tr.tv_sec + JAN_1970) << 16) +
+ (tr.tv_usec << 10) / 15625;
+ base.rtime = ((tr.tv_sec + JAN_1970) << 16) +
+ (tr.tv_usec << 10) / 15625;
+ base.len = len;
+ bcopy((char *)igmp, (char *)&base.igmp, ipdatalen);
+ /*
+ * If the user specified which traces to monitor,
+ * only accept traces that correspond to the
+ * request
+ */
+ if ((qsrc != 0 && qsrc != base.qhdr.tr_src) ||
+ (qdst != 0 && qdst != base.qhdr.tr_dst) ||
+ (qgrp != 0 && qgrp != igmp->igmp_group.s_addr))
+ continue;
+
+ printf("Mtrace from %s to %s via group %s (mxhop=%d)\n",
+ inet_fmt(base.qhdr.tr_dst, s1), inet_fmt(base.qhdr.tr_src, s2),
+ inet_fmt(igmp->igmp_group.s_addr, s3), igmp->igmp_code);
+ if (len == 0)
+ continue;
+ printf(" 0 ");
+ print_host(base.qhdr.tr_dst);
+ printf("\n");
+ print_trace(1, &base);
+ r = base.resps + base.len - 1;
+ VAL_TO_MASK(smask, r->tr_smask);
+ if ((r->tr_inaddr & smask) == (base.qhdr.tr_src & smask)) {
+ printf("%3d ", -(base.len+1));
+ print_host(base.qhdr.tr_src);
+ printf("\n");
+ } else if (r->tr_rmtaddr != 0) {
+ printf("%3d ", -(base.len+1));
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace"
+ : "is the next hop");
+ }
+ printf("\n");
+ }
+}
+
+char *
+print_host(u_int32_t addr)
+{
+ return print_host2(addr, 0);
+}
+
+/*
+ * On some routers, one interface has a name and the other doesn't.
+ * We always print the address of the outgoing interface, but can
+ * sometimes get the name from the incoming interface. This might be
+ * confusing but should be slightly more helpful than just a "?".
+ */
+char *
+print_host2(u_int32_t addr1, u_int32_t addr2)
+{
+ char *name;
+
+ if (numeric) {
+ printf("%s", inet_fmt(addr1, s1));
+ return ("");
+ }
+ name = inet_name(addr1);
+ if (*name == '?' && *(name + 1) == '\0' && addr2 != 0)
+ name = inet_name(addr2);
+ printf("%s (%s)", name, inet_fmt(addr1, s1));
+ return (name);
+}
+
+/*
+ * Print responses as received (reverse path from dst to src)
+ */
+void
+print_trace(int index, struct resp_buf *buf)
+{
+ struct tr_resp *r;
+ char *name;
+ int i;
+ int hop;
+ char *ms, *ft;
+
+ i = abs(index);
+ r = buf->resps + i - 1;
+
+ for (; i <= buf->len; ++i, ++r) {
+ if (index > 0) printf("%3d ", -i);
+ name = print_host2(r->tr_outaddr, r->tr_inaddr);
+ printf(" %s thresh^ %d", proto_type(r->tr_rproto), r->tr_fttl);
+ if (verbose) {
+ hop = t_diff(fixtime(ntohl(r->tr_qarr)), buf->qtime);
+ ms = scale(&hop);
+ printf(" %d%s", hop, ms);
+ }
+ ft = flag_type(r->tr_rflags);
+ if (strlen(ft) != 0)
+ printf(" %s", ft);
+ printf("\n");
+ memcpy(names[i-1], name, sizeof(names[0]) - 1);
+ names[i-1][sizeof(names[0])-1] = '\0';
+ }
+}
+
+/*
+ * See what kind of router is the next hop
+ */
+int
+what_kind(struct resp_buf *buf, char *why)
+{
+ u_int32_t smask;
+ int retval;
+ int hops = buf->len;
+ struct tr_resp *r = buf->resps + hops - 1;
+ u_int32_t next = r->tr_rmtaddr;
+
+ retval = send_recv(next, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0]);
+ print_host(next);
+ if (retval) {
+ u_int32_t version = ntohl(incr[0].igmp.igmp_group.s_addr);
+ u_int32_t *p = (u_int32_t *)incr[0].ndata;
+ u_int32_t *ep = p + (incr[0].len >> 2);
+ char *type = "";
+ retval = 0;
+ switch (version & 0xFF) {
+ case 1:
+ type = "proteon/mrouted ";
+ retval = 1;
+ break;
+
+ case 2:
+ case 3:
+ if (((version >> 8) & 0xFF) < 3) retval = 1;
+ /* Fall through */
+ case 4:
+ type = "mrouted ";
+ break;
+
+ case 10:
+ type = "cisco ";
+ }
+ printf(" [%s%d.%d] %s\n",
+ type, version & 0xFF, (version >> 8) & 0xFF,
+ why);
+ VAL_TO_MASK(smask, r->tr_smask);
+ while (p < ep) {
+ u_int32_t laddr = *p++;
+ int flags = (ntohl(*p) & 0xFF00) >> 8;
+ int n = ntohl(*p++) & 0xFF;
+ if (!(flags & (DVMRP_NF_DOWN | DVMRP_NF_DISABLED)) &&
+ (laddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(hops+2));
+ print_host(qsrc);
+ printf("\n");
+ return 1;
+ }
+ p += n;
+ }
+ return retval;
+ }
+ printf(" %s\n", why);
+ return 0;
+}
+
+
+char *
+scale(int *hop)
+{
+ if (*hop > -1000 && *hop < 10000) return (" ms");
+ *hop /= 1000;
+ if (*hop > -1000 && *hop < 10000) return (" s ");
+ return ("s ");
+}
+
+/*
+ * Calculate and print one line of packet loss and packet rate statistics.
+ * Checks for count of all ones from mrouted 2.3 that doesn't have counters.
+ */
+#define NEITHER 0
+#define INS 1
+#define OUTS 2
+#define BOTH 3
+void
+stat_line(struct tr_resp *r, struct tr_resp *s, int have_next, int *rst)
+{
+ int timediff = (fixtime(ntohl(s->tr_qarr)) -
+ fixtime(ntohl(r->tr_qarr))) >> 16;
+ int v_lost, v_pct;
+ int g_lost, g_pct;
+ int v_out = ntohl(s->tr_vifout) - ntohl(r->tr_vifout);
+ int g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt);
+ int v_pps, g_pps;
+ char v_str[8], g_str[8];
+ int have = NEITHER;
+ int res = *rst;
+
+ if (timediff == 0) timediff = 1;
+ v_pps = v_out / timediff;
+ g_pps = g_out / timediff;
+
+ if ((v_out && (s->tr_vifout != 0xFFFFFFFF && s->tr_vifout != 0)) ||
+ (r->tr_vifout != 0xFFFFFFFF && r->tr_vifout != 0))
+ have |= OUTS;
+
+ if (have_next) {
+ --r, --s, --rst;
+ if ((s->tr_vifin != 0xFFFFFFFF && s->tr_vifin != 0) ||
+ (r->tr_vifin != 0xFFFFFFFF && r->tr_vifin != 0))
+ have |= INS;
+ if (*rst)
+ res = 1;
+ }
+
+ switch (have) {
+ case BOTH:
+ v_lost = v_out - (ntohl(s->tr_vifin) - ntohl(r->tr_vifin));
+ if (v_out) v_pct = (v_lost * 100 + (v_out >> 1)) / v_out;
+ else v_pct = 0;
+ if (-100 < v_pct && v_pct < 101 && v_out > 10)
+ (void)snprintf(v_str, sizeof v_str, "%3d", v_pct);
+ else memcpy(v_str, " --", 4);
+
+ g_lost = g_out - (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
+ if (g_out) g_pct = (g_lost * 100 + (g_out >> 1))/ g_out;
+ else g_pct = 0;
+ if (-100 < g_pct && g_pct < 101 && g_out > 10)
+ (void)snprintf(g_str, sizeof g_str, "%3d", g_pct);
+ else memcpy(g_str, " --", 4);
+
+ printf("%6d/%-5d=%s%%%4d pps",
+ v_lost, v_out, v_str, v_pps);
+ if (res)
+ printf("\n");
+ else
+ printf("%6d/%-5d=%s%%%4d pps\n",
+ g_lost, g_out, g_str, g_pps);
+ break;
+
+ case INS:
+ v_out = ntohl(s->tr_vifin) - ntohl(r->tr_vifin);
+ v_pps = v_out / timediff;
+ /* Fall through */
+
+ case OUTS:
+ printf(" %-5d %4d pps",
+ v_out, v_pps);
+ if (res)
+ printf("\n");
+ else
+ printf(" %-5d %4d pps\n",
+ g_out, g_pps);
+ break;
+
+ case NEITHER:
+ printf("\n");
+ break;
+ }
+
+ if (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(s->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(s->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(s->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(r->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(r->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(r->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ",
+ (long)ntohl(s->tr_vifin)-ntohl(r->tr_vifin));
+ printf("v_out: %ld ",
+ (long)(ntohl(s->tr_vifout) - ntohl(r->tr_vifout)));
+ printf("pkts: %ld ", (long)(ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt)));
+ printf("time: %d\n", timediff);
+ printf("\t\t\t\tres: %d\n", res);
+ }
+}
+
+/*
+ * A fixup to check if any pktcnt has been reset, and to fix the
+ * byteorder bugs in mrouted 3.6 on little-endian machines.
+ */
+void
+fixup_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new)
+{
+ int rno = base->len;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int *r = reset + rno;
+ int *s = swaps + rno;
+ int res;
+
+ /* Check for byte-swappers */
+ while (--rno >= 0) {
+ --n; --p; --b; --s;
+ if (*s || abs(ntohl(n->tr_vifout) - ntohl(p->tr_vifout)) > 100000) {
+ /* This host sends byteswapped reports; swap 'em */
+ if (!*s) {
+ *s = 1;
+ b->tr_qarr = byteswap(b->tr_qarr);
+ b->tr_vifin = byteswap(b->tr_vifin);
+ b->tr_vifout = byteswap(b->tr_vifout);
+ b->tr_pktcnt = byteswap(b->tr_pktcnt);
+ }
+
+ n->tr_qarr = byteswap(n->tr_qarr);
+ n->tr_vifin = byteswap(n->tr_vifin);
+ n->tr_vifout = byteswap(n->tr_vifout);
+ n->tr_pktcnt = byteswap(n->tr_pktcnt);
+ }
+ }
+
+ rno = base->len;
+ b = base->resps + rno;
+ p = prev->resps + rno;
+ n = new->resps + rno;
+
+ while (--rno >= 0) {
+ --n; --p; --b; --r;
+ res = ((ntohl(n->tr_pktcnt) < ntohl(b->tr_pktcnt)) ||
+ (ntohl(n->tr_pktcnt) < ntohl(p->tr_pktcnt)));
+ if (debug > 2)
+ printf("\t\tr=%d, res=%d\n", *r, res);
+ if (*r) {
+ if (res || *r > 1) {
+ /*
+ * This router appears to be a 3.4 with that nasty ol'
+ * neighbor version bug, which causes it to constantly
+ * reset. Just nuke the statistics for this node, and
+ * don't even bother giving it the benefit of the
+ * doubt from now on.
+ */
+ p->tr_pktcnt = b->tr_pktcnt = n->tr_pktcnt;
+ r++;
+ } else {
+ /*
+ * This is simply the situation that the original
+ * fixup_stats was meant to deal with -- that a
+ * 3.3 or 3.4 router deleted a cache entry while
+ * traffic was still active.
+ */
+ *r = 0;
+ break;
+ }
+ } else
+ *r = res;
+ }
+
+ if (rno < 0) return;
+
+ rno = base->len;
+ b = base->resps + rno;
+ p = prev->resps + rno;
+
+ while (--rno >= 0) (--b)->tr_pktcnt = (--p)->tr_pktcnt;
+}
+
+/*
+ * Print responses with statistics for forward path (from src to dst)
+ */
+int
+print_stats(struct resp_buf *base, struct resp_buf *prev, struct resp_buf *new)
+{
+ int rtt, hop;
+ char *ms;
+ u_int32_t smask;
+ int rno = base->len - 1;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int *r = reset + rno;
+ u_long resptime = new->rtime;
+ u_long qarrtime = fixtime(ntohl(n->tr_qarr));
+ u_int ttl = n->tr_fttl;
+ int first = (base == prev);
+
+ VAL_TO_MASK(smask, b->tr_smask);
+ printf(" Source Response Dest");
+ printf(" Packet Statistics For Only For Traffic\n");
+ printf("%-15s %-15s All Multicast Traffic From %s\n",
+ ((b->tr_inaddr & smask) == (qsrc & smask)) ? s1 : " * * * ",
+ inet_fmt(base->qhdr.tr_raddr, s2), inet_fmt(qsrc, s1));
+ rtt = t_diff(resptime, new->qtime);
+ ms = scale(&rtt);
+ printf(" %c __/ rtt%5d%s Lost/Sent = Pct Rate To %s\n",
+ first ? 'v' : '|', rtt, ms, inet_fmt(qgrp, s2));
+ if (!first) {
+ hop = t_diff(resptime, qarrtime);
+ ms = scale(&hop);
+ printf(" v / hop%5d%s", hop, ms);
+ printf(" --------------------- --------------------\n");
+ }
+ if (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(n->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(n->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(n->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(b->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(b->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(b->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ",
+ (long)(ntohl(n->tr_vifin) - ntohl(b->tr_vifin)));
+ printf("v_out: %ld ",
+ (long)(ntohl(n->tr_vifout) - ntohl(b->tr_vifout)));
+ printf("pkts: %ld\n",
+ (long)(ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt)));
+ printf("\t\t\t\treset: %d\n", *r);
+ }
+
+ while (TRUE) {
+ if ((n->tr_inaddr != b->tr_inaddr) || (n->tr_inaddr != b->tr_inaddr))
+ return 1; /* Route changed */
+
+ if ((n->tr_inaddr != n->tr_outaddr))
+ printf("%-15s\n", inet_fmt(n->tr_inaddr, s1));
+ printf("%-15s %-14s %s\n", inet_fmt(n->tr_outaddr, s1), names[rno],
+ flag_type(n->tr_rflags));
+
+ if (rno-- < 1) break;
+
+ printf(" %c ^ ttl%5d ", first ? 'v' : '|', ttl);
+ stat_line(p, n, TRUE, r);
+ if (!first) {
+ resptime = qarrtime;
+ qarrtime = fixtime(ntohl((n-1)->tr_qarr));
+ hop = t_diff(resptime, qarrtime);
+ ms = scale(&hop);
+ printf(" v | hop%5d%s", hop, ms);
+ stat_line(b, n, TRUE, r);
+ }
+
+ --b, --p, --n, --r;
+ if (ttl < n->tr_fttl) ttl = n->tr_fttl;
+ else ++ttl;
+ }
+
+ printf(" %c \\__ ttl%5d ", first ? 'v' : '|', ttl);
+ stat_line(p, n, FALSE, r);
+ if (!first) {
+ hop = t_diff(qarrtime, new->qtime);
+ ms = scale(&hop);
+ printf(" v \\ hop%5d%s", hop, ms);
+ stat_line(b, n, FALSE, r);
+ }
+ printf("%-15s %s\n", inet_fmt(qdst, s1), inet_fmt(lcl_addr, s2));
+ printf(" Receiver Query Source\n\n");
+ return 0;
+}
+
+
+/***************************************************************************
+ * main
+ ***************************************************************************/
+
+int
+main(int argc, char **argv)
+{
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+ int recvlen;
+ struct timeval tv;
+ struct resp_buf *prev, *new;
+ struct tr_resp *r;
+ u_int32_t smask;
+ int rno;
+ int hops, nexthop, tries;
+ u_int32_t lastout = 0;
+ int numstats = 1;
+ int waittime;
+ int seed;
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "mtrace: must be root\n");
+ exit(1);
+ }
+ init_igmp();
+ if (setuid(getuid()) == -1)
+ log(LOG_ERR, errno, "setuid");
+
+ argv++, argc--;
+ if (argc == 0) goto usage;
+
+ while (argc > 0 && *argv[0] == '-') {
+ char *p = *argv++; argc--;
+ p++;
+ do {
+ char c = *p++;
+ char *arg = (char *) 0;
+ if (isdigit(*p)) {
+ arg = p;
+ p = "";
+ } else if (argc > 0) arg = argv[0];
+ switch (c) {
+ case 'd': /* Unlisted debug print option */
+ if (arg && isdigit(*arg)) {
+ debug = atoi(arg);
+ if (debug < 0) debug = 0;
+ if (debug > 3) debug = 3;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'M': /* Use multicast for reponse */
+ multicast = TRUE;
+ break;
+ case 'l': /* Loop updating stats indefinitely */
+ numstats = 3153600;
+ break;
+ case 'n': /* Don't reverse map host addresses */
+ numeric = TRUE;
+ break;
+ case 'p': /* Passive listen for traces */
+ passive = TRUE;
+ break;
+ case 'v': /* Verbosity */
+ verbose = TRUE;
+ break;
+ case 's': /* Short form, don't wait for stats */
+ numstats = 0;
+ break;
+ case 'w': /* Time to wait for packet arrival */
+ if (arg && isdigit(*arg)) {
+ timeout = atoi(arg);
+ if (timeout < 1) timeout = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'm': /* Max number of hops to trace */
+ if (arg && isdigit(*arg)) {
+ qno = atoi(arg);
+ if (qno > MAXHOPS) qno = MAXHOPS;
+ else if (qno < 1) qno = 0;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'q': /* Number of query retries */
+ if (arg && isdigit(*arg)) {
+ nqueries = atoi(arg);
+ if (nqueries < 1) nqueries = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'g': /* Last-hop gateway (dest of query) */
+ if (arg && (gwy = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 't': /* TTL for query packet */
+ if (arg && isdigit(*arg)) {
+ qttl = atoi(arg);
+ if (qttl < 1) qttl = 1;
+ rttl = qttl;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'r': /* Dest for response packet */
+ if (arg && (raddr = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'i': /* Local interface address */
+ if (arg && (lcl_addr = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ case 'S': /* Stat accumulation interval */
+ if (arg && isdigit(*arg)) {
+ statint = atoi(arg);
+ if (statint < 1) statint = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ goto usage;
+ default:
+ goto usage;
+ }
+ } while (*p);
+ }
+
+ if (argc > 0 && (qsrc = host_addr(argv[0]))) { /* Source of path */
+ if (IN_MULTICAST(ntohl(qsrc))) goto usage;
+ argv++, argc--;
+ if (argc > 0 && (qdst = host_addr(argv[0]))) { /* Dest of path */
+ argv++, argc--;
+ if (argc > 0 && (qgrp = host_addr(argv[0]))) { /* Path via group */
+ argv++, argc--;
+ }
+ if (IN_MULTICAST(ntohl(qdst))) {
+ u_int32_t temp = qdst;
+ qdst = qgrp;
+ qgrp = temp;
+ if (IN_MULTICAST(ntohl(qdst))) goto usage;
+ } else if (qgrp && !IN_MULTICAST(ntohl(qgrp))) goto usage;
+ }
+ }
+
+ if (passive) {
+ passive_mode();
+ return(0);
+ }
+
+ if (argc > 0 || qsrc == 0) {
+usage: printf("\
+Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\
+ [-S statint] [-t ttl] [-r resp_dest] [-i if_addr] source [receiver] [group]\n");
+ exit(1);
+ }
+
+ /*
+ * Set useful defaults for as many parameters as possible.
+ */
+
+ defgrp = htonl(0xE0020001); /* MBone Audio (224.2.0.1) */
+ query_cast = htonl(0xE0000002); /* All routers multicast addr */
+ resp_cast = htonl(0xE0000120); /* Mtrace response multicast addr */
+ if (qgrp == 0) qgrp = defgrp;
+
+ /*
+ * Get default local address for multicasts to use in setting defaults.
+ */
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ addr.sin_len = sizeof(addr);
+#endif
+ addr.sin_addr.s_addr = qgrp;
+ addr.sin_port = htons(2000); /* Any port above 1024 will do */
+
+ if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ||
+ (connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) ||
+ getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
+ perror("Determining local address");
+ exit(1);
+ }
+
+#ifdef SUNOS5
+ /*
+ * SunOS 5.X prior to SunOS 2.6, getsockname returns 0 for udp socket.
+ * This call to sysinfo will return the hostname.
+ * If the default multicast interfface (set with the route
+ * for 224.0.0.0) is not the same as the hostname,
+ * mtrace -i [if_addr] will have to be used.
+ */
+ if (addr.sin_addr.s_addr == 0) {
+ char myhostname[MAXHOSTNAMELEN];
+ struct hostent *hp;
+ int error;
+
+ error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
+ if (error == -1) {
+ perror("Getting my hostname");
+ exit(1);
+ }
+
+ hp = gethostbyname(myhostname);
+ if (hp == NULL || hp->h_addrtype != AF_INET ||
+ hp->h_length != sizeof(addr.sin_addr)) {
+ perror("Finding IP address for my hostname");
+ exit(1);
+ }
+
+ memcpy((char *)&addr.sin_addr.s_addr, hp->h_addr,
+ sizeof(addr.sin_addr.s_addr));
+ }
+#endif
+
+ /*
+ * Default destination for path to be queried is the local host.
+ */
+ if (qdst == 0) qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
+ dst_netmask = get_netmask(udp, qdst);
+ close(udp);
+ if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr;
+
+ /*
+ * Initialize the seed for random query identifiers.
+ */
+ gettimeofday(&tv, 0);
+ seed = tv.tv_usec ^ lcl_addr;
+#ifdef SYSV
+ srand48(seed);
+#else
+ srandom(seed);
+#endif
+
+ /*
+ * Protect against unicast queries to mrouted versions that might crash.
+ */
+ if (gwy && !IN_MULTICAST(ntohl(gwy)))
+ if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0])) {
+ int version = ntohl(incr[0].igmp.igmp_group.s_addr) & 0xFFFF;
+ if (version == 0x0303 || version == 0x0503) {
+ printf("Don't use -g to address an mrouted 3.%d, it might crash\n",
+ (version >> 8) & 0xFF);
+ exit(0);
+ }
+ }
+
+ printf("Mtrace from %s to %s via group %s\n",
+ inet_fmt(qsrc, s1), inet_fmt(qdst, s2), inet_fmt(qgrp, s3));
+
+ if ((qdst & dst_netmask) == (qsrc & dst_netmask)) {
+ printf("Source & receiver are directly connected, no path to trace\n");
+ exit(0);
+ }
+
+ /*
+ * If the response is to be a multicast address, make sure we
+ * are listening on that multicast address.
+ */
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr);
+ } else k_join(resp_cast, lcl_addr);
+
+ /*
+ * If the destination is on the local net, the last-hop router can
+ * be found by multicast to the all-routers multicast group.
+ * Otherwise, use the group address that is the subject of the
+ * query since by definition the last-hop router will be a member.
+ * Set default TTLs for local remote multicasts.
+ */
+ restart:
+
+ if (gwy == 0)
+ if ((qdst & dst_netmask) == (lcl_addr & dst_netmask)) tdst = query_cast;
+ else tdst = qgrp;
+ else tdst = gwy;
+
+ if (IN_MULTICAST(ntohl(tdst))) {
+ k_set_loop(1); /* If I am running on a router, I need to hear this */
+ if (tdst == query_cast) k_set_ttl(qttl ? qttl : 1);
+ else k_set_ttl(qttl ? qttl : MULTICAST_TTL1);
+ }
+
+ /*
+ * Try a query at the requested number of hops or MAXHOPS if unspecified.
+ */
+ if (qno == 0) {
+ hops = MAXHOPS;
+ tries = 1;
+ printf("Querying full reverse path... ");
+ fflush(stdout);
+ } else {
+ hops = qno;
+ tries = nqueries;
+ printf("Querying reverse path, maximum %d hops... ", qno);
+ fflush(stdout);
+ }
+ base.rtime = 0;
+ base.len = 0;
+
+ recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, hops, tries, &base);
+
+ /*
+ * If the initial query was successful, print it. Otherwise, if
+ * the query max hop count is the default of zero, loop starting
+ * from one until there is no response for four hops. The extra
+ * hops allow getting past an mtrace-capable mrouter that can't
+ * send multicast packets because all phyints are disabled.
+ */
+ if (recvlen) {
+ printf("\n 0 ");
+ print_host(qdst);
+ printf("\n");
+ print_trace(1, &base);
+ r = base.resps + base.len - 1;
+ if (r->tr_rflags == TR_OLD_ROUTER || r->tr_rflags == TR_NO_SPACE ||
+ qno != 0) {
+ printf("%3d ", -(base.len+1));
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace"
+ : "is the next hop");
+ } else {
+ VAL_TO_MASK(smask, r->tr_smask);
+ if ((r->tr_inaddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(base.len+1));
+ print_host(qsrc);
+ printf("\n");
+ }
+ }
+ } else if (qno == 0) {
+ printf("switching to hop-by-hop:\n 0 ");
+ print_host(qdst);
+ printf("\n");
+
+ for (hops = 1, nexthop = 1; hops <= MAXHOPS; ++hops) {
+ printf("%3d ", -hops);
+ fflush(stdout);
+
+ /*
+ * After a successful first hop, try switching to the unicast
+ * address of the last-hop router instead of multicasting the
+ * trace query. This should be safe for mrouted versions 3.3
+ * and 3.5 because there is a long route timeout with metric
+ * infinity before a route disappears. Switching to unicast
+ * reduces the amount of multicast traffic and avoids a bug
+ * with duplicate suppression in mrouted 3.5.
+ */
+ if (hops == 2 && gwy == 0 &&
+ (recvlen = send_recv(lastout, IGMP_MTRACE_QUERY, hops, 1, &base)))
+ tdst = lastout;
+ else recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, hops, nqueries, &base);
+
+ if (recvlen == 0) {
+ if (hops == 1) break;
+ if (hops == nexthop) {
+ if (what_kind(&base, "didn't respond")) {
+ /* the ask_neighbors determined that the
+ * not-responding router is the first-hop. */
+ break;
+ }
+ } else if (hops < nexthop + 3) {
+ printf("\n");
+ } else {
+ printf("...giving up\n");
+ break;
+ }
+ continue;
+ }
+ r = base.resps + base.len - 1;
+ if (base.len == hops &&
+ (hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) {
+ if (hops == nexthop) {
+ print_trace(-hops, &base);
+ } else {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base);
+ }
+ } else {
+ if (base.len < hops) {
+ /*
+ * A shorter trace than requested means a fatal error
+ * occurred along the path, or that the route changed
+ * to a shorter one.
+ *
+ * If the trace is longer than the last one we received,
+ * then we are resuming from a skipped router (but there
+ * is still probably a problem).
+ *
+ * If the trace is shorter than the last one we
+ * received, then the route must have changed (and
+ * there is still probably a problem).
+ */
+ if (nexthop <= base.len) {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base);
+ } else if (nexthop > base.len + 1) {
+ hops = base.len;
+ printf("\nRoute must have changed...\n");
+ print_trace(1, &base);
+ }
+ } else {
+ /*
+ * The last hop address is not the same as it was;
+ * the route probably changed underneath us.
+ */
+ hops = base.len;
+ printf("\nRoute must have changed...\n");
+ print_trace(1, &base);
+ }
+ }
+ lastout = r->tr_outaddr;
+
+ if (base.len < hops ||
+ r->tr_rmtaddr == 0 ||
+ (r->tr_rflags & 0x80)) {
+ VAL_TO_MASK(smask, r->tr_smask);
+ if (r->tr_rmtaddr) {
+ if (hops != nexthop) {
+ printf("\n%3d ", -(base.len+1));
+ }
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace" :
+ "would be the next hop");
+ /* XXX could do segmented trace if TR_NO_SPACE */
+ } else if (r->tr_rflags == TR_NO_ERR &&
+ (r->tr_inaddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(hops + 1));
+ print_host(qsrc);
+ printf("\n");
+ }
+ break;
+ }
+
+ nexthop = hops + 1;
+ }
+ }
+
+ if (base.rtime == 0) {
+ printf("Timed out receiving responses\n");
+ if (IN_MULTICAST(ntohl(tdst))) {
+ if (tdst == query_cast)
+ printf("Perhaps no local router has a route for source %s\n",
+ inet_fmt(qsrc, s1));
+ else
+ printf("Perhaps receiver %s is not a member of group %s,\n"
+ "or no router local to it has a route for source %s,\n"
+ "or multicast at ttl %d doesn't reach its last-hop router"
+ " for that source\n",
+ inet_fmt(qdst, s2), inet_fmt(qgrp, s3), inet_fmt(qsrc, s1),
+ qttl ? qttl : MULTICAST_TTL1);
+ }
+ exit(1);
+ }
+
+ printf("Round trip time %d ms\n\n", t_diff(base.rtime, base.qtime));
+
+ /*
+ * Use the saved response which was the longest one received,
+ * and make additional probes after delay to measure loss.
+ */
+ raddr = base.qhdr.tr_raddr;
+ rttl = base.qhdr.tr_rttl;
+ gettimeofday(&tv, 0);
+ waittime = statint - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16));
+ prev = &base;
+ new = &incr[numstats&1];
+
+ while (numstats--) {
+ if (waittime < 1) printf("\n");
+ else {
+ printf("Waiting to accumulate statistics... ");
+ fflush(stdout);
+ sleep((unsigned)waittime);
+ }
+ rno = base.len;
+ recvlen = send_recv(tdst, IGMP_MTRACE_QUERY, rno, nqueries, new);
+
+ if (recvlen == 0) {
+ printf("Timed out.\n");
+ exit(1);
+ }
+
+ if (rno != new->len) {
+ printf("Trace length doesn't match:\n");
+ /*
+ * XXX Should this trace result be printed, or is that
+ * too verbose? Perhaps it should just say restarting.
+ * But if the path is changing quickly, this may be the
+ * only snapshot of the current path. But, if the path
+ * is changing that quickly, does the current path really
+ * matter?
+ */
+ print_trace(1, new);
+ printf("Restarting.\n\n");
+ numstats++;
+ goto restart;
+ }
+
+ printf("Results after %d seconds:\n\n",
+ (int)((new->qtime - base.qtime) >> 16));
+ fixup_stats(&base, prev, new);
+ if (print_stats(&base, prev, new)) {
+ printf("Route changed:\n");
+ print_trace(1, new);
+ printf("Restarting.\n\n");
+ goto restart;
+ }
+ prev = new;
+ new = &incr[numstats&1];
+ waittime = statint;
+ }
+
+ /*
+ * If the response was multicast back, leave the group
+ */
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr);
+ } else k_leave(resp_cast, lcl_addr);
+
+ return (0);
+}
+
+void
+check_vif_state(void)
+{
+ log(LOG_WARNING, errno, "sendto");
+}
+
+/*
+ * Log errors and other messages to stderr, according to the severity
+ * of the message and the current debug level. For errors of severity
+ * LOG_ERR or worse, terminate the program.
+ */
+void
+log(int severity, int syserr, const char *format, ...)
+{
+ va_list ap;
+
+ switch (debug) {
+ case 0: if (severity > LOG_WARNING) return;
+ case 1: if (severity > LOG_NOTICE) return;
+ case 2: if (severity > LOG_INFO ) return;
+ default:
+ if (severity == LOG_WARNING)
+ fprintf(stderr, "warning - ");
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ": %s\n", strerror(syserr));
+ }
+ if (severity <= LOG_ERR) exit(1);
+}
+
+/* dummies */
+void accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen,
+ u_int32_t level)
+{
+}
+void accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group,
+ int r_type)
+{
+}
+void accept_neighbor_request2(u_int32_t src, u_int32_t dst)
+{
+}
+void accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
+ u_int32_t level)
+{
+}
+void accept_neighbor_request(u_int32_t src, u_int32_t dst)
+{
+}
+void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
+{
+}
+void add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
+{
+}
+void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
+{
+}
+void accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
+ u_int no, int datalen)
+{
+}
+void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group,
+ int tmo)
+{
+}
+void accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
+ u_int32_t level)
+{
+}
+void accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
+ u_int32_t level)
+{
+}
+void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
+{
+}
+void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
+{
+}
diff --git a/gnu/usr.sbin/Makefile b/gnu/usr.sbin/Makefile
index f9ab527f266..70497139cda 100644
--- a/gnu/usr.sbin/Makefile
+++ b/gnu/usr.sbin/Makefile
@@ -1,9 +1,9 @@
-# $NetBSD: Makefile,v 1.6 2002/10/01 03:30:51 itojun Exp $
+# $NetBSD: Makefile,v 1.7 2002/10/01 03:50:11 itojun Exp $
# XXX Temporary for NO_SENDMAIL
.include <bsd.own.mk>
-SUBDIR= dbsym mdsetimage mrouted postfix
+SUBDIR= dbsym map-mbone mdsetimage mrinfo mrouted mtrace postfix
.if !defined(NO_SENDMAIL)
SUBDIR+= sendmail
diff --git a/gnu/usr.sbin/map-mbone/Makefile b/gnu/usr.sbin/map-mbone/Makefile
new file mode 100644
index 00000000000..7843fe0c844
--- /dev/null
+++ b/gnu/usr.sbin/map-mbone/Makefile
@@ -0,0 +1,13 @@
+# $NetBSD: Makefile,v 1.1 2002/10/01 03:50:12 itojun Exp $
+# from: Id: Makefile,v 1.5 1993/06/24 05:11:16 deering Exp
+
+.include <bsd.own.mk>
+
+.PATH: ${NETBSDSRCDIR}/gnu/dist/map-mbone ${NETBSDSRCDIR}/gnu/dist/mrouted
+CPPFLAGS+= -I${NETBSDSRCDIR}/gnu/dist/mrouted
+
+PROG= map-mbone
+SRCS= igmp.c inet.c kern.c mapper.c
+MAN= map-mbone.8
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.sbin/mrinfo/Makefile b/gnu/usr.sbin/mrinfo/Makefile
new file mode 100644
index 00000000000..df81c8acb06
--- /dev/null
+++ b/gnu/usr.sbin/mrinfo/Makefile
@@ -0,0 +1,15 @@
+# $NetBSD: Makefile,v 1.1 2002/10/01 03:50:12 itojun Exp $
+# from: Id: Makefile,v 1.5 1993/06/24 05:11:16 deering Exp
+
+.include <bsd.own.mk>
+
+.PATH: ${NETBSDSRCDIR}/gnu/dist/mrinfo ${NETBSDSRCDIR}/gnu/dist/mrouted
+CPPFLAGS+= -I${NETBSDSRCDIR}/gnu/dist/mrouted
+
+PROG= mrinfo
+SRCS= igmp.c inet.c kern.c mrinfo.c
+MAN= mrinfo.8
+BINOWN= root
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.sbin/mtrace/Makefile b/gnu/usr.sbin/mtrace/Makefile
new file mode 100644
index 00000000000..047dd680c0f
--- /dev/null
+++ b/gnu/usr.sbin/mtrace/Makefile
@@ -0,0 +1,16 @@
+# $NetBSD: Makefile,v 1.1 2002/10/01 03:50:12 itojun Exp $
+# from: Id: Makefile,v 1.5 1993/06/24 05:11:16 deering Exp
+
+.include <bsd.own.mk>
+
+.PATH: ${NETBSDSRCDIR}/gnu/dist/mrouted ${NETBSDSRCDIR}/gnu/dist/mtrace
+CPPFLAGS+= -I${NETBSDSRCDIR}/gnu/dist/mrouted
+
+PROG= mtrace
+SRCS= igmp.c inet.c kern.c mtrace.c
+MAN= mtrace.8
+
+BINMODE=4555
+BINOWN= root
+
+.include <bsd.prog.mk>