xref: /netbsd-src/usr.sbin/map-mbone/mapper.c (revision fbffadb9f864c0324fb295860ab0faeb187269cc)
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@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 
find_node(u_int32_t addr,Node ** ptr)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 
find_interface(u_int32_t addr,Node * node)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 
find_neighbor(u_int32_t addr,Node * node)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
logit(int severity,int syserr,const char * format,...)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  */
ask(u_int32_t dst)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 
ask2(u_int32_t dst)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  */
accept_group_report(u_int32_t src,u_int32_t dst,u_int32_t group,int r_type)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  */
accept_probe(u_int32_t src,u_int32_t dst,char * p,int datalen,u_int32_t level)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  */
accept_report(u_int32_t src,u_int32_t dst,char * p,int datalen,u_int32_t level)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  */
accept_neighbor_request(u_int32_t src,u_int32_t dst)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 
accept_neighbor_request2(u_int32_t src,u_int32_t dst)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  */
accept_neighbors(u_int32_t src,u_int32_t dst,u_char * p,int datalen,u_int32_t level)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 
accept_neighbors2(u_int32_t src,u_int32_t dst,u_char * p,int datalen,u_int32_t level)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 
check_vif_state(void)576 void check_vif_state(void)
577 {
578     logit(LOG_NOTICE, 0, "network marked down...");
579 }
580 
581 
retry_requests(Node * node)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 
inet_name(u_int32_t addr)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 
print_map(Node * node)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 
graph_name(u_int32_t addr,char * buf,size_t l)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 
graph_edges(Node * node)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 
elide_aliases(Node * node)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 
graph_map(void)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 
get_number(int * var,int deflt,char *** pargv,int * pargc)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 
host_addr(char * name)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 
main(int argc,char ** argv)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 */
accept_prune(u_int32_t src,u_int32_t dst,char * p,int datalen)951 void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
952 {
953 }
accept_graft(u_int32_t src,u_int32_t dst,char * p,int datalen)954 void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
955 {
956 }
accept_g_ack(u_int32_t src,u_int32_t dst,char * p,int datalen)957 void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
958 {
959 }
add_table_entry(u_int32_t origin,u_int32_t mcastgrp)960 void add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
961 {
962 }
accept_leave_message(u_int32_t src,u_int32_t dst,u_int32_t group)963 void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
964 {
965 }
accept_mtrace(u_int32_t src,u_int32_t dst,u_int32_t group,char * data,u_int no,int datalen)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 }
accept_membership_query(u_int32_t src,u_int32_t dst,u_int32_t group,int tmo)970 void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group,
971 			     int tmo)
972 {
973 }
accept_info_request(u_int32_t src,u_int32_t dst,u_char * p,int datalen)974 void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
975 {
976 }
accept_info_reply(u_int32_t src,u_int32_t dst,u_char * p,int datalen)977 void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
978 {
979 }
980