Merge from vendor branch BZIP:
[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.4 2004/03/15 18:10:28 dillon 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 #include <stdarg.h>
34
35 #define DEFAULT_TIMEOUT 2       /* How long to wait before retrying requests */
36 #define DEFAULT_RETRIES 1       /* How many times to ask each router */
37
38
39 /* All IP addresses are stored in the data structure in NET order. */
40
41 typedef struct neighbor {
42     struct neighbor    *next;
43     u_int32             addr;           /* IP address in NET order */
44     u_char              metric;         /* TTL cost of forwarding */
45     u_char              threshold;      /* TTL threshold to forward */
46     u_short             flags;          /* flags on connection */
47 #define NF_PRESENT 0x8000       /* True if flags are meaningful */
48 } Neighbor;
49
50 typedef struct interface {
51     struct interface *next;
52     u_int32     addr;           /* IP address of the interface in NET order */
53     Neighbor   *neighbors;      /* List of neighbors' IP addresses */
54 } Interface;
55
56 typedef struct node {
57     u_int32     addr;           /* IP address of this entry in NET order */
58     u_int32     version;        /* which mrouted version is running */
59     int         tries;          /* How many requests sent?  -1 for aliases */
60     union {
61         struct node *alias;             /* If alias, to what? */
62         struct interface *interfaces;   /* Else, neighbor data */
63     } u;
64     struct node *left, *right;
65 } Node;
66
67
68 Node   *routers = 0;
69 u_int32 our_addr, target_addr = 0;              /* in NET order */
70 int     debug = 0;
71 int     retries = DEFAULT_RETRIES;
72 int     timeout = DEFAULT_TIMEOUT;
73 int     show_names = TRUE;
74 vifi_t  numvifs;                /* to keep loader happy */
75                                 /* (see COPY_TABLES macro called in kern.c) */
76
77 Node *                  find_node(u_int32 addr, Node **ptr);
78 Interface *             find_interface(u_int32 addr, Node *node);
79 Neighbor *              find_neighbor(u_int32 addr, Node *node);
80 int                     main(int argc, char **argv);
81 void                    ask(u_int32 dst);
82 void                    ask2(u_int32 dst);
83 int                     retry_requests(Node *node);
84 char *                  inet_name(u_int32 addr);
85 void                    print_map(Node *node);
86 char *                  graph_name(u_int32 addr, char *buf, int len);
87 void                    graph_edges(Node *node);
88 void                    elide_aliases(Node *node);
89 void                    graph_map(void);
90 int                     get_number(int *var, int deflt, char ***pargv,
91                                                 int *pargc);
92 u_int32                 host_addr(char *name);
93 static void             usage(void);
94
95 Node *
96 find_node(u_int32 addr, Node **ptr)
97 {
98     Node *n = *ptr;
99
100     if (!n) {
101         *ptr = n = (Node *) malloc(sizeof(Node));
102         n->addr = addr;
103         n->version = 0;
104         n->tries = 0;
105         n->u.interfaces = 0;
106         n->left = n->right = 0;
107         return n;
108     } else if (addr == n->addr)
109         return n;
110     else if (addr < n->addr)
111         return find_node(addr, &(n->left));
112     else
113         return find_node(addr, &(n->right));
114 }
115
116 Interface *
117 find_interface(u_int32 addr, Node *node)
118 {
119     Interface *ifc;
120
121     for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
122         if (ifc->addr == addr)
123             return ifc;
124
125     ifc = (Interface *) malloc(sizeof(Interface));
126     ifc->addr = addr;
127     ifc->next = node->u.interfaces;
128     node->u.interfaces = ifc;
129     ifc->neighbors = 0;
130
131     return ifc;
132 }
133
134 Neighbor *
135 find_neighbor(u_int32 addr, Node *node)
136 {
137     Interface *ifc;
138
139     for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
140         Neighbor *nb;
141
142         for (nb = ifc->neighbors; nb; nb = nb->next)
143             if (nb->addr == addr)
144                 return nb;
145     }
146
147     return 0;
148 }
149
150
151 /*
152  * Log errors and other messages to stderr, according to the severity of the
153  * message and the current debug level.  For errors of severity LOG_ERR or
154  * worse, terminate the program.
155  */
156 void
157 log(int severity, int syserr, char *format, ...)
158 {
159         va_list ap;
160         char    fmt[100];
161
162         va_start(ap, format);
163
164     switch (debug) {
165         case 0: if (severity > LOG_WARNING) return;
166         case 1: if (severity > LOG_NOTICE ) return;
167         case 2: if (severity > LOG_INFO   ) return;
168         default:
169             fmt[0] = '\0';
170             if (severity == LOG_WARNING)
171                 strcpy(fmt, "warning - ");
172             strncat(fmt, format, sizeof(fmt)-strlen(fmt));
173             fmt[sizeof(fmt)-1]='\0';
174             vfprintf(stderr, fmt, ap);
175             if (syserr == 0)
176                 fprintf(stderr, "\n");
177             else if (syserr < sys_nerr)
178                 fprintf(stderr, ": %s\n", sys_errlist[syserr]);
179             else
180                 fprintf(stderr, ": errno %d\n", syserr);
181     }
182
183     if (severity <= LOG_ERR)
184         exit(1);
185 }
186
187
188 /*
189  * Send a neighbors-list request.
190  */
191 void
192 ask(u_int32 dst)
193 {
194
195     send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
196         htonl(MROUTED_LEVEL), 0);
197 }
198
199 void
200 ask2(u_int32 dst)
201 {
202
203     send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
204         htonl(MROUTED_LEVEL), 0);
205 }
206
207
208 /*
209  * Process an incoming group membership report.
210  */
211 void
212 accept_group_report(u_int32 src, u_int32 dst, u_int32 group, int r_type)
213 {
214
215     log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
216         inet_fmt(src, s1), inet_fmt(dst, s2));
217 }
218
219
220 /*
221  * Process an incoming neighbor probe message.
222  */
223 void
224 accept_probe(u_int32 src, u_int32 dst, char *p, int datalen, u_int32 level)
225 {
226     log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
227         inet_fmt(src, s1), inet_fmt(dst, s2));
228 }
229
230
231 /*
232  * Process an incoming route report message.
233  */
234 void
235 accept_report(u_int32 src, u_int32 dst, char *p, int datalen, u_int32 level)
236 {
237     log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
238         inet_fmt(src, s1), inet_fmt(dst, s2));
239 }
240
241
242 /*
243  * Process an incoming neighbor-list request message.
244  */
245 void
246 accept_neighbor_request(u_int32 src, u_int32 dst)
247 {
248     if (src != our_addr) {
249         log(LOG_INFO, 0,
250             "ignoring spurious DVMRP neighbor request from %s to %s",
251             inet_fmt(src, s1), inet_fmt(dst, s2));
252     }
253 }
254
255 void
256 accept_neighbor_request2(u_int32 src, u_int32 dst)
257 {
258     if (src != our_addr) {
259         log(LOG_INFO, 0,
260             "ignoring spurious DVMRP neighbor request2 from %s to %s",
261             inet_fmt(src, s1), inet_fmt(dst, s2));
262     }
263 }
264
265 /*
266  * Process an incoming neighbor-list message.
267  */
268 void
269 accept_neighbors(u_int32 src, u_int32 dst, u_char *p, int datalen,
270                       u_int32 level)
271 {
272     Node       *node = find_node(src, &routers);
273
274     if (node->tries == 0)       /* Never heard of 'em; must have hit them at */
275         node->tries = 1;        /* least once, though...*/
276     else if (node->tries == -1) /* follow alias link */
277         node = node->u.alias;
278
279 #define GET_ADDR(a) (a = ((u_int32)*p++ << 24), a += ((u_int32)*p++ << 16),\
280                      a += ((u_int32)*p++ << 8), a += *p++)
281
282     /* if node is running a recent mrouted, ask for additional info */
283     if (level != 0) {
284         node->version = level;
285         node->tries = 1;
286         ask2(src);
287         return;
288     }
289
290     if (debug > 3) {
291         int i;
292
293         fprintf(stderr, "    datalen = %d\n", datalen);
294         for (i = 0; i < datalen; i++) {
295             if ((i & 0xF) == 0)
296                 fprintf(stderr, "   ");
297             fprintf(stderr, " %02x", p[i]);
298             if ((i & 0xF) == 0xF)
299                 fprintf(stderr, "\n");
300         }
301         if ((datalen & 0xF) != 0xF)
302             fprintf(stderr, "\n");
303     }
304
305     while (datalen > 0) {       /* loop through interfaces */
306         u_int32         ifc_addr;
307         u_char          metric, threshold, ncount;
308         Node           *ifc_node;
309         Interface      *ifc;
310         Neighbor       *old_neighbors;
311
312         if (datalen < 4 + 3) {
313             log(LOG_WARNING, 0, "received truncated interface record from %s",
314                 inet_fmt(src, s1));
315             return;
316         }
317
318         GET_ADDR(ifc_addr);
319         ifc_addr = htonl(ifc_addr);
320         metric = *p++;
321         threshold = *p++;
322         ncount = *p++;
323         datalen -= 4 + 3;
324
325         /* Fix up any alias information */
326         ifc_node = find_node(ifc_addr, &routers);
327         if (ifc_node->tries == 0) { /* new node */
328             ifc_node->tries = -1;
329             ifc_node->u.alias = node;
330         } else if (ifc_node != node
331                    && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
332             /* must merge two hosts' nodes */
333             Interface  *ifc_i, *next_ifc_i;
334
335             if (ifc_node->tries == -1) {
336                 Node *tmp = ifc_node->u.alias;
337
338                 ifc_node->u.alias = node;
339                 ifc_node = tmp;
340             }
341
342             /* Merge ifc_node (foo_i) into node (foo_n) */
343
344             if (ifc_node->tries > node->tries)
345                 node->tries = ifc_node->tries;
346
347             for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
348                 Neighbor *nb_i, *next_nb_i, *nb_n;
349                 Interface *ifc_n = find_interface(ifc_i->addr, node);
350
351                 old_neighbors = ifc_n->neighbors;
352                 for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
353                     next_nb_i = nb_i->next;
354                     for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
355                         if (nb_i->addr == nb_n->addr) {
356                             if (nb_i->metric != nb_n->metric
357                                 || nb_i->threshold != nb_n->threshold)
358                                 log(LOG_WARNING, 0,
359                                     "inconsistent %s for neighbor %s of %s",
360                                     "metric/threshold",
361                                     inet_fmt(nb_i->addr, s1),
362                                     inet_fmt(node->addr, s2));
363                             free(nb_i);
364                             break;
365                         }
366                     if (!nb_n) { /* no match for this neighbor yet */
367                         nb_i->next = ifc_n->neighbors;
368                         ifc_n->neighbors = nb_i;
369                     }
370                 }
371
372                 next_ifc_i = ifc_i->next;
373                 free(ifc_i);
374             }
375
376             ifc_node->tries = -1;
377             ifc_node->u.alias = node;
378         }
379         
380         ifc = find_interface(ifc_addr, node);
381         old_neighbors = ifc->neighbors;
382         
383         /* Add the neighbors for this interface */
384         while (ncount--) {
385             u_int32     neighbor;
386             Neighbor   *nb;
387             Node       *n_node;
388
389             if (datalen < 4) {
390                 log(LOG_WARNING, 0, "received truncated neighbor list from %s",
391                     inet_fmt(src, s1));
392                 return;
393             }
394
395             GET_ADDR(neighbor);
396             neighbor = htonl(neighbor);
397             datalen -= 4;
398
399             for (nb = old_neighbors; nb; nb = nb->next)
400                 if (nb->addr == neighbor) {
401                     if (metric != nb->metric || threshold != nb->threshold)
402                         log(LOG_WARNING, 0,
403                             "inconsistent %s for neighbor %s of %s",
404                             "metric/threshold",
405                             inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
406                     goto next_neighbor;
407                 }
408
409             nb = (Neighbor *) malloc(sizeof(Neighbor));
410             nb->next = ifc->neighbors;
411             ifc->neighbors = nb;
412             nb->addr = neighbor;
413             nb->metric = metric;
414             nb->threshold = threshold;
415             nb->flags = 0;
416
417             n_node = find_node(neighbor, &routers);
418             if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
419                 ask(neighbor);
420                 n_node->tries = 1;
421             }
422
423           next_neighbor: ;
424         }
425     }
426 }
427
428 void
429 accept_neighbors2(u_int32 src, u_int32 dst, u_char *p, int datalen,
430                        u_int32 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         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             log(LOG_WARNING, 0, "received truncated interface record from %s",
450                 inet_fmt(src, s1));
451             return;
452         }
453
454         ifc_addr = *(u_int32*)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                                 log(LOG_WARNING, 0,
501                                     "inconsistent %s for neighbor %s of %s",
502                                     "metric/threshold",
503                                     inet_fmt(nb_i->addr, s1),
504                                     inet_fmt(node->addr, s2));
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     neighbor;
528             Neighbor   *nb;
529             Node       *n_node;
530
531             if (datalen < 4) {
532                 log(LOG_WARNING, 0, "received truncated neighbor list from %s",
533                     inet_fmt(src, s1));
534                 return;
535             }
536
537             neighbor = *(u_int32*)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                         log(LOG_WARNING, 0,
548                             "inconsistent %s for neighbor %s of %s",
549                             "metric/threshold",
550                             inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
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 void
574 check_vif_state(void)
575 {
576     log(LOG_NOTICE, 0, "network marked down...");
577 }
578
579 int
580 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 char *
600 inet_name(u_int32 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 void
610 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, s1);
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, s1));
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, s1);
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, s1));
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 char *
677 graph_name(u_int32 addr, char *buf, int len)
678 {
679     char *name;
680
681     if (len < sizeof("255.255.255.255")) {
682         fprintf(stderr, 
683 "Buffer too small in graph_name, provided %d bytes, but needed %d.\n", 
684             len, sizeof("255.255.255.255"));
685         return NULL;
686     }
687     if (show_names  &&  (name = inet_name(addr))) {
688         strncpy(buf, name, len - 1);
689         buf[len - 1] = '\0';
690     } else
691         inet_fmt(addr, buf);
692
693     return buf;
694 }
695
696 void
697 graph_edges(Node *node)
698 {
699     Interface *ifc;
700     Neighbor *nb;
701     char name[100];
702
703     if (node) {
704         graph_edges(node->left);
705         if (node->tries >= 0) {
706             printf("  %d {$ NP %d0 %d0 $} \"%s%s\" \n",
707                    (int) node->addr,
708                    node->addr & 0xFF, (node->addr >> 8) & 0xFF,
709                    graph_name(node->addr, name, sizeof(name)),
710                    node->u.interfaces ? "" : "*");
711             for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
712                 for (nb = ifc->neighbors; nb; nb = nb->next) {
713                     Node *nb_node = find_node(nb->addr, &routers);
714                     Neighbor *nb2;
715
716                     if (nb_node->tries < 0)
717                         nb_node = nb_node->u.alias;
718
719                     if (node != nb_node &&
720                         (!(nb2 = find_neighbor(node->addr, nb_node))
721                          || node->addr < nb_node->addr)) {
722                         printf("    %d \"%d/%d",
723                                nb_node->addr, nb->metric, nb->threshold);
724                         if (nb2 && (nb2->metric != nb->metric
725                                     || nb2->threshold != nb->threshold))
726                             printf(",%d/%d", nb2->metric, nb2->threshold);
727                         if (nb->flags & NF_PRESENT)
728                             printf("%s%s",
729                                    nb->flags & DVMRP_NF_SRCRT ? "" :
730                                    nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
731                                    nb->flags & DVMRP_NF_DOWN ? "D" : "");
732                         printf("\"\n");
733                     }
734                 }
735             printf("    ;\n");
736         }
737         graph_edges(node->right);
738     }
739 }
740
741 void
742 elide_aliases(Node *node)
743 {
744     if (node) {
745         elide_aliases(node->left);
746         if (node->tries >= 0) {
747             Interface *ifc;
748
749             for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
750                 Neighbor *nb;
751
752                 for (nb = ifc->neighbors; nb; nb = nb->next) {
753                     Node *nb_node = find_node(nb->addr, &routers);
754
755                     if (nb_node->tries < 0)
756                         nb->addr = nb_node->u.alias->addr;
757                 }
758             }
759         }
760         elide_aliases(node->right);
761     }
762 }
763
764 void
765 graph_map(void)
766 {
767     time_t now;
768     char *nowstr;
769
770     now = time(0);
771     nowstr = ctime(&now);
772     nowstr[24] = '\0';          /* Kill the newline at the end */
773     elide_aliases(routers);
774     printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
775            nowstr);
776     graph_edges(routers);
777     printf("END\n");
778 }
779
780 int
781 get_number(int *var, int deflt, char ***pargv, int *pargc)
782 {
783     if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
784         if (*pargc > 1  &&  isdigit((*pargv)[1][0])) {
785             (*pargv)++, (*pargc)--;
786             *var = atoi((*pargv)[0]);
787             return 1;
788         } else if (deflt >= 0) {
789             *var = deflt;
790             return 1;
791         } else
792             return 0;
793     } else {                    /* Get value from the rest of this argument */
794         if (isdigit((*pargv)[0][2])) {
795             *var = atoi((*pargv)[0] + 2);
796             return 1;
797         } else {
798             return 0;
799         }
800     }
801 }
802
803 u_int32
804 host_addr(char *name)
805 {
806     struct hostent *e = gethostbyname(name);
807     int addr;
808
809     if (e && e->h_length == sizeof(addr))
810         memcpy(&addr, e->h_addr_list[0], e->h_length);
811     else {
812         addr = inet_addr(name);
813         if (addr == -1)
814             addr = 0;
815     }
816
817     return addr;
818 }
819
820 int 
821 main(int argc, char **argv)
822 {
823     int flood = FALSE, graph = FALSE;
824     
825     if (geteuid() != 0)
826         errx(1, "must be root");
827
828     init_igmp();
829     setuid(getuid());
830
831     setlinebuf(stderr);
832
833     argv++, argc--;
834     while (argc > 0 && argv[0][0] == '-') {
835         switch (argv[0][1]) {
836           case 'd':
837             if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
838                 usage();
839             break;
840           case 'f':
841             flood = TRUE;
842             break;
843           case 'g':
844             graph = TRUE;
845             break;
846           case 'n':
847             show_names = FALSE;
848             break;
849           case 'r':
850             if (!get_number(&retries, -1, &argv, &argc))
851                 usage();
852             break;
853           case 't':
854             if (!get_number(&timeout, -1, &argv, &argc))
855                 usage();
856             break;
857           default:
858             usage();
859         }
860         argv++, argc--;
861     }
862
863     if (argc > 1) {
864         usage();
865     } else if (argc == 1 && !(target_addr = host_addr(argv[0])))
866         errx(2, "unknown host: %s", argv[0]);
867
868     if (debug)
869         fprintf(stderr, "Debug level %u\n", debug);
870
871     {                           /* Find a good local address for us. */
872         int udp;
873         struct sockaddr_in addr;
874         int addrlen = sizeof(addr);
875
876         addr.sin_family = AF_INET;
877 #ifdef HAVE_SA_LEN
878         addr.sin_len = sizeof addr;
879 #endif
880         addr.sin_addr.s_addr = dvmrp_group;
881         addr.sin_port = htons(2000); /* any port over 1024 will do... */
882         if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
883             || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
884             || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0)
885             err(-1, "determining local address");
886         close(udp);
887         our_addr = addr.sin_addr.s_addr;
888     }
889
890     /* Send initial seed message to all local routers */
891     ask(target_addr ? target_addr : allhosts_group);
892
893     if (target_addr) {
894         Node *n = find_node(target_addr, &routers);
895
896         n->tries = 1;
897
898         if (flood)
899             target_addr = 0;
900     }
901
902     /* Main receive loop */
903     for(;;) {
904         fd_set          fds;
905         struct timeval  tv;
906         int             count, recvlen, dummy = 0;
907
908         if (igmp_socket >= FD_SETSIZE)
909                 log(LOG_ERR, 0, "descriptor too big");
910         FD_ZERO(&fds);
911         FD_SET(igmp_socket, &fds);
912
913         tv.tv_sec = timeout;
914         tv.tv_usec = 0;
915
916         count = select(igmp_socket + 1, &fds, 0, 0, &tv);
917
918         if (count < 0) {
919             if (errno != EINTR)
920                 warn("select");
921             continue;
922         } else if (count == 0) {
923             log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
924             if (retry_requests(routers))
925                 continue;
926             else
927                 break;
928         }
929
930         recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
931                            0, NULL, &dummy);
932         if (recvlen >= 0)
933             accept_igmp(recvlen);
934         else if (errno != EINTR)
935             warn("recvfrom");
936     }
937
938     printf("\n");
939
940     if (graph)
941         graph_map();
942     else {
943         if (!target_addr)
944             printf("Multicast Router Connectivity:\n\n");
945         print_map(routers);
946     }
947
948     exit(0);
949 }
950
951 static void
952 usage(void)
953 {
954         fprintf(stderr, "%s\n%s\n",
955         "usage: map-mbone [-f] [-g] [-n] [-t timeout] [-r retries]",
956         "                 [-d [debug-level]] [router]");
957         exit(1);
958 }
959
960 /* dummies */
961 void
962 accept_prune(u_int32 src, u_int32 dst, char *p, int datalen)
963 {
964 }
965
966 void
967 accept_graft(u_int32 src, u_int32 dst, char *p, int datalen)
968 {
969 }
970
971 void
972 accept_g_ack(u_int32 src, u_int32 dst, char *p, int datalen)
973 {
974 }
975
976 void
977 add_table_entry(u_int32 origin, u_int32 mcastgrp)
978 {
979 }
980
981 void
982 accept_leave_message(u_int32 src, u_int32 dst, u_int32 group)
983 {
984 }
985
986 void
987 accept_mtrace(u_int32 src, u_int32 dst, u_int32 group,
988                    char *data, u_int no, int datalen)
989 {
990 }
991
992 void
993 accept_membership_query(u_int32 src, u_int32 dst, u_int32 group, int tmo)
994 {
995 }
996
997 void
998 accept_info_request(u_int32 src, u_int32 dst, u_char *p, int datalen)
999 {
1000 }
1001
1002 void
1003 accept_info_reply(u_int32 src, u_int32 dst, u_char *p, int datalen)
1004 {
1005 }