1/*	$NetBSD: mapper.c,v 1.24 2007/02/22 01:25:13 hubertf 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
53typedef 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
62typedef 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
68typedef 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
80Node   *routers = 0;
81u_int32_t	our_addr, target_addr = 0;		/* in NET order */
82int	debug = 0;
83int	retries = DEFAULT_RETRIES;
84int	timeout = DEFAULT_TIMEOUT;
85int	show_names = TRUE;
86vifi_t  numvifs;		/* to keep loader happy */
87				/* (see COPY_TABLES macro called in kern.c) */
88
89Node *			find_node(u_int32_t addr, Node **ptr);
90Interface *		find_interface(u_int32_t addr, Node *node);
91Neighbor *		find_neighbor(u_int32_t addr, Node *node);
92int			main(int argc, char *argv[]);
93void			ask(u_int32_t dst);
94void			ask2(u_int32_t dst);
95int			retry_requests(Node *node);
96char *			inet_name(u_int32_t addr);
97void			print_map(Node *node);
98char *			graph_name(u_int32_t addr, char *buf, size_t);
99void			graph_edges(Node *node);
100void			elide_aliases(Node *node);
101void			graph_map(void);
102int			get_number(int *var, int deflt, char ***pargv,
103				   int *pargc);
104u_int32_t		host_addr(char *name);
105
106void logit(int severity, int syserr, const char *format, ...)
107	__attribute__((__format__(__printf__, 3, 4)));
108
109Node *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
130Interface *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
148Neighbor *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 */
169void
170logit(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 */
202void ask(u_int32_t dst)
203{
204    send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
205		htonl(MROUTED_LEVEL), 0);
206}
207
208void 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 */
218void 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 */
228void 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 */
239void 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 */
250void 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
258void 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 */
270void 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
429void 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
574void check_vif_state(void)
575{
576    logit(LOG_NOTICE, 0, "network marked down...");
577}
578
579
580int 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
600char *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
610void 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
677char *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
690void 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
734void 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
756void 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
770int 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
793u_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
810int 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 */
949void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
950{
951}
952void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
953{
954}
955void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
956{
957}
958void add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
959{
960}
961void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
962{
963}
964void accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
965		   u_int no, int datalen)
966{
967}
968void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group,
969			     int tmo)
970{
971}
972void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
973{
974}
975void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
976{
977}
978