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