Home | History | Annotate | Line # | Download | only in map-mbone
      1 /*	$NetBSD: mapper.c,v 1.28 2019/02/03 03:19:31 mrg Exp $	*/
      2 
      3 /* Mapper for connections between MRouteD multicast routers.
      4  * Written by Pavel Curtis <Pavel (at) PARC.Xerox.Com>
      5  */
      6 
      7 /*
      8  * Copyright (c) 1992, 2001 Xerox Corporation.  All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without modification,
     11  * are permitted provided that the following conditions are met:
     12  *
     13  * Redistributions of source code must retain the above copyright notice,
     14  * this list of conditions and the following disclaimer.
     15  *
     16  * Redistributions in binary form must reproduce the above copyright notice,
     17  * this list of conditions and the following disclaimer in the documentation
     18  * and/or other materials provided with the distribution.
     19  *
     20  * Neither name of the Xerox, PARC, nor the names of its contributors may be used
     21  * to endorse or promote products derived from this software
     22  * without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
     25  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     26  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     27  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XEROX CORPORATION OR CONTRIBUTORS
     28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     31  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     32  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     33  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     34  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 #include <ctype.h>
     39 #include <string.h>
     40 #include <netdb.h>
     41 #include <sys/time.h>
     42 #include <poll.h>
     43 #include "defs.h"
     44 #include <arpa/inet.h>
     45 #include <stdarg.h>
     46 
     47 #define DEFAULT_TIMEOUT	2	/* How long to wait before retrying requests */
     48 #define DEFAULT_RETRIES 1	/* How many times to ask each router */
     49 
     50 
     51 /* All IP addresses are stored in the data structure in NET order. */
     52 
     53 typedef struct neighbor {
     54     struct neighbor    *next;
     55     u_int32_t		addr;		/* IP address in NET order */
     56     u_char		metric;		/* TTL cost of forwarding */
     57     u_char		threshold;	/* TTL threshold to forward */
     58     u_short		flags;		/* flags on connection */
     59 #define NF_PRESENT 0x8000	/* True if flags are meaningful */
     60 } Neighbor;
     61 
     62 typedef struct interface {
     63     struct interface *next;
     64     u_int32_t	addr;		/* IP address of the interface in NET order */
     65     Neighbor   *neighbors;	/* List of neighbors' IP addresses */
     66 } Interface;
     67 
     68 typedef struct node {
     69     u_int32_t	addr;		/* IP address of this entry in NET order */
     70     u_int32_t	version;	/* which mrouted version is running */
     71     int		tries;		/* How many requests sent?  -1 for aliases */
     72     union {
     73 	struct node *alias;		/* If alias, to what? */
     74 	struct interface *interfaces;	/* Else, neighbor data */
     75     } u;
     76     struct node *left, *right;
     77 } Node;
     78 
     79 
     80 Node   *routers = 0;
     81 u_int32_t	our_addr, target_addr = 0;		/* in NET order */
     82 int	debug = 0;
     83 int	retries = DEFAULT_RETRIES;
     84 int	timeout = DEFAULT_TIMEOUT;
     85 int	show_names = TRUE;
     86 vifi_t  numvifs;		/* to keep loader happy */
     87 				/* (see COPY_TABLES macro called in kern.c) */
     88 
     89 Node *			find_node(u_int32_t addr, Node **ptr);
     90 Interface *		find_interface(u_int32_t addr, Node *node);
     91 Neighbor *		find_neighbor(u_int32_t addr, Node *node);
     92 void			ask(u_int32_t dst);
     93 void			ask2(u_int32_t dst);
     94 int			retry_requests(Node *node);
     95 char *			inet_name(u_int32_t addr);
     96 void			print_map(Node *node);
     97 char *			graph_name(u_int32_t addr, char *buf, size_t);
     98 void			graph_edges(Node *node);
     99 void			elide_aliases(Node *node);
    100 void			graph_map(void);
    101 int			get_number(int *var, int deflt, char ***pargv,
    102 				   int *pargc);
    103 u_int32_t		host_addr(char *name);
    104 
    105 void logit(int severity, int syserr, const char *format, ...)
    106 	__attribute__((__format__(__printf__, 3, 4)));
    107 
    108 Node *find_node(u_int32_t addr, Node **ptr)
    109 {
    110     Node *n = *ptr;
    111 
    112     if (!n) {
    113 	*ptr = n = (Node *) malloc(sizeof(Node));
    114 	n->addr = addr;
    115 	n->version = 0;
    116 	n->tries = 0;
    117 	n->u.interfaces = 0;
    118 	n->left = n->right = 0;
    119 	return n;
    120     } else if (addr == n->addr)
    121 	return n;
    122     else if (addr < n->addr)
    123 	return find_node(addr, &(n->left));
    124     else
    125 	return find_node(addr, &(n->right));
    126 }
    127 
    128 
    129 Interface *find_interface(u_int32_t addr, Node *node)
    130 {
    131     Interface *ifc;
    132 
    133     for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
    134 	if (ifc->addr == addr)
    135 	    return ifc;
    136 
    137     ifc = (Interface *) malloc(sizeof(Interface));
    138     ifc->addr = addr;
    139     ifc->next = node->u.interfaces;
    140     node->u.interfaces = ifc;
    141     ifc->neighbors = 0;
    142 
    143     return ifc;
    144 }
    145 
    146 
    147 Neighbor *find_neighbor(u_int32_t addr, Node *node)
    148 {
    149     Interface *ifc;
    150 
    151     for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
    152 	Neighbor *nb;
    153 
    154 	for (nb = ifc->neighbors; nb; nb = nb->next)
    155 	    if (nb->addr == addr)
    156 		return nb;
    157     }
    158 
    159     return 0;
    160 }
    161 
    162 
    163 /*
    164  * Log errors and other messages to stderr, according to the severity of the
    165  * message and the current debug level.  For errors of severity LOG_ERR or
    166  * worse, terminate the program.
    167  */
    168 void
    169 logit(int severity, int syserr, const char *format, ...)
    170 {
    171     va_list ap;
    172     char    fmt[100];
    173 
    174     switch (debug) {
    175 	case 0: if (severity > LOG_WARNING) return;
    176 		/* FALLTHROUGH */
    177 	case 1: if (severity > LOG_NOTICE ) return;
    178 		/* FALLTHROUGH */
    179 	case 2: if (severity > LOG_INFO   ) return;
    180 		/* FALLTHROUGH */
    181 	default:
    182 	    fmt[0] = '\0';
    183 	    if (severity == LOG_WARNING)
    184 		strlcat(fmt, "warning - ", sizeof(fmt));
    185 	    strlcat(fmt, format, sizeof(fmt));
    186 	    format = fmt;
    187 	    va_start(ap, format);
    188 	    vfprintf(stderr, format, ap);
    189 	    va_end(ap);
    190 	    if (syserr == 0)
    191 		fprintf(stderr, "\n");
    192 	    else
    193 		fprintf(stderr, ": %s\n", strerror(syserr));
    194     }
    195 
    196     if (severity <= LOG_ERR)
    197 	exit(1);
    198 }
    199 
    200 
    201 /*
    202  * Send a neighbors-list request.
    203  */
    204 void ask(u_int32_t dst)
    205 {
    206     send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
    207 		htonl(MROUTED_LEVEL), 0);
    208 }
    209 
    210 void ask2(u_int32_t dst)
    211 {
    212     send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
    213 		htonl(MROUTED_LEVEL), 0);
    214 }
    215 
    216 
    217 /*
    218  * Process an incoming group membership report.
    219  */
    220 void accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group, int r_type)
    221 {
    222     logit(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
    223 	inet_fmt(src), inet_fmt(dst));
    224 }
    225 
    226 
    227 /*
    228  * Process an incoming neighbor probe message.
    229  */
    230 void accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen,
    231 		  u_int32_t level)
    232 {
    233     logit(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
    234 	inet_fmt(src), inet_fmt(dst));
    235 }
    236 
    237 
    238 /*
    239  * Process an incoming route report message.
    240  */
    241 void accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
    242 		   u_int32_t level)
    243 {
    244     logit(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
    245 	inet_fmt(src), inet_fmt(dst));
    246 }
    247 
    248 
    249 /*
    250  * Process an incoming neighbor-list request message.
    251  */
    252 void accept_neighbor_request(u_int32_t src, u_int32_t dst)
    253 {
    254     if (src != our_addr)
    255 	logit(LOG_INFO, 0,
    256 	    "ignoring spurious DVMRP neighbor request from %s to %s",
    257 	    inet_fmt(src), inet_fmt(dst));
    258 }
    259 
    260 void accept_neighbor_request2(u_int32_t src, u_int32_t dst)
    261 {
    262     if (src != our_addr)
    263 	logit(LOG_INFO, 0,
    264 	    "ignoring spurious DVMRP neighbor request2 from %s to %s",
    265 	    inet_fmt(src), inet_fmt(dst));
    266 }
    267 
    268 
    269 /*
    270  * Process an incoming neighbor-list message.
    271  */
    272 void accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
    273 		      u_int32_t level)
    274 {
    275     Node       *node = find_node(src, &routers);
    276 
    277     if (node->tries == 0)	/* Never heard of 'em; must have hit them at */
    278 	node->tries = 1;	/* least once, though...*/
    279     else if (node->tries == -1)	/* follow alias link */
    280 	node = node->u.alias;
    281 
    282 #define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\
    283 		     a += ((u_int32_t)*p++ << 8), a += *p++)
    284 
    285     /* if node is running a recent mrouted, ask for additional info */
    286     if (level != 0) {
    287 	node->version = level;
    288 	node->tries = 1;
    289 	ask2(src);
    290 	return;
    291     }
    292 
    293     if (debug > 3) {
    294 	int i;
    295 
    296 	fprintf(stderr, "    datalen = %d\n", datalen);
    297 	for (i = 0; i < datalen; i++) {
    298 	    if ((i & 0xF) == 0)
    299 		fprintf(stderr, "   ");
    300 	    fprintf(stderr, " %02x", p[i]);
    301 	    if ((i & 0xF) == 0xF)
    302 		fprintf(stderr, "\n");
    303 	}
    304 	if ((datalen & 0xF) != 0xF)
    305 	    fprintf(stderr, "\n");
    306     }
    307 
    308     while (datalen > 0) {	/* loop through interfaces */
    309 	u_int32_t		ifc_addr;
    310 	u_char		metric, threshold, ncount;
    311 	Node   	       *ifc_node;
    312 	Interface      *ifc;
    313 	Neighbor       *old_neighbors;
    314 
    315 	if (datalen < 4 + 3) {
    316 	    logit(LOG_WARNING, 0, "received truncated interface record from %s",
    317 		inet_fmt(src));
    318 	    return;
    319 	}
    320 
    321 	GET_ADDR(ifc_addr);
    322 	ifc_addr = htonl(ifc_addr);
    323 	metric = *p++;
    324 	threshold = *p++;
    325 	ncount = *p++;
    326 	datalen -= 4 + 3;
    327 
    328 	/* Fix up any alias information */
    329 	ifc_node = find_node(ifc_addr, &routers);
    330 	if (ifc_node->tries == 0) { /* new node */
    331 	    ifc_node->tries = -1;
    332 	    ifc_node->u.alias = node;
    333 	} else if (ifc_node != node
    334 		   && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
    335 	    /* must merge two hosts' nodes */
    336 	    Interface  *ifc_i, *next_ifc_i;
    337 
    338 	    if (ifc_node->tries == -1) {
    339 		Node *tmp = ifc_node->u.alias;
    340 
    341 		ifc_node->u.alias = node;
    342 		ifc_node = tmp;
    343 	    }
    344 
    345 	    /* Merge ifc_node (foo_i) into node (foo_n) */
    346 
    347 	    if (ifc_node->tries > node->tries)
    348 		node->tries = ifc_node->tries;
    349 
    350 	    for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
    351 		Neighbor *nb_i, *next_nb_i, *nb_n;
    352 		Interface *ifc_n = find_interface(ifc_i->addr, node);
    353 
    354 		old_neighbors = ifc_n->neighbors;
    355 		for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
    356 		    next_nb_i = nb_i->next;
    357 		    for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
    358 			if (nb_i->addr == nb_n->addr) {
    359 			    if (nb_i->metric != nb_n->metric
    360 				|| nb_i->threshold != nb_n->threshold)
    361 				logit(LOG_WARNING, 0,
    362 				    "inconsistent %s for neighbor %s of %s",
    363 				    "metric/threshold",
    364 				    inet_fmt(nb_i->addr),
    365 				    inet_fmt(node->addr));
    366 			    free(nb_i);
    367 			    break;
    368 			}
    369 		    if (!nb_n) { /* no match for this neighbor yet */
    370 			nb_i->next = ifc_n->neighbors;
    371 			ifc_n->neighbors = nb_i;
    372 		    }
    373 		}
    374 
    375 		next_ifc_i = ifc_i->next;
    376 		free(ifc_i);
    377 	    }
    378 
    379 	    ifc_node->tries = -1;
    380 	    ifc_node->u.alias = node;
    381 	}
    382 
    383 	ifc = find_interface(ifc_addr, node);
    384 	old_neighbors = ifc->neighbors;
    385 
    386 	/* Add the neighbors for this interface */
    387 	while (ncount--) {
    388 	    u_int32_t 	neighbor;
    389 	    Neighbor   *nb;
    390 	    Node       *n_node;
    391 
    392 	    if (datalen < 4) {
    393 		logit(LOG_WARNING, 0, "received truncated neighbor list from %s",
    394 		    inet_fmt(src));
    395 		return;
    396 	    }
    397 
    398 	    GET_ADDR(neighbor);
    399 	    neighbor = htonl(neighbor);
    400 	    datalen -= 4;
    401 
    402 	    for (nb = old_neighbors; nb; nb = nb->next)
    403 		if (nb->addr == neighbor) {
    404 		    if (metric != nb->metric || threshold != nb->threshold)
    405 			logit(LOG_WARNING, 0,
    406 			    "inconsistent %s for neighbor %s of %s",
    407 			    "metric/threshold",
    408 			    inet_fmt(nb->addr), inet_fmt(node->addr));
    409 		    goto next_neighbor;
    410 		}
    411 
    412 	    nb = (Neighbor *) malloc(sizeof(Neighbor));
    413 	    nb->next = ifc->neighbors;
    414 	    ifc->neighbors = nb;
    415 	    nb->addr = neighbor;
    416 	    nb->metric = metric;
    417 	    nb->threshold = threshold;
    418 	    nb->flags = 0;
    419 
    420 	    n_node = find_node(neighbor, &routers);
    421 	    if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
    422 		ask(neighbor);
    423 		n_node->tries = 1;
    424 	    }
    425 
    426 	  next_neighbor: ;
    427 	}
    428     }
    429 }
    430 
    431 void accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
    432 		       u_int32_t level)
    433 {
    434     Node       *node = find_node(src, &routers);
    435     u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
    436     /* well, only possibly_broken_cisco, but that's too long to type. */
    437 
    438     if (node->tries == 0)	/* Never heard of 'em; must have hit them at */
    439 	node->tries = 1;	/* least once, though...*/
    440     else if (node->tries == -1)	/* follow alias link */
    441 	node = node->u.alias;
    442 
    443     while (datalen > 0) {	/* loop through interfaces */
    444 	u_int32_t		ifc_addr;
    445 	u_char		metric, threshold, ncount, flags;
    446 	Node   	       *ifc_node;
    447 	Interface      *ifc;
    448 	Neighbor       *old_neighbors;
    449 
    450 	if (datalen < 4 + 4) {
    451 	    logit(LOG_WARNING, 0, "received truncated interface record from %s",
    452 		inet_fmt(src));
    453 	    return;
    454 	}
    455 
    456 	ifc_addr = *(u_int32_t*)p;
    457 	p += 4;
    458 	metric = *p++;
    459 	threshold = *p++;
    460 	flags = *p++;
    461 	ncount = *p++;
    462 	datalen -= 4 + 4;
    463 
    464 	if (broken_cisco && ncount == 0)	/* dumb Ciscos */
    465 		ncount = 1;
    466 	if (broken_cisco && ncount > 15)	/* dumb Ciscos */
    467 		ncount = ncount & 0xf;
    468 
    469 	/* Fix up any alias information */
    470 	ifc_node = find_node(ifc_addr, &routers);
    471 	if (ifc_node->tries == 0) { /* new node */
    472 	    ifc_node->tries = -1;
    473 	    ifc_node->u.alias = node;
    474 	} else if (ifc_node != node
    475 		   && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
    476 	    /* must merge two hosts' nodes */
    477 	    Interface  *ifc_i, *next_ifc_i;
    478 
    479 	    if (ifc_node->tries == -1) {
    480 		Node *tmp = ifc_node->u.alias;
    481 
    482 		ifc_node->u.alias = node;
    483 		ifc_node = tmp;
    484 	    }
    485 
    486 	    /* Merge ifc_node (foo_i) into node (foo_n) */
    487 
    488 	    if (ifc_node->tries > node->tries)
    489 		node->tries = ifc_node->tries;
    490 
    491 	    for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
    492 		Neighbor *nb_i, *next_nb_i, *nb_n;
    493 		Interface *ifc_n = find_interface(ifc_i->addr, node);
    494 
    495 		old_neighbors = ifc_n->neighbors;
    496 		for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
    497 		    next_nb_i = nb_i->next;
    498 		    for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
    499 			if (nb_i->addr == nb_n->addr) {
    500 			    if (nb_i->metric != nb_n->metric
    501 				|| nb_i->threshold != nb_n->threshold)
    502 				logit(LOG_WARNING, 0,
    503 				    "inconsistent %s for neighbor %s of %s",
    504 				    "metric/threshold",
    505 				    inet_fmt(nb_i->addr),
    506 				    inet_fmt(node->addr));
    507 			    free(nb_i);
    508 			    break;
    509 			}
    510 		    if (!nb_n) { /* no match for this neighbor yet */
    511 			nb_i->next = ifc_n->neighbors;
    512 			ifc_n->neighbors = nb_i;
    513 		    }
    514 		}
    515 
    516 		next_ifc_i = ifc_i->next;
    517 		free(ifc_i);
    518 	    }
    519 
    520 	    ifc_node->tries = -1;
    521 	    ifc_node->u.alias = node;
    522 	}
    523 
    524 	ifc = find_interface(ifc_addr, node);
    525 	old_neighbors = ifc->neighbors;
    526 
    527 	/* Add the neighbors for this interface */
    528 	while (ncount-- && datalen > 0) {
    529 	    u_int32_t 	neighbor;
    530 	    Neighbor   *nb;
    531 	    Node       *n_node;
    532 
    533 	    if (datalen < 4) {
    534 		logit(LOG_WARNING, 0, "received truncated neighbor list from %s",
    535 		    inet_fmt(src));
    536 		return;
    537 	    }
    538 
    539 	    neighbor = *(u_int32_t*)p;
    540 	    p += 4;
    541 	    datalen -= 4;
    542 	    if (neighbor == 0)
    543 		/* make leaf nets point to themselves */
    544 		neighbor = ifc_addr;
    545 
    546 	    for (nb = old_neighbors; nb; nb = nb->next)
    547 		if (nb->addr == neighbor) {
    548 		    if (metric != nb->metric || threshold != nb->threshold)
    549 			logit(LOG_WARNING, 0,
    550 			    "inconsistent %s for neighbor %s of %s",
    551 			    "metric/threshold",
    552 			    inet_fmt(nb->addr), inet_fmt(node->addr));
    553 		    goto next_neighbor;
    554 		}
    555 
    556 	    nb = (Neighbor *) malloc(sizeof(Neighbor));
    557 	    nb->next = ifc->neighbors;
    558 	    ifc->neighbors = nb;
    559 	    nb->addr = neighbor;
    560 	    nb->metric = metric;
    561 	    nb->threshold = threshold;
    562 	    nb->flags = flags | NF_PRESENT;
    563 
    564 	    n_node = find_node(neighbor, &routers);
    565 	    if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
    566 		ask(neighbor);
    567 		n_node->tries = 1;
    568 	    }
    569 
    570 	  next_neighbor: ;
    571 	}
    572     }
    573 }
    574 
    575 
    576 void check_vif_state(void)
    577 {
    578     logit(LOG_NOTICE, 0, "network marked down...");
    579 }
    580 
    581 
    582 int retry_requests(Node *node)
    583 {
    584     int	result;
    585 
    586     if (node) {
    587 	result = retry_requests(node->left);
    588 	if (node->tries > 0  &&  node->tries < retries) {
    589 	    if (node->version)
    590 		ask2(node->addr);
    591 	    else
    592 		ask(node->addr);
    593 	    node->tries++;
    594 	    result = 1;
    595 	}
    596 	return retry_requests(node->right) || result;
    597     } else
    598 	return 0;
    599 }
    600 
    601 
    602 char *inet_name(u_int32_t addr)
    603 {
    604     struct hostent *e;
    605 
    606     e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
    607 
    608     return e ? e->h_name : 0;
    609 }
    610 
    611 
    612 void print_map(Node *node)
    613 {
    614     if (node) {
    615 	char *name, *addr;
    616 
    617 	print_map(node->left);
    618 
    619 	addr = inet_fmt(node->addr);
    620 	if (!target_addr
    621 	    || (node->tries >= 0 && node->u.interfaces)
    622 	    || (node->tries == -1
    623 		&& node->u.alias->tries >= 0
    624 		&& node->u.alias->u.interfaces)) {
    625 	    if (show_names && (name = inet_name(node->addr)))
    626 		printf("%s (%s):", addr, name);
    627 	    else
    628 		printf("%s:", addr);
    629 	    if (node->tries < 0)
    630 		printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr));
    631 	    else if (!node->u.interfaces)
    632 		printf(" no response to query\n\n");
    633 	    else {
    634 		Interface *ifc;
    635 
    636 		if (node->version)
    637 		    printf(" <v%d.%d>", node->version & 0xff,
    638 					(node->version >> 8) & 0xff);
    639 		printf("\n");
    640 		for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
    641 		    Neighbor *nb;
    642 		    char *ifc_name = inet_fmt(ifc->addr);
    643 		    int ifc_len = strlen(ifc_name);
    644 		    int count = 0;
    645 
    646 		    printf("    %s:", ifc_name);
    647 		    for (nb = ifc->neighbors; nb; nb = nb->next) {
    648 			if (count > 0)
    649 			    printf("%*s", ifc_len + 5, "");
    650 			printf("  %s", inet_fmt(nb->addr));
    651 			if (show_names  &&  (name = inet_name(nb->addr)))
    652 			    printf(" (%s)", name);
    653 			printf(" [%d/%d", nb->metric, nb->threshold);
    654 			if (nb->flags) {
    655 			    u_short flags = nb->flags;
    656 			    if (flags & DVMRP_NF_TUNNEL)
    657 				    printf("/tunnel");
    658 			    if (flags & DVMRP_NF_SRCRT)
    659 				    printf("/srcrt");
    660 			    if (flags & DVMRP_NF_QUERIER)
    661 				    printf("/querier");
    662 			    if (flags & DVMRP_NF_DISABLED)
    663 				    printf("/disabled");
    664 			    if (flags & DVMRP_NF_DOWN)
    665 				    printf("/down");
    666 			}
    667                         printf("]\n");
    668 			count++;
    669 		    }
    670 		}
    671 		printf("\n");
    672 	    }
    673 	}
    674 	print_map(node->right);
    675     }
    676 }
    677 
    678 
    679 char *graph_name(u_int32_t addr, char *buf, size_t l)
    680 {
    681     char *name;
    682 
    683     if (show_names && (name = inet_name(addr)))
    684 	strlcpy(buf, name, l);
    685     else
    686 	inet_fmt(addr);
    687 
    688     return buf;
    689 }
    690 
    691 
    692 void graph_edges(Node *node)
    693 {
    694     Interface *ifc;
    695     Neighbor *nb;
    696     char name[100];
    697 
    698     if (node) {
    699 	graph_edges(node->left);
    700 	if (node->tries >= 0) {
    701 	    printf("  %d {$ NP %d0 %d0 $} \"%s%s\" \n",
    702 		   (int) node->addr,
    703 		   node->addr & 0xFF, (node->addr >> 8) & 0xFF,
    704 		   graph_name(node->addr, name, sizeof(name)),
    705 		   node->u.interfaces ? "" : "*");
    706 	    for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
    707 		for (nb = ifc->neighbors; nb; nb = nb->next) {
    708 		    Node *nb_node = find_node(nb->addr, &routers);
    709 		    Neighbor *nb2;
    710 
    711 		    if (nb_node->tries < 0)
    712 			nb_node = nb_node->u.alias;
    713 
    714 		    if (node != nb_node &&
    715 			(!(nb2 = find_neighbor(node->addr, nb_node))
    716 			 || node->addr < nb_node->addr)) {
    717 			printf("    %d \"%d/%d",
    718 			       nb_node->addr, nb->metric, nb->threshold);
    719 			if (nb2 && (nb2->metric != nb->metric
    720 				    || nb2->threshold != nb->threshold))
    721 			    printf(",%d/%d", nb2->metric, nb2->threshold);
    722 			if (nb->flags & NF_PRESENT)
    723 			    printf("%s%s",
    724 				   nb->flags & DVMRP_NF_SRCRT ? "" :
    725 				   nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
    726 				   nb->flags & DVMRP_NF_DOWN ? "D" : "");
    727 			printf("\"\n");
    728 		    }
    729 		}
    730 	    printf("    ;\n");
    731 	}
    732 	graph_edges(node->right);
    733     }
    734 }
    735 
    736 void elide_aliases(Node *node)
    737 {
    738     if (node) {
    739 	elide_aliases(node->left);
    740 	if (node->tries >= 0) {
    741 	    Interface *ifc;
    742 
    743 	    for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
    744 		Neighbor *nb;
    745 
    746 		for (nb = ifc->neighbors; nb; nb = nb->next) {
    747 		    Node *nb_node = find_node(nb->addr, &routers);
    748 
    749 		    if (nb_node->tries < 0)
    750 			nb->addr = nb_node->u.alias->addr;
    751 		}
    752 	    }
    753 	}
    754 	elide_aliases(node->right);
    755     }
    756 }
    757 
    758 void graph_map(void)
    759 {
    760     time_t now = time(0);
    761     char *nowstr = ctime(&now);
    762 
    763     nowstr[24] = '\0';		/* Kill the newline at the end */
    764     elide_aliases(routers);
    765     printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
    766 	   nowstr);
    767     graph_edges(routers);
    768     printf("END\n");
    769 }
    770 
    771 
    772 int get_number(int *var, int deflt, char ***pargv, int *pargc)
    773 {
    774     if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
    775 	if (*pargc > 1  &&  isdigit((unsigned char)(*pargv)[1][0])) {
    776 	    (*pargv)++, (*pargc)--;
    777 	    *var = atoi((*pargv)[0]);
    778 	    return 1;
    779 	} else if (deflt >= 0) {
    780 	    *var = deflt;
    781 	    return 1;
    782 	} else
    783 	    return 0;
    784     } else {			/* Get value from the rest of this argument */
    785 	if (isdigit((unsigned char)(*pargv)[0][2])) {
    786 	    *var = atoi((*pargv)[0] + 2);
    787 	    return 1;
    788 	} else {
    789 	    return 0;
    790 	}
    791     }
    792 }
    793 
    794 
    795 u_int32_t host_addr(char *name)
    796 {
    797     struct hostent *e = gethostbyname(name);
    798     int addr;
    799 
    800     if (e)
    801 	memcpy(&addr, e->h_addr_list[0], e->h_length);
    802     else {
    803 	addr = inet_addr(name);
    804 	if (addr == -1)
    805 	    addr = 0;
    806     }
    807 
    808     return addr;
    809 }
    810 
    811 
    812 int main(int argc, char **argv)
    813 {
    814     int flood = FALSE, graph = FALSE;
    815     struct pollfd set[1];
    816 
    817     setlinebuf(stderr);
    818 
    819     if (geteuid() != 0) {
    820 	fprintf(stderr, "must be root\n");
    821 	exit(1);
    822     }
    823 
    824     argv++, argc--;
    825     while (argc > 0 && argv[0][0] == '-') {
    826 	switch (argv[0][1]) {
    827 	  case 'd':
    828 	    if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
    829 		goto usage;
    830 	    break;
    831 	  case 'f':
    832 	    flood = TRUE;
    833 	    break;
    834 	  case 'g':
    835 	    graph = TRUE;
    836 	    break;
    837 	  case 'n':
    838 	    show_names = FALSE;
    839 	    break;
    840 	  case 'r':
    841 	    if (!get_number(&retries, -1, &argv, &argc))
    842 		goto usage;
    843 	    break;
    844 	  case 't':
    845 	    if (!get_number(&timeout, -1, &argv, &argc))
    846 		goto usage;
    847 	    break;
    848 	  default:
    849 	    goto usage;
    850 	}
    851 	argv++, argc--;
    852     }
    853 
    854     if (argc > 1) {
    855       usage:
    856 	fprintf(stderr,
    857 		"usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n",
    858 		"[-r retries] [-d [debug-level]] [router]");
    859         fprintf(stderr, "\t-f  Flood the routing graph with queries\n");
    860         fprintf(stderr, "\t    (True by default unless `router' is given)\n");
    861         fprintf(stderr, "\t-g  Generate output in GraphEd format\n");
    862         fprintf(stderr, "\t-n  Don't look up DNS names for routers\n");
    863 	exit(1);
    864     } else if (argc == 1 && !(target_addr = host_addr(argv[0]))) {
    865 	fprintf(stderr, "Unknown host: %s\n", argv[0]);
    866 	exit(2);
    867     }
    868 
    869     if (debug)
    870 	fprintf(stderr, "Debug level %u\n", debug);
    871 
    872     init_igmp();
    873 
    874     {				/* Find a good local address for us. */
    875 	int udp;
    876 	struct sockaddr_in addr;
    877 	socklen_t addrlen = sizeof(addr);
    878 
    879 	memset(&addr, 0, sizeof(addr));
    880 	addr.sin_family = AF_INET;
    881 #if (defined(BSD) && (BSD >= 199103))
    882 	addr.sin_len = sizeof addr;
    883 #endif
    884 	addr.sin_addr.s_addr = dvmrp_group;
    885 	addr.sin_port = htons(2000); /* any port over 1024 will do... */
    886 	if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
    887 	    || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
    888 	    || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
    889 	    perror("Determining local address");
    890 	    exit(1);
    891 	}
    892 	close(udp);
    893 	our_addr = addr.sin_addr.s_addr;
    894     }
    895 
    896     /* Send initial seed message to all local routers */
    897     ask(target_addr ? target_addr : allhosts_group);
    898 
    899     if (target_addr) {
    900 	Node *n = find_node(target_addr, &routers);
    901 
    902 	n->tries = 1;
    903 
    904 	if (flood)
    905 	    target_addr = 0;
    906     }
    907 
    908     /* Main receive loop */
    909     set[0].fd = igmp_socket;
    910     set[0].events = POLLIN;
    911     for(;;) {
    912 	int 		count, recvlen;
    913 	socklen_t dummy = 0;
    914 
    915 	count = poll(set, 1, timeout * 1000);
    916 
    917 	if (count < 0) {
    918 	    if (errno != EINTR)
    919 		perror("poll");
    920 	    continue;
    921 	} else if (count == 0) {
    922 	    logit(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
    923 	    if (retry_requests(routers))
    924 		continue;
    925 	    else
    926 		break;
    927 	}
    928 
    929 	recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
    930 			   0, NULL, &dummy);
    931 	if (recvlen >= 0)
    932 	    accept_igmp(recvlen);
    933 	else if (errno != EINTR)
    934 	    perror("recvfrom");
    935     }
    936 
    937     printf("\n");
    938 
    939     if (graph)
    940 	graph_map();
    941     else {
    942 	if (!target_addr)
    943 	    printf("Multicast Router Connectivity:\n\n");
    944 	print_map(routers);
    945     }
    946 
    947     exit(0);
    948 }
    949 
    950 /* dummies */
    951 void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
    952 {
    953 }
    954 void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
    955 {
    956 }
    957 void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
    958 {
    959 }
    960 void add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
    961 {
    962 }
    963 void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
    964 {
    965 }
    966 void accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
    967 		   u_int no, int datalen)
    968 {
    969 }
    970 void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group,
    971 			     int tmo)
    972 {
    973 }
    974 void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
    975 {
    976 }
    977 void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
    978 {
    979 }
    980