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