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