diff options
| author | itojun <itojun@NetBSD.org> | 2002-10-01 03:50:07 +0000 |
|---|---|---|
| committer | itojun <itojun@NetBSD.org> | 2002-10-01 03:50:07 +0000 |
| commit | c3cbda0efb3b329d205eb2c08351e4ecb1c7ae42 (patch) | |
| tree | 4f7eb5fad5747f7638560fff0688ac41df4277f8 /gnu | |
| parent | d0a9c54d7d09e2584ad11b5eceef59d07b717e3d (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.8 | 91 | ||||
| -rw-r--r-- | gnu/dist/map-mbone/mapper.c | 974 | ||||
| -rw-r--r-- | gnu/dist/mrinfo/mrinfo.8 | 85 | ||||
| -rw-r--r-- | gnu/dist/mrinfo/mrinfo.c | 600 | ||||
| -rw-r--r-- | gnu/dist/mtrace/mtrace.8 | 516 | ||||
| -rw-r--r-- | gnu/dist/mtrace/mtrace.c | 1758 | ||||
| -rw-r--r-- | gnu/usr.sbin/Makefile | 4 | ||||
| -rw-r--r-- | gnu/usr.sbin/map-mbone/Makefile | 13 | ||||
| -rw-r--r-- | gnu/usr.sbin/mrinfo/Makefile | 15 | ||||
| -rw-r--r-- | gnu/usr.sbin/mtrace/Makefile | 16 |
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> |
