1 /* Mapper for connections between MRouteD multicast routers.
2 * Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
4 * $Id: mapper.c,v 1.8 1994/08/24 23:53:54 thyagara Exp $
8 * Copyright (c) Xerox Corporation 1992. All rights reserved.
10 * License is granted to copy, to use, and to make and to use derivative
11 * works for research and evaluation purposes, provided that Xerox is
12 * acknowledged in all documentation pertaining to any such copy or derivative
13 * work. Xerox grants no other licenses expressed or implied. The Xerox trade
14 * name should not be used in any advertising without its written permission.
16 * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
17 * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
18 * FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
19 * express or implied warranty of any kind.
21 * These notices must be retained in any copies of any part of this software.
28 #define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */
29 #define DEFAULT_RETRIES 1 /* How many times to ask each router */
32 /* All IP addresses are stored in the data structure in NET order. */
34 typedef struct neighbor {
35 struct neighbor *next;
36 u_long addr; /* IP address in NET order */
37 u_char metric; /* TTL cost of forwarding */
38 u_char threshold; /* TTL threshold to forward */
39 u_short flags; /* flags on connection */
40 #define NF_PRESENT 0x8000 /* True if flags are meaningful */
43 typedef struct interface {
44 struct interface *next;
45 u_long addr; /* IP address of the interface in NET order */
46 Neighbor *neighbors; /* List of neighbors' IP addresses */
50 u_long addr; /* IP address of this entry in NET order */
51 u_long version; /* which mrouted version is running */
52 int tries; /* How many requests sent? -1 for aliases */
54 struct node *alias; /* If alias, to what? */
55 struct interface *interfaces; /* Else, neighbor data */
57 struct node *left, *right;
62 u_long our_addr, target_addr = 0; /* in NET order */
64 int retries = DEFAULT_RETRIES;
65 int timeout = DEFAULT_TIMEOUT;
66 int show_names = TRUE;
67 vifi_t numvifs; /* to keep loader happy */
68 /* (see COPY_TABLES macro called in kern.c) */
71 Node *find_node(addr, ptr)
78 *ptr = n = (Node *) malloc(sizeof(Node));
83 n->left = n->right = 0;
85 } else if (addr == n->addr)
87 else if (addr < n->addr)
88 return find_node(addr, &(n->left));
90 return find_node(addr, &(n->right));
94 Interface *find_interface(addr, node)
100 for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
101 if (ifc->addr == addr)
104 ifc = (Interface *) malloc(sizeof(Interface));
106 ifc->next = node->u.interfaces;
107 node->u.interfaces = ifc;
114 Neighbor *find_neighbor(addr, node)
120 for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
123 for (nb = ifc->neighbors; nb; nb = nb->next)
124 if (nb->addr == addr)
133 * Log errors and other messages to stderr, according to the severity of the
134 * message and the current debug level. For errors of severity LOG_ERR or
135 * worse, terminate the program.
137 void log(severity, syserr, format, a, b, c, d, e)
138 int severity, syserr;
145 case 0: if (severity > LOG_WARNING) return;
146 case 1: if (severity > LOG_NOTICE ) return;
147 case 2: if (severity > LOG_INFO ) return;
150 if (severity == LOG_WARNING)
151 strcat(fmt, "warning - ");
152 strncat(fmt, format, 80);
153 fprintf(stderr, fmt, a, b, c, d, e);
155 fprintf(stderr, "\n");
156 else if (syserr < sys_nerr)
157 fprintf(stderr, ": %s\n", sys_errlist[syserr]);
159 fprintf(stderr, ": errno %d\n", syserr);
162 if (severity <= LOG_ERR)
168 * Send a neighbors-list request.
173 send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
174 htonl(MROUTED_LEVEL), 0);
180 send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
181 htonl(MROUTED_LEVEL), 0);
186 * Process an incoming group membership report.
188 void accept_group_report(src, dst, group)
189 u_long src, dst, group;
191 log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
192 inet_fmt(src, s1), inet_fmt(dst, s2));
197 * Process an incoming neighbor probe message.
199 void accept_probe(src, dst)
202 log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
203 inet_fmt(src, s1), inet_fmt(dst, s2));
208 * Process an incoming route report message.
210 void accept_report(src, dst, p, datalen)
215 log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
216 inet_fmt(src, s1), inet_fmt(dst, s2));
221 * Process an incoming neighbor-list request message.
223 void accept_neighbor_request(src, dst)
228 "ignoring spurious DVMRP neighbor request from %s to %s",
229 inet_fmt(src, s1), inet_fmt(dst, s2));
232 void accept_neighbor_request2(src, dst)
237 "ignoring spurious DVMRP neighbor request2 from %s to %s",
238 inet_fmt(src, s1), inet_fmt(dst, s2));
243 * Process an incoming neighbor-list message.
245 void accept_neighbors(src, dst, p, datalen, level)
246 u_long src, dst, level;
250 Node *node = find_node(src, &routers);
252 if (node->tries == 0) /* Never heard of 'em; must have hit them at */
253 node->tries = 1; /* least once, though...*/
254 else if (node->tries == -1) /* follow alias link */
255 node = node->u.alias;
257 #define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
258 a += ((u_long)*p++ << 8), a += *p++)
260 /* if node is running a recent mrouted, ask for additional info */
262 node->version = ntohl(level);
271 fprintf(stderr, " datalen = %d\n", datalen);
272 for (i = 0; i < datalen; i++) {
274 fprintf(stderr, " ");
275 fprintf(stderr, " %02x", p[i]);
276 if ((i & 0xF) == 0xF)
277 fprintf(stderr, "\n");
279 if ((datalen & 0xF) != 0xF)
280 fprintf(stderr, "\n");
283 while (datalen > 0) { /* loop through interfaces */
285 u_char metric, threshold, ncount;
288 Neighbor *old_neighbors;
290 if (datalen < 4 + 3) {
291 log(LOG_WARNING, 0, "received truncated interface record from %s",
297 ifc_addr = htonl(ifc_addr);
303 /* Fix up any alias information */
304 ifc_node = find_node(ifc_addr, &routers);
305 if (ifc_node->tries == 0) { /* new node */
306 ifc_node->tries = -1;
307 ifc_node->u.alias = node;
308 } else if (ifc_node != node
309 && (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
310 /* must merge two hosts' nodes */
311 Interface *ifc_i, *next_ifc_i;
313 if (ifc_node->tries == -1) {
314 Node *tmp = ifc_node->u.alias;
316 ifc_node->u.alias = node;
320 /* Merge ifc_node (foo_i) into node (foo_n) */
322 if (ifc_node->tries > node->tries)
323 node->tries = ifc_node->tries;
325 for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
326 Neighbor *nb_i, *next_nb_i, *nb_n;
327 Interface *ifc_n = find_interface(ifc_i->addr, node);
329 old_neighbors = ifc_n->neighbors;
330 for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
331 next_nb_i = nb_i->next;
332 for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
333 if (nb_i->addr == nb_n->addr) {
334 if (nb_i->metric != nb_n->metric
335 || nb_i->threshold != nb_i->threshold)
337 "inconsistent %s for neighbor %s of %s",
339 inet_fmt(nb_i->addr, s1),
340 inet_fmt(node->addr, s2));
344 if (!nb_n) { /* no match for this neighbor yet */
345 nb_i->next = ifc_n->neighbors;
346 ifc_n->neighbors = nb_i;
350 next_ifc_i = ifc_i->next;
354 ifc_node->tries = -1;
355 ifc_node->u.alias = node;
358 ifc = find_interface(ifc_addr, node);
359 old_neighbors = ifc->neighbors;
361 /* Add the neighbors for this interface */
368 log(LOG_WARNING, 0, "received truncated neighbor list from %s",
374 neighbor = htonl(neighbor);
377 for (nb = old_neighbors; nb; nb = nb->next)
378 if (nb->addr == neighbor) {
379 if (metric != nb->metric || threshold != nb->threshold)
381 "inconsistent %s for neighbor %s of %s",
383 inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
387 nb = (Neighbor *) malloc(sizeof(Neighbor));
388 nb->next = ifc->neighbors;
392 nb->threshold = threshold;
395 n_node = find_node(neighbor, &routers);
396 if (n_node->tries == 0 && !target_addr) { /* it's a new router */
406 void accept_neighbors2(src, dst, p, datalen)
411 Node *node = find_node(src, &routers);
413 if (node->tries == 0) /* Never heard of 'em; must have hit them at */
414 node->tries = 1; /* least once, though...*/
415 else if (node->tries == -1) /* follow alias link */
416 node = node->u.alias;
418 while (datalen > 0) { /* loop through interfaces */
420 u_char metric, threshold, ncount, flags;
423 Neighbor *old_neighbors;
425 if (datalen < 4 + 4) {
426 log(LOG_WARNING, 0, "received truncated interface record from %s",
431 ifc_addr = *(u_long*)p;
439 /* Fix up any alias information */
440 ifc_node = find_node(ifc_addr, &routers);
441 if (ifc_node->tries == 0) { /* new node */
442 ifc_node->tries = -1;
443 ifc_node->u.alias = node;
444 } else if (ifc_node != node
445 && (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
446 /* must merge two hosts' nodes */
447 Interface *ifc_i, *next_ifc_i;
449 if (ifc_node->tries == -1) {
450 Node *tmp = ifc_node->u.alias;
452 ifc_node->u.alias = node;
456 /* Merge ifc_node (foo_i) into node (foo_n) */
458 if (ifc_node->tries > node->tries)
459 node->tries = ifc_node->tries;
461 for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
462 Neighbor *nb_i, *next_nb_i, *nb_n;
463 Interface *ifc_n = find_interface(ifc_i->addr, node);
465 old_neighbors = ifc_n->neighbors;
466 for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
467 next_nb_i = nb_i->next;
468 for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
469 if (nb_i->addr == nb_n->addr) {
470 if (nb_i->metric != nb_n->metric
471 || nb_i->threshold != nb_i->threshold)
473 "inconsistent %s for neighbor %s of %s",
475 inet_fmt(nb_i->addr, s1),
476 inet_fmt(node->addr, s2));
480 if (!nb_n) { /* no match for this neighbor yet */
481 nb_i->next = ifc_n->neighbors;
482 ifc_n->neighbors = nb_i;
486 next_ifc_i = ifc_i->next;
490 ifc_node->tries = -1;
491 ifc_node->u.alias = node;
494 ifc = find_interface(ifc_addr, node);
495 old_neighbors = ifc->neighbors;
497 /* Add the neighbors for this interface */
504 log(LOG_WARNING, 0, "received truncated neighbor list from %s",
509 neighbor = *(u_long*)p;
513 /* make leaf nets point to themselves */
516 for (nb = old_neighbors; nb; nb = nb->next)
517 if (nb->addr == neighbor) {
518 if (metric != nb->metric || threshold != nb->threshold)
520 "inconsistent %s for neighbor %s of %s",
522 inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
526 nb = (Neighbor *) malloc(sizeof(Neighbor));
527 nb->next = ifc->neighbors;
531 nb->threshold = threshold;
532 nb->flags = flags | NF_PRESENT;
534 n_node = find_node(neighbor, &routers);
535 if (n_node->tries == 0 && !target_addr) { /* it's a new router */
546 void check_vif_state()
548 log(LOG_NOTICE, 0, "network marked down...");
552 int retry_requests(node)
558 result = retry_requests(node->left);
559 if (node->tries > 0 && node->tries < retries) {
567 return retry_requests(node->right) || result;
573 char *inet_name(addr)
578 e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
580 return e ? e->h_name : 0;
590 print_map(node->left);
592 addr = inet_fmt(node->addr, s1);
594 || (node->tries >= 0 && node->u.interfaces)
595 || (node->tries == -1
596 && node->u.alias->tries >= 0
597 && node->u.alias->u.interfaces)) {
598 if (show_names && (name = inet_name(node->addr)))
599 printf("%s (%s):", addr, name);
603 printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1));
604 else if (!node->u.interfaces)
605 printf(" no response to query\n\n");
610 printf(" <v%d.%d>", node->version & 0xff,
611 (node->version >> 8) & 0xff);
613 for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
615 char *ifc_name = inet_fmt(ifc->addr, s1);
616 int ifc_len = strlen(ifc_name);
619 printf(" %s:", ifc_name);
620 for (nb = ifc->neighbors; nb; nb = nb->next) {
622 printf("%*s", ifc_len + 5, "");
623 printf(" %s", inet_fmt(nb->addr, s1));
624 if (show_names && (name = inet_name(nb->addr)))
625 printf(" (%s)", name);
626 printf(" [%d/%d", nb->metric, nb->threshold);
628 u_short flags = nb->flags;
629 if (flags & DVMRP_NF_TUNNEL)
631 if (flags & DVMRP_NF_SRCRT)
633 if (flags & DVMRP_NF_QUERIER)
635 if (flags & DVMRP_NF_DISABLED)
637 if (flags & DVMRP_NF_DOWN)
647 print_map(node->right);
652 char *graph_name(addr, buf)
658 if (show_names && (name = inet_name(addr)))
667 void graph_edges(node)
675 graph_edges(node->left);
676 if (node->tries >= 0) {
677 printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n",
679 node->addr & 0xFF, (node->addr >> 8) & 0xFF,
680 graph_name(node->addr, name),
681 node->u.interfaces ? "" : "*");
682 for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
683 for (nb = ifc->neighbors; nb; nb = nb->next) {
684 Node *nb_node = find_node(nb->addr, &routers);
687 if (nb_node->tries < 0)
688 nb_node = nb_node->u.alias;
690 if (node != nb_node &&
691 (!(nb2 = find_neighbor(node->addr, nb_node))
692 || node->addr < nb_node->addr)) {
693 printf(" %d \"%d/%d",
694 nb_node->addr, nb->metric, nb->threshold);
695 if (nb2 && (nb2->metric != nb->metric
696 || nb2->threshold != nb->threshold))
697 printf(",%d/%d", nb2->metric, nb2->threshold);
698 if (nb->flags & NF_PRESENT)
700 nb->flags & DVMRP_NF_SRCRT ? "" :
701 nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
702 nb->flags & DVMRP_NF_DOWN ? "D" : "");
708 graph_edges(node->right);
712 void elide_aliases(node)
716 elide_aliases(node->left);
717 if (node->tries >= 0) {
720 for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
723 for (nb = ifc->neighbors; nb; nb = nb->next) {
724 Node *nb_node = find_node(nb->addr, &routers);
726 if (nb_node->tries < 0)
727 nb->addr = nb_node->u.alias->addr;
731 elide_aliases(node->right);
737 u_long now = time(0);
738 char *nowstr = ctime(&now);
740 nowstr[24] = '\0'; /* Kill the newline at the end */
741 elide_aliases(routers);
742 printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
744 graph_edges(routers);
749 int get_number(var, deflt, pargv, pargc)
750 int *var, *pargc, deflt;
753 if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
754 if (*pargc > 1 && isdigit((*pargv)[1][0])) {
755 (*pargv)++, (*pargc)--;
756 *var = atoi((*pargv)[0]);
758 } else if (deflt >= 0) {
763 } else { /* Get value from the rest of this argument */
764 if (isdigit((*pargv)[0][2])) {
765 *var = atoi((*pargv)[0] + 2);
774 u_long host_addr(name)
777 struct hostent *e = gethostbyname(name);
781 memcpy(&addr, e->h_addr_list[0], e->h_length);
783 addr = inet_addr(name);
796 int flood = FALSE, graph = FALSE;
799 setvbuf(stderr, NULL, _IOLBF, 0);
804 if (geteuid() != 0) {
805 fprintf(stderr, "must be root\n");
810 while (argc > 0 && argv[0][0] == '-') {
811 switch (argv[0][1]) {
813 if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
826 if (!get_number(&retries, -1, &argv, &argc))
830 if (!get_number(&timeout, -1, &argv, &argc))
842 "Usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n",
843 "[-r retries] [-d [debug-level]] [router]");
844 fprintf(stderr, "\t-f Flood the routing graph with queries\n");
845 fprintf(stderr, "\t (True by default unless `router' is given)\n");
846 fprintf(stderr, "\t-g Generate output in GraphEd format\n");
847 fprintf(stderr, "\t-n Don't look up DNS names for routers\n");
849 } else if (argc == 1 && !(target_addr = host_addr(argv[0]))) {
850 fprintf(stderr, "Unknown host: %s\n", argv[0]);
855 fprintf(stderr, "Debug level %u\n", debug);
859 { /* Find a good local address for us. */
861 struct sockaddr_in addr;
862 int addrlen = sizeof(addr);
864 addr.sin_family = AF_INET;
865 addr.sin_len = sizeof addr;
866 addr.sin_addr.s_addr = dvmrp_group;
867 addr.sin_port = htons(2000); /* any port over 1024 will do... */
868 if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
869 || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
870 || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
871 perror("Determining local address");
875 our_addr = addr.sin_addr.s_addr;
878 /* Send initial seed message to all local routers */
879 ask(target_addr ? target_addr : allhosts_group);
882 Node *n = find_node(target_addr, &routers);
890 /* Main receive loop */
894 int count, recvlen, dummy = 0;
897 FD_SET(igmp_socket, &fds);
902 count = select(igmp_socket + 1, &fds, 0, 0, &tv);
908 } else if (count == 0) {
909 log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
910 if (retry_requests(routers))
916 recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
919 accept_igmp(recvlen);
920 else if (errno != EINTR)
930 printf("Multicast Router Connectivity:\n\n");
946 void add_table_entry()
949 void leave_group_message()