/* $OpenBSD: mapper.c,v 1.19 2007/02/18 20:57:53 jmc Exp $ */ /* $NetBSD: mapper.c,v 1.3 1995/12/10 11:12:04 mycroft Exp $ */ /* Mapper for connections between MRouteD multicast routers. * Written by Pavel Curtis */ /* * Copyright (c) 1992, 2001 Xerox Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither name of the Xerox, PARC, nor the names of its contributors may be used * to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XEROX CORPORATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "defs.h" #include #include #include #define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */ #define DEFAULT_RETRIES 1 /* How many times to ask each router */ /* All IP addresses are stored in the data structure in NET order. */ typedef struct neighbor { struct neighbor *next; u_int32_t addr; /* IP address in NET order */ u_char metric; /* TTL cost of forwarding */ u_char threshold; /* TTL threshold to forward */ u_short flags; /* flags on connection */ #define NF_PRESENT 0x8000 /* True if flags are meaningful */ } Neighbor; typedef struct interface { struct interface *next; u_int32_t addr; /* IP address of the interface in NET order */ Neighbor *neighbors; /* List of neighbors' IP addresses */ } Interface; typedef struct node { u_int32_t addr; /* IP address of this entry in NET order */ u_int32_t version; /* which mrouted version is running */ int tries; /* How many requests sent? -1 for aliases */ union { struct node *alias; /* If alias, to what? */ struct interface *interfaces; /* Else, neighbor data */ } u; struct node *left, *right; } Node; Node *routers = 0; u_int32_t our_addr, target_addr = 0; /* in NET order */ int debug = 0; int retries = DEFAULT_RETRIES; int timeout = DEFAULT_TIMEOUT; int show_names = TRUE; vifi_t numvifs; /* to keep loader happy */ /* (see COPY_TABLES macro called in kern.c) */ Node * find_node(u_int32_t addr, Node **ptr); Interface * find_interface(u_int32_t addr, Node *node); Neighbor * find_neighbor(u_int32_t addr, Node *node); int main(int argc, char *argv[]); void ask(u_int32_t dst); void ask2(u_int32_t dst); int retry_requests(Node *node); char * inet_name(u_int32_t addr); void print_map(Node *node); char * graph_name(u_int32_t addr, char *buf, size_t len); void graph_edges(Node *node); void elide_aliases(Node *node); void graph_map(void); u_int32_t host_addr(char *name); void usage(void); Node *find_node(u_int32_t addr, Node **ptr) { Node *n = *ptr; if (!n) { *ptr = n = (Node *) malloc(sizeof(Node)); n->addr = addr; n->version = 0; n->tries = 0; n->u.interfaces = 0; n->left = n->right = 0; return n; } else if (addr == n->addr) return n; else if (addr < n->addr) return find_node(addr, &(n->left)); else return find_node(addr, &(n->right)); } Interface *find_interface(u_int32_t addr, Node *node) { Interface *ifc; for (ifc = node->u.interfaces; ifc; ifc = ifc->next) if (ifc->addr == addr) return ifc; ifc = (Interface *) malloc(sizeof(Interface)); ifc->addr = addr; ifc->next = node->u.interfaces; node->u.interfaces = ifc; ifc->neighbors = 0; return ifc; } Neighbor *find_neighbor(u_int32_t addr, Node *node) { Interface *ifc; for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { Neighbor *nb; for (nb = ifc->neighbors; nb; nb = nb->next) if (nb->addr == addr) return nb; } return 0; } /* * Log errors and other messages to stderr, according to the severity of the * message and the current debug level. For errors of severity LOG_ERR or * worse, terminate the program. */ void logit(int severity, int syserr, char *format, ...) { va_list ap; char fmt[100]; switch (debug) { case 0: if (severity > LOG_WARNING) return; case 1: if (severity > LOG_NOTICE ) return; case 2: if (severity > LOG_INFO ) return; default: va_start(ap, format); fmt[0] = '\0'; if (severity == LOG_WARNING) strlcat(fmt, "warning - ", sizeof(fmt)); strncat(fmt, format, 80); vfprintf(stderr, fmt, ap); va_end(ap); if (syserr == 0) fprintf(stderr, "\n"); else if (syserr < sys_nerr) fprintf(stderr, ": %s\n", sys_errlist[syserr]); else fprintf(stderr, ": errno %d\n", syserr); } if (severity <= LOG_ERR) exit(1); } /* * Send a neighbors-list request. */ void ask(u_int32_t dst) { send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS, htonl(MROUTED_LEVEL), 0); } void ask2(u_int32_t dst) { send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, htonl(MROUTED_LEVEL), 0); } /* * Process an incoming group membership report. */ void accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group, int r_type) { logit(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming neighbor probe message. */ void accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen, u_int32_t level) { logit(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming route report message. */ void accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen, u_int32_t level) { logit(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming neighbor-list request message. */ void accept_neighbor_request(u_int32_t src, u_int32_t dst) { if (src != our_addr) logit(LOG_INFO, 0, "ignoring spurious DVMRP neighbor request from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } void accept_neighbor_request2(u_int32_t src, u_int32_t dst) { if (src != our_addr) logit(LOG_INFO, 0, "ignoring spurious DVMRP neighbor request2 from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming neighbor-list message. */ void accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen, u_int32_t level) { Node *node = find_node(src, &routers); if (node->tries == 0) /* Never heard of 'em; must have hit them at */ node->tries = 1; /* least once, though...*/ else if (node->tries == -1) /* follow alias link */ node = node->u.alias; #define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\ a += ((u_int32_t)*p++ << 8), a += *p++) /* if node is running a recent mrouted, ask for additional info */ if (level != 0) { node->version = level; node->tries = 1; ask2(src); return; } if (debug > 3) { int i; fprintf(stderr, " datalen = %d\n", datalen); for (i = 0; i < datalen; i++) { if ((i & 0xF) == 0) fprintf(stderr, " "); fprintf(stderr, " %02x", p[i]); if ((i & 0xF) == 0xF) fprintf(stderr, "\n"); } if ((datalen & 0xF) != 0xF) fprintf(stderr, "\n"); } while (datalen > 0) { /* loop through interfaces */ u_int32_t ifc_addr; u_char metric, threshold, ncount; Node *ifc_node; Interface *ifc; Neighbor *old_neighbors; if (datalen < 4 + 3) { logit(LOG_WARNING, 0, "received truncated interface record from %s", inet_fmt(src, s1)); return; } GET_ADDR(ifc_addr); ifc_addr = htonl(ifc_addr); metric = *p++; threshold = *p++; ncount = *p++; datalen -= 4 + 3; /* Fix up any alias information */ ifc_node = find_node(ifc_addr, &routers); if (ifc_node->tries == 0) { /* new node */ ifc_node->tries = -1; ifc_node->u.alias = node; } else if (ifc_node != node && (ifc_node->tries > 0 || ifc_node->u.alias != node)) { /* must merge two hosts' nodes */ Interface *ifc_i, *next_ifc_i; if (ifc_node->tries == -1) { Node *tmp = ifc_node->u.alias; ifc_node->u.alias = node; ifc_node = tmp; } /* Merge ifc_node (foo_i) into node (foo_n) */ if (ifc_node->tries > node->tries) node->tries = ifc_node->tries; for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) { Neighbor *nb_i, *next_nb_i, *nb_n; Interface *ifc_n = find_interface(ifc_i->addr, node); old_neighbors = ifc_n->neighbors; for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) { next_nb_i = nb_i->next; for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next) if (nb_i->addr == nb_n->addr) { if (nb_i->metric != nb_n->metric || nb_i->threshold != nb_n->threshold) logit(LOG_WARNING, 0, "inconsistent %s for neighbor %s of %s", "metric/threshold", inet_fmt(nb_i->addr, s1), inet_fmt(node->addr, s2)); free(nb_i); break; } if (!nb_n) { /* no match for this neighbor yet */ nb_i->next = ifc_n->neighbors; ifc_n->neighbors = nb_i; } } next_ifc_i = ifc_i->next; free(ifc_i); } ifc_node->tries = -1; ifc_node->u.alias = node; } ifc = find_interface(ifc_addr, node); old_neighbors = ifc->neighbors; /* Add the neighbors for this interface */ while (ncount--) { u_int32_t neighbor; Neighbor *nb; Node *n_node; if (datalen < 4) { logit(LOG_WARNING, 0, "received truncated neighbor list from %s", inet_fmt(src, s1)); return; } GET_ADDR(neighbor); neighbor = htonl(neighbor); datalen -= 4; for (nb = old_neighbors; nb; nb = nb->next) if (nb->addr == neighbor) { if (metric != nb->metric || threshold != nb->threshold) logit(LOG_WARNING, 0, "inconsistent %s for neighbor %s of %s", "metric/threshold", inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2)); goto next_neighbor; } nb = (Neighbor *) malloc(sizeof(Neighbor)); nb->next = ifc->neighbors; ifc->neighbors = nb; nb->addr = neighbor; nb->metric = metric; nb->threshold = threshold; nb->flags = 0; n_node = find_node(neighbor, &routers); if (n_node->tries == 0 && !target_addr) { /* it's a new router */ ask(neighbor); n_node->tries = 1; } next_neighbor: ; } } } void accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen, u_int32_t level) { Node *node = find_node(src, &routers); u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */ /* well, only possibly_broken_cisco, but that's too long to type. */ if (node->tries == 0) /* Never heard of 'em; must have hit them at */ node->tries = 1; /* least once, though...*/ else if (node->tries == -1) /* follow alias link */ node = node->u.alias; while (datalen > 0) { /* loop through interfaces */ u_int32_t ifc_addr; u_char metric, threshold, ncount, flags; Node *ifc_node; Interface *ifc; Neighbor *old_neighbors; if (datalen < 4 + 4) { logit(LOG_WARNING, 0, "received truncated interface record from %s", inet_fmt(src, s1)); return; } ifc_addr = *(u_int32_t*)p; p += 4; metric = *p++; threshold = *p++; flags = *p++; ncount = *p++; datalen -= 4 + 4; if (broken_cisco && ncount == 0) /* dumb Ciscos */ ncount = 1; if (broken_cisco && ncount > 15) /* dumb Ciscos */ ncount = ncount & 0xf; /* Fix up any alias information */ ifc_node = find_node(ifc_addr, &routers); if (ifc_node->tries == 0) { /* new node */ ifc_node->tries = -1; ifc_node->u.alias = node; } else if (ifc_node != node && (ifc_node->tries > 0 || ifc_node->u.alias != node)) { /* must merge two hosts' nodes */ Interface *ifc_i, *next_ifc_i; if (ifc_node->tries == -1) { Node *tmp = ifc_node->u.alias; ifc_node->u.alias = node; ifc_node = tmp; } /* Merge ifc_node (foo_i) into node (foo_n) */ if (ifc_node->tries > node->tries) node->tries = ifc_node->tries; for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) { Neighbor *nb_i, *next_nb_i, *nb_n; Interface *ifc_n = find_interface(ifc_i->addr, node); old_neighbors = ifc_n->neighbors; for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) { next_nb_i = nb_i->next; for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next) if (nb_i->addr == nb_n->addr) { if (nb_i->metric != nb_n->metric || nb_i->threshold != nb_i->threshold) logit(LOG_WARNING, 0, "inconsistent %s for neighbor %s of %s", "metric/threshold", inet_fmt(nb_i->addr, s1), inet_fmt(node->addr, s2)); free(nb_i); break; } if (!nb_n) { /* no match for this neighbor yet */ nb_i->next = ifc_n->neighbors; ifc_n->neighbors = nb_i; } } next_ifc_i = ifc_i->next; free(ifc_i); } ifc_node->tries = -1; ifc_node->u.alias = node; } ifc = find_interface(ifc_addr, node); old_neighbors = ifc->neighbors; /* Add the neighbors for this interface */ while (ncount-- && datalen > 0) { u_int32_t neighbor; Neighbor *nb; Node *n_node; if (datalen < 4) { logit(LOG_WARNING, 0, "received truncated neighbor list from %s", inet_fmt(src, s1)); return; } neighbor = *(u_int32_t*)p; p += 4; datalen -= 4; if (neighbor == 0) /* make leaf nets point to themselves */ neighbor = ifc_addr; for (nb = old_neighbors; nb; nb = nb->next) if (nb->addr == neighbor) { if (metric != nb->metric || threshold != nb->threshold) logit(LOG_WARNING, 0, "inconsistent %s for neighbor %s of %s", "metric/threshold", inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2)); goto next_neighbor; } nb = (Neighbor *) malloc(sizeof(Neighbor)); nb->next = ifc->neighbors; ifc->neighbors = nb; nb->addr = neighbor; nb->metric = metric; nb->threshold = threshold; nb->flags = flags | NF_PRESENT; n_node = find_node(neighbor, &routers); if (n_node->tries == 0 && !target_addr) { /* it's a new router */ ask(neighbor); n_node->tries = 1; } next_neighbor: ; } } } void check_vif_state(void) { logit(LOG_NOTICE, 0, "network marked down..."); } int retry_requests(Node *node) { int result; if (node) { result = retry_requests(node->left); if (node->tries > 0 && node->tries < retries) { if (node->version) ask2(node->addr); else ask(node->addr); node->tries++; result = 1; } return retry_requests(node->right) || result; } else return 0; } char *inet_name(u_int32_t addr) { struct hostent *e; e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); return e ? e->h_name : 0; } void print_map(Node *node) { if (node) { char *name, *addr; print_map(node->left); addr = inet_fmt(node->addr, s1); if (!target_addr || (node->tries >= 0 && node->u.interfaces) || (node->tries == -1 && node->u.alias->tries >= 0 && node->u.alias->u.interfaces)) { if (show_names && (name = inet_name(node->addr))) printf("%s (%s):", addr, name); else printf("%s:", addr); if (node->tries < 0) printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1)); else if (!node->u.interfaces) printf(" no response to query\n\n"); else { Interface *ifc; if (node->version) printf(" ", node->version & 0xff, (node->version >> 8) & 0xff); printf("\n"); for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { Neighbor *nb; char *ifc_name = inet_fmt(ifc->addr, s1); int ifc_len = strlen(ifc_name); int count = 0; printf(" %s:", ifc_name); for (nb = ifc->neighbors; nb; nb = nb->next) { if (count > 0) printf("%*s", ifc_len + 5, ""); printf(" %s", inet_fmt(nb->addr, s1)); if (show_names && (name = inet_name(nb->addr))) printf(" (%s)", name); printf(" [%d/%d", nb->metric, nb->threshold); if (nb->flags) { u_short flags = nb->flags; if (flags & DVMRP_NF_TUNNEL) printf("/tunnel"); if (flags & DVMRP_NF_SRCRT) printf("/srcrt"); if (flags & DVMRP_NF_QUERIER) printf("/querier"); if (flags & DVMRP_NF_DISABLED) printf("/disabled"); if (flags & DVMRP_NF_DOWN) printf("/down"); } printf("]\n"); count++; } } printf("\n"); } } print_map(node->right); } } char *graph_name(u_int32_t addr, char *buf, size_t len) { char *name; if (show_names && (name = inet_name(addr))) strlcpy(buf, name, len); else inet_fmt(addr, buf); return buf; } void graph_edges(Node *node) { Interface *ifc; Neighbor *nb; char name[MAXHOSTNAMELEN]; if (node) { graph_edges(node->left); if (node->tries >= 0) { printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n", (int) node->addr, node->addr & 0xFF, (node->addr >> 8) & 0xFF, graph_name(node->addr, name, sizeof(name)), node->u.interfaces ? "" : "*"); for (ifc = node->u.interfaces; ifc; ifc = ifc->next) for (nb = ifc->neighbors; nb; nb = nb->next) { Node *nb_node = find_node(nb->addr, &routers); Neighbor *nb2; if (nb_node->tries < 0) nb_node = nb_node->u.alias; if (node != nb_node && (!(nb2 = find_neighbor(node->addr, nb_node)) || node->addr < nb_node->addr)) { printf(" %d \"%d/%d", nb_node->addr, nb->metric, nb->threshold); if (nb2 && (nb2->metric != nb->metric || nb2->threshold != nb->threshold)) printf(",%d/%d", nb2->metric, nb2->threshold); if (nb->flags & NF_PRESENT) printf("%s%s", nb->flags & DVMRP_NF_SRCRT ? "" : nb->flags & DVMRP_NF_TUNNEL ? "E" : "P", nb->flags & DVMRP_NF_DOWN ? "D" : ""); printf("\"\n"); } } printf(" ;\n"); } graph_edges(node->right); } } void elide_aliases(Node *node) { if (node) { elide_aliases(node->left); if (node->tries >= 0) { Interface *ifc; for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { Neighbor *nb; for (nb = ifc->neighbors; nb; nb = nb->next) { Node *nb_node = find_node(nb->addr, &routers); if (nb_node->tries < 0) nb->addr = nb_node->u.alias->addr; } } } elide_aliases(node->right); } } void graph_map(void) { time_t now = time(0); char *nowstr = ctime(&now); nowstr[24] = '\0'; /* Kill the newline at the end */ elide_aliases(routers); printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n", nowstr); graph_edges(routers); printf("END\n"); } u_int32_t host_addr(char *name) { struct hostent *e = gethostbyname(name); int addr; if (e) memcpy(&addr, e->h_addr_list[0], e->h_length); else { addr = inet_addr(name); if (addr == -1) addr = 0; } return addr; } void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-fgn] [-d level] [-r count] [-t seconds] " "[starting_router]\n\n", __progname); fprintf(stderr, "\t-f Flood the routing graph with queries\n"); fprintf(stderr, "\t (True by default unless `router' is given)\n"); fprintf(stderr, "\t-g Generate output in GraphEd format\n"); fprintf(stderr, "\t-n Don't look up DNS names for routers\n"); exit(1); } int main(int argc, char *argv[]) { int flood = FALSE, graph = FALSE; int ch; const char *errstr; if (geteuid() != 0) { fprintf(stderr, "map-mbone: must be root\n"); exit(1); } init_igmp(); setuid(getuid()); setlinebuf(stderr); while ((ch = getopt(argc, argv, "d::fgnr:t:")) != -1) { switch (ch) { case 'd': if (!optarg) debug = DEFAULT_DEBUG; else { debug = strtonum(optarg, 0, 3, &errstr); if (errstr) { warnx("debug level %s", errstr); debug = DEFAULT_DEBUG; } } break; case 'f': flood = TRUE; break; case 'g': graph = TRUE; break; case 'n': show_names = FALSE; break; case 'r': retries = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) { warnx("retries %s", errstr); usage(); } break; case 't': timeout = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) { warnx("timeout %s", errstr); usage(); } break; default: usage(); } } argc -= optind; argv += optind; if (argc > 1) usage(); else if (argc == 1 && !(target_addr = host_addr(argv[0]))) { fprintf(stderr, "Unknown host: %s\n", argv[0]); exit(2); } if (debug) fprintf(stderr, "Debug level %u\n", debug); { /* Find a good local address for us. */ int udp; struct sockaddr_in addr; int addrlen = sizeof(addr); memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; #if (defined(BSD) && (BSD >= 199103)) addr.sin_len = sizeof addr; #endif addr.sin_addr.s_addr = dvmrp_group; addr.sin_port = htons(2000); /* any port over 1024 will do... */ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0 || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) { perror("Determining local address"); exit(1); } close(udp); our_addr = addr.sin_addr.s_addr; } /* Send initial seed message to all local routers */ ask(target_addr ? target_addr : allhosts_group); if (target_addr) { Node *n = find_node(target_addr, &routers); n->tries = 1; if (flood) target_addr = 0; } /* Main receive loop */ for(;;) { struct pollfd pfd[1]; int count, recvlen, dummy = 0; pfd[0].fd = igmp_socket; pfd[0].events = POLLIN; count = poll(pfd, 1, timeout * 1000); if (count < 0) { if (errno != EINTR) perror("select"); continue; } else if (count == 0) { logit(LOG_DEBUG, 0, "Timed out receiving neighbor lists"); if (retry_requests(routers)) continue; else break; } recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy); if (recvlen >= 0) accept_igmp(recvlen); else if (errno != EINTR) perror("recvfrom"); } printf("\n"); if (graph) graph_map(); else { if (!target_addr) printf("Multicast Router Connectivity:\n\n"); print_map(routers); } exit(0); } /* dummies */ void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen) { } void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen) { } void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen) { } void add_table_entry(u_int32_t origin, u_int32_t mcastgrp) { } void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group) { } void accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data, u_int no, int datalen) { } void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group, int tmo) { } void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen) { } void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen) { }