diff --git a/usr.sbin/mrouted/callout.c b/usr.sbin/mrouted/callout.c index 0538a3f5acd2..51437e49ad2e 100644 --- a/usr.sbin/mrouted/callout.c +++ b/usr.sbin/mrouted/callout.c @@ -1,201 +1,203 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: callout.c,v 1.1 1994/08/24 23:52:49 thyagara Exp $ + * $Id: callout.c,v 1.2 1994/09/08 02:51:11 wollman Exp $ */ #include "defs.h" /* the code below implements a callout queue */ static int id = 0; static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */ static int in_callout= 0; typedef void (* cfunc_t)(); struct timeout_q { struct timeout_q *next; /* next event */ int id; cfunc_t func ; /* function to call */ char *data; /* func's data */ int time; /* time offset to next event*/ }; -callout_init() +static void print_Q(); + +void callout_init() { Q = (struct timeout_q *) 0; } /* * signal handler for SIGALARM that is called once every second */ -age_callout_queue() +void age_callout_queue() { struct timeout_q *ptr; if (in_callout) return; in_callout = 1; ptr = Q; while (ptr){ if (!ptr->time ) { /* timeout has happened */ if(ptr->func) ptr->func(ptr->data); Q = Q->next; free(ptr); ptr = Q; } else { ptr->time --; #ifdef IGMP_DEBUG log(LOG_DEBUG,0,"[callout, age_callout_queue] -- time (%d)", ptr->time); #endif IGMP_DEBUG in_callout = 0; return; } } in_callout = 0; return; } /* * sets the timer */ int timer_setTimer(delay, action, data) int delay; /* number of units for timeout */ cfunc_t action; /* function to be called on timeout */ char *data; /* what to call the timeout function with */ { struct timeout_q *ptr, *node, *prev; if (in_callout) - return; + return -1; in_callout = 1; /* create a node */ node = (struct timeout_q *)malloc(sizeof(struct timeout_q)); if ((int) node <= 0) { log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n"); in_callout = 0; return -1; } node->func = action; node->data = data; node->time = delay; node->next = 0; node->id = ++id; prev = ptr = Q; /* insert node in the queue */ /* if the queue is empty, insert the node and return */ if (!Q) Q = node; else { /* chase the pointer looking for the right place */ while (ptr){ if (delay < ptr->time){ /* right place */ node->next = ptr; if (ptr == Q) Q = node; else prev->next = node; ptr->time -= node->time; print_Q(); in_callout = 0; return node->id; } else { /* keep moving */ delay -= ptr->time; node->time = delay; prev = ptr; ptr = ptr->next; } } prev->next = node; } print_Q(); in_callout = 0; return node->id; } /* clears the associated timer */ void timer_clearTimer( id) int id; { struct timeout_q *ptr, *prev; if (in_callout) return; in_callout = 1; if ( !id ) {in_callout = 0; return;} prev = ptr = Q; /* * find the right node, delete it. the subsequent node's time * gets bumped up */ print_Q(); while (ptr){ if (ptr->id == id){ /* got the right node */ /* unlink it from the queue */ if ( ptr == Q) Q = Q->next; else prev->next = ptr->next; /* increment next node if any */ if (ptr->next != 0) (ptr->next)->time += ptr->time; free(ptr->data); free(ptr); print_Q(); in_callout = 0; return; } prev = ptr; ptr = ptr->next; } print_Q(); in_callout = 0; } /* * debugging utility */ -print_Q() +static void print_Q() { +#ifdef IGMP_DEBUG struct timeout_q *ptr; -#ifdef IGMP_DEBUG for(ptr = Q; ptr; ptr = ptr->next) log(LOG_DEBUG,0,"(%d,%d) ", ptr->id, ptr->time); #endif IGMP_DEBUG } diff --git a/usr.sbin/mrouted/defs.h b/usr.sbin/mrouted/defs.h index a0a9f4a07cdf..c75dd8e8f881 100644 --- a/usr.sbin/mrouted/defs.h +++ b/usr.sbin/mrouted/defs.h @@ -1,172 +1,172 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: defs.h,v 1.8 1994/08/24 23:53:23 thyagara Exp $ + * $Id: defs.h,v 1.2 1994/09/08 02:51:13 wollman Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dvmrp.h" #include "vif.h" #include "route.h" #include "prune.h" /* * Miscellaneous constants and macros. */ #define FALSE 0 #define TRUE 1 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) #define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY #define PROTOCOL_VERSION 3 /* increment when packet format/content changes */ -#define MROUTED_VERSION 3 /* increment on local changes or bug fixes, */ +#define MROUTED_VERSION 4 /* increment on local changes or bug fixes, */ /* reset to 0 whever PROTOCOL_VERSION increments */ #define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION ) /* for IGMP 'group' field of DVMRP messages */ #define DEL_RTE_GROUP 0 #define DEL_ALL_ROUTES 1 /* for Deleting kernel table entries */ /* * External declarations for global variables and functions. */ extern char recv_buf[MAX_IP_PACKET_LEN]; extern char send_buf[MAX_IP_PACKET_LEN]; extern int igmp_socket; extern u_long allhosts_group; extern u_long dvmrp_group; extern u_long dvmrp_genid; #define DEFAULT_DEBUG 2 /* default if "-d" given without value */ extern int debug; extern u_char pruning; extern int routes_changed; extern int delay_change_reports; extern unsigned nroutes; extern struct uvif uvifs[MAXVIFS]; extern vifi_t numvifs; extern int vifs_down; extern int udp_socket; extern char s1[]; extern char s2[]; extern char s3[]; extern int errno; extern int sys_nerr; #ifndef __FreeBSD__ extern char * sys_errlist[]; #endif extern void log(); extern void init_igmp(); extern void accept_igmp(); extern void send_igmp(); extern void init_routes(); extern void start_route_updates(); extern void update_route(); extern void age_routes(); extern void expire_all_routes(); extern void free_all_routes(); extern void accept_probe(); extern void accept_report(); extern void report(); extern void report_to_all_neighbors(); extern int report_next_chunk(); extern void add_vif_to_routes(); extern void delete_vif_from_routes(); extern void delete_neighbor_from_routes(); extern void dump_routes(); extern void init_vifs(); extern void check_vif_state(); extern vifi_t find_vif(); extern void age_vifs(); extern void dump_vifs(); extern void stop_all_vifs(); extern void accept_group_report(); extern void query_groups(); extern void probe_for_neighbors(); extern int update_neighbor(); extern void accept_neighbor_request(); extern void config_vifs_from_kernel(); extern void config_vifs_from_file(); extern int inet_valid_host(); extern int inet_valid_subnet(); extern char * inet_fmt(); extern char * inet_fmts(); extern u_long inet_parse(); extern int inet_cksum(); extern struct rtentry * determine_route(); extern void init_ktable(); extern int grplst_mem(); extern void add_table_entry(); extern void del_table_entry(); extern void update_table_entry(); extern void update_lclgrp(); extern void delete_lclgrp(); extern unsigned kroutes; extern void send_prune(); extern void accept_prune(); extern int no_entry_exists(); extern struct ktable * find_src_grp(); extern int rtr_cnt(); extern void free_all_prunes(); extern void age_table_entry(); extern void dump_cache(); extern void chkgrp_graft(); extern void accept_graft(); extern void send_graft_ack(); extern void send_graft(); extern void accept_g_ack(); extern void mtrace(); extern char * malloc(); extern char * fgets(); extern FILE * fopen(); #ifndef htonl extern u_long htonl(); extern u_long ntohl(); #endif diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c index ce5c1cebf28c..f7a04dfe88e7 100644 --- a/usr.sbin/mrouted/igmp.c +++ b/usr.sbin/mrouted/igmp.c @@ -1,291 +1,294 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * * igmp.c,v 1.2 1994/09/08 02:51:15 wollman Exp */ #include "defs.h" /* * Exported variables. */ char recv_buf[MAX_IP_PACKET_LEN]; /* input packet buffer */ char send_buf[MAX_IP_PACKET_LEN]; /* output packet buffer */ int igmp_socket; /* socket for all network I/O */ u_long allhosts_group; /* allhosts addr in net order */ u_long dvmrp_group; /* DVMRP grp addr in net order */ u_long dvmrp_genid; /* IGMP generation id */ /* * Open and initialize the igmp socket, and fill in the non-changing * IP header fields in the output packet buffer. */ void init_igmp() { struct ip *ip; if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) log(LOG_ERR, errno, "IGMP socket"); k_hdr_include(TRUE); /* include IP header when sending */ k_set_rcvbuf(48*1024); /* lots of input buffering */ k_set_ttl(1); /* restrict multicasts to one hop */ k_set_loop(FALSE); /* disable multicast loopback */ ip = (struct ip *)send_buf; ip->ip_hl = sizeof(struct ip) >> 2; ip->ip_v = IPVERSION; ip->ip_tos = 0; ip->ip_off = 0; ip->ip_p = IPPROTO_IGMP; ip->ip_ttl = MAXTTL; /* applies to unicasts only */ allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); dvmrp_group = htonl(INADDR_DVMRP_GROUP); } /* %%% hack for PIM %%% */ #define IGMP_PIM 0x14 #define PIM_QUERY 0 #define PIM_REGISTER 1 #define PIM_REGISTER_STOP 2 #define PIM_JOIN_PRUNE 3 #define PIM_RP_REACHABLE 4 #define PIM_ASSERT 5 #define PIM_GRAFT 6 #define PIM_GRAFT_ACK 7 static char *packet_kind(type, code) u_char type, code; { switch (type) { case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; case IGMP_HOST_MEMBERSHIP_REPORT: return "membership report "; case IGMP_HOST_NEW_MEMBERSHIP_REPORT: return "new membership report "; case IGMP_HOST_LEAVE_MESSAGE: return "leave message"; case IGMP_DVMRP: switch (code) { case DVMRP_PROBE: return "neighbor probe "; case DVMRP_REPORT: return "route report "; case DVMRP_ASK_NEIGHBORS: return "neighbor request "; case DVMRP_NEIGHBORS: return "neighbor list "; case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; case DVMRP_NEIGHBORS2: return "neighbor list 2 "; case DVMRP_PRUNE: return "prune message "; case DVMRP_GRAFT: return "graft message "; case DVMRP_GRAFT_ACK: return "graft message ack "; default: return "unknown DVMRP msg "; } case IGMP_PIM: /* %%% hack for PIM %%% */ switch (code) { case PIM_QUERY: return "PIM Router-Query "; case PIM_REGISTER: return "PIM Register "; case PIM_REGISTER_STOP: return "PIM Register-Stop "; case PIM_JOIN_PRUNE: return "PIM Join/Prune "; case PIM_RP_REACHABLE: return "PIM RP-Reachable "; case PIM_ASSERT: return "PIM Assert "; case PIM_GRAFT: return "PIM Graft "; case PIM_GRAFT_ACK: return "PIM Graft-Ack "; default: return "unknown PIM msg "; } case IGMP_MTRACE: return "IGMP trace query "; case IGMP_MTRACE_RESP: return "IGMP trace reply "; default: return "unknown IGMP msg "; } } /* * Process a newly received IGMP packet that is sitting in the input * packet buffer. */ void accept_igmp(recvlen) int recvlen; { - register vifi_t vifi; register u_long src, dst, group; struct ip *ip; struct igmp *igmp; int ipdatalen, iphdrlen, igmpdatalen; if (recvlen < sizeof(struct ip)) { log(LOG_WARNING, 0, "received packet too short (%u bytes) for IP header", recvlen); return; } ip = (struct ip *)recv_buf; src = ip->ip_src.s_addr; dst = ip->ip_dst.s_addr; /* * this is most likely a message from the kernel indicating that * a new src grp pair message has arrived and so, it would be * necessary to install a route into the kernel for this. */ if (ip->ip_p == 0) { - if (src == NULL || dst == NULL) + if (src == 0 || dst == 0) log(LOG_WARNING, 0, "kernel request not accurate"); else add_table_entry(src, dst); return; } iphdrlen = ip->ip_hl << 2; ipdatalen = ip->ip_len; if (iphdrlen + ipdatalen != recvlen) { log(LOG_WARNING, 0, "received packet shorter (%u bytes) than hdr+data length (%u+%u)", recvlen, iphdrlen, ipdatalen); return; } igmp = (struct igmp *)(recv_buf + iphdrlen); group = igmp->igmp_group.s_addr; igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { log(LOG_WARNING, 0, "received IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inet_fmt(src, s1)); return; } log(LOG_DEBUG, 0, "RECV %s from %-15s to %s", packet_kind(igmp->igmp_type, igmp->igmp_code), inet_fmt(src, s1), inet_fmt(dst, s2)); switch (igmp->igmp_type) { case IGMP_HOST_MEMBERSHIP_QUERY: /* we have to do the determination of the querrier router here */ return; case IGMP_HOST_MEMBERSHIP_REPORT: case IGMP_HOST_NEW_MEMBERSHIP_REPORT: accept_group_report(src, dst, group,igmp->igmp_type); return; case IGMP_HOST_LEAVE_MESSAGE: leave_group_message(src, dst, group); return; case IGMP_DVMRP: switch (igmp->igmp_code) { case DVMRP_PROBE: accept_probe(src, dst, (char *)(igmp+1), igmpdatalen, ntohl(group)); return; case DVMRP_REPORT: accept_report(src, dst, (char *)(igmp+1), igmpdatalen, ntohl(group)); return; case DVMRP_ASK_NEIGHBORS: accept_neighbor_request(src, dst); return; case DVMRP_ASK_NEIGHBORS2: accept_neighbor_request2(src, dst); return; case DVMRP_NEIGHBORS: accept_neighbors(src, dst, (char *)(igmp+1), igmpdatalen, group); return; case DVMRP_NEIGHBORS2: accept_neighbors2(src, dst, (char *)(igmp+1), igmpdatalen, group); return; case DVMRP_PRUNE: accept_prune(src, dst, (char *)(igmp+1), igmpdatalen); return; case DVMRP_GRAFT: accept_graft(src, dst, (char *)(igmp+1), igmpdatalen); return; case DVMRP_GRAFT_ACK: accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen); return; default: log(LOG_INFO, 0, "ignoring unknown DVMRP message code %u from %s to %s", igmp->igmp_code, inet_fmt(src, s1), inet_fmt(dst, s2)); return; } case IGMP_PIM: /* %%% hack for PIM %%% */ return; case IGMP_MTRACE: mtrace(src, dst, group, (char *)(igmp+1), igmp->igmp_code, igmpdatalen); return; default: log(LOG_INFO, 0, "ignoring unknown IGMP message type %u from %s to %s", igmp->igmp_type, inet_fmt(src, s1), inet_fmt(dst, s2)); return; } } /* * Construct an IGMP message in the output packet buffer. The caller may * have already placed data in that buffer, of length 'datalen'. Then send * the message from the interface with IP address 'src' to destination 'dst'. */ void send_igmp(src, dst, type, code, group, datalen) u_long src, dst; int type, code; u_long group; int datalen; { static struct sockaddr_in sdst = {AF_INET, sizeof sdst}; struct ip *ip; struct igmp *igmp; ip = (struct ip *)send_buf; ip->ip_src.s_addr = src; ip->ip_dst.s_addr = dst; ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen; igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); igmp->igmp_type = type; igmp->igmp_code = code; igmp->igmp_group.s_addr = group; igmp->igmp_cksum = 0; igmp->igmp_cksum = inet_cksum((u_short *)igmp, IGMP_MINLEN + datalen); if (IN_MULTICAST(ntohl(dst))) k_set_if(src); if (dst == allhosts_group) k_set_loop(TRUE); sdst.sin_addr.s_addr = dst; if (sendto(igmp_socket, send_buf, ip->ip_len, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { - if (errno == ENETDOWN) check_vif_state(); - else log(LOG_WARNING, errno, "sendto on %s", inet_fmt(src, s1)); + if (errno == ENETDOWN) + check_vif_state(); + else + log(LOG_WARNING, errno, + "sendto to %s on %s", + inet_fmt(dst, s1), inet_fmt(src, s2)); } if (dst == allhosts_group) k_set_loop(FALSE); log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2)); } diff --git a/usr.sbin/mrouted/mrinfo.c b/usr.sbin/mrouted/mrinfo.c index a24a2e3781d5..f82b671063a4 100644 --- a/usr.sbin/mrouted/mrinfo.c +++ b/usr.sbin/mrouted/mrinfo.c @@ -1,481 +1,498 @@ /* * This tool requests configuration info from a multicast router * and prints the reply (if any). Invoke it as: * * mrinfo router-name-or-address * * Written Wed Mar 24 1993 by Van Jacobson (adapted from the * multicast mapper written by Pavel Curtis). * * The lawyers insist we include the following UC copyright notice. * The mapper from which this is derived contained a Xerox copyright * notice which follows the UC one. Try not to get depressed noting * that the legal gibberish is larger than the program. * * Copyright (c) 1993 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Computer Systems * Engineering Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * --------------------------------- * Copyright (c) Xerox Corporation 1992. All rights reserved. * * License is granted to copy, to use, and to make and to use derivative works * for research and evaluation purposes, provided that Xerox is acknowledged * in all documentation pertaining to any such copy or derivative work. Xerox * grants no other licenses expressed or implied. The Xerox trade name should * not be used in any advertising without its written permission. * * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR * ANY PARTICULAR PURPOSE. The software is provided "as is" without express * or implied warranty of any kind. * * These notices must be retained in any copies of any part of this software. */ #ifndef lint static char rcsid[] = - "@(#) $Id: mrinfo.c,v 1.7 1994/08/24 23:54:04 thyagara Exp $"; + "@(#) $Id: mrinfo.c,v 1.2 1994/09/08 02:51:20 wollman Exp $"; /* original rcsid: "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)"; */ #endif #include #include #include "defs.h" #define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */ #define DEFAULT_RETRIES 3 /* How many times to ask each router */ u_long our_addr, target_addr = 0; /* in NET order */ int debug = 0; int retries = DEFAULT_RETRIES; int timeout = DEFAULT_TIMEOUT; int target_level; vifi_t numvifs; /* to keep loader happy */ /* (see COPY_TABLES macro called in kern.c) */ char * inet_name(addr) u_long addr; { struct hostent *e; e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); return e ? e->h_name : "?"; } /* * 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 log(severity, syserr, format, a, b, c, d, e) int severity, syserr; char *format; int a, b, c, d, e; { 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: fmt[0] = '\0'; if (severity == LOG_WARNING) strcat(fmt, "warning - "); strncat(fmt, format, 80); fprintf(stderr, fmt, a, b, c, d, e); 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(dst) u_long dst; { send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS, htonl(MROUTED_LEVEL), 0); } void ask2(dst) u_long dst; { send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, htonl(MROUTED_LEVEL), 0); } /* * Process an incoming neighbor-list message. */ void accept_neighbors(src, dst, p, datalen) u_long src, dst; u_char *p; int datalen; { u_char *ep = p + datalen; #define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\ a += ((u_long)*p++ << 8), a += *p++) printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src)); while (p < ep) { register u_long laddr; register u_char metric; register u_char thresh; register int ncount; GET_ADDR(laddr); laddr = htonl(laddr); metric = *p++; thresh = *p++; ncount = *p++; while (--ncount >= 0) { register u_long neighbor; GET_ADDR(neighbor); neighbor = htonl(neighbor); printf(" %s -> ", inet_fmt(laddr, s1)); printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1), inet_name(neighbor), metric, thresh); } } } void accept_neighbors2(src, dst, p, datalen) u_long src, dst; u_char *p; int datalen; { u_char *ep = p + datalen; printf("%s (%s) [version %d.%d]:\n", inet_fmt(src, s1), inet_name(src), target_level & 0xff, (target_level >> 8) & 0xff); while (p < ep) { register u_char metric; register u_char thresh; register u_char flags; register int ncount; register u_long laddr = *(u_long*)p; p += 4; metric = *p++; thresh = *p++; flags = *p++; ncount = *p++; while (--ncount >= 0) { register u_long neighbor = *(u_long*)p; p += 4; printf(" %s -> ", inet_fmt(laddr, s1)); printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1), inet_name(neighbor), metric, thresh); 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"); } } } int get_number(var, deflt, pargv, pargc) int *var, *pargc, deflt; char ***pargv; { if ((*pargv)[0][2] == '\0') { /* Get the value from the next * argument */ if (*pargc > 1 && isdigit((*pargv)[1][0])) { (*pargv)++, (*pargc)--; *var = atoi((*pargv)[0]); return 1; } else if (deflt >= 0) { *var = deflt; return 1; } else return 0; } else { /* Get value from the rest of this argument */ if (isdigit((*pargv)[0][2])) { *var = atoi((*pargv)[0] + 2); return 1; } else { return 0; } } } u_long host_addr(name) 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; } -main(argc, argv) +int main(argc, argv) int argc; char *argv[]; { setlinebuf(stderr); if (geteuid() != 0) { fprintf(stderr, "must be root\n"); exit(1); } argv++, argc--; while (argc > 0 && argv[0][0] == '-') { switch (argv[0][1]) { case 'd': if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc)) goto usage; break; case 'r': if (!get_number(&retries, -1, &argv, &argc)) goto usage; break; case 't': if (!get_number(&timeout, -1, &argv, &argc)) goto usage; break; default: goto usage; } argv++, argc--; } if (argc > 1 || (argc == 1 && !(target_addr = host_addr(argv[0])))) { usage: fprintf(stderr, "Usage: mrinfo [-t timeout] [-r retries] router\n"); exit(1); } if (target_addr == 0) goto usage; if (debug) fprintf(stderr, "Debug level %u\n", debug); init_igmp(); { /* Find a good local address for us. */ int udp; struct sockaddr_in addr; int addrlen = sizeof(addr); addr.sin_family = AF_INET; addr.sin_len = sizeof addr; addr.sin_addr.s_addr = target_addr; 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; } ask(target_addr); /* Main receive loop */ for (;;) { fd_set fds; struct timeval tv; int count, recvlen, dummy = 0; register u_long src, dst, group; struct ip *ip; struct igmp *igmp; int ipdatalen, iphdrlen, igmpdatalen; FD_ZERO(&fds); FD_SET(igmp_socket, &fds); tv.tv_sec = timeout; tv.tv_usec = 0; count = select(igmp_socket + 1, &fds, 0, 0, &tv); if (count < 0) { if (errno != EINTR) perror("select"); continue; } else if (count == 0) { log(LOG_DEBUG, 0, "Timed out receiving neighbor lists"); if (--retries < 0) exit(1); if (target_level == 0) ask(target_addr); else ask2(target_addr); continue; } recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), 0, NULL, &dummy); if (recvlen <= 0) { if (recvlen && errno != EINTR) perror("recvfrom"); continue; } if (recvlen < sizeof(struct ip)) { log(LOG_WARNING, 0, "packet too short (%u bytes) for IP header", recvlen); continue; } ip = (struct ip *) recv_buf; + if (ip->ip_p == 0) + continue; /* Request to install cache entry */ src = ip->ip_src.s_addr; if (src != target_addr) { fprintf(stderr, "mrinfo: got reply from %s", inet_fmt(src, s1)); fprintf(stderr, " instead of %s\n", inet_fmt(target_addr, s1)); continue; } dst = ip->ip_dst.s_addr; iphdrlen = ip->ip_hl << 2; ipdatalen = ip->ip_len; if (iphdrlen + ipdatalen != recvlen) { log(LOG_WARNING, 0, "packet shorter (%u bytes) than hdr+data length (%u+%u)", recvlen, iphdrlen, ipdatalen); continue; } igmp = (struct igmp *) (recv_buf + iphdrlen); group = igmp->igmp_group.s_addr; igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { log(LOG_WARNING, 0, "IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inet_fmt(src, s1)); continue; } if (igmp->igmp_type != IGMP_DVMRP) continue; + switch (igmp->igmp_code) { + case DVMRP_NEIGHBORS: + case DVMRP_NEIGHBORS2: + if (src != target_addr) { + fprintf(stderr, "mrinfo: got reply from %s", + inet_fmt(src, s1)); + fprintf(stderr, " instead of %s\n", + inet_fmt(target_addr, s1)); + continue; + } + break; + default: + continue; /* ignore all other DVMRP messages */ + } + switch (igmp->igmp_code) { case DVMRP_NEIGHBORS: if (group) { /* knows about DVMRP_NEIGHBORS2 msg */ if (target_level == 0) { target_level = ntohl(group); ask2(target_addr); } } else { accept_neighbors(src, dst, (char *)(igmp + 1), igmpdatalen); exit(0); } break; case DVMRP_NEIGHBORS2: accept_neighbors2(src, dst, (char *)(igmp + 1), igmpdatalen); exit(0); } } } /* dummies */ void accept_probe() { } void accept_group_report() { } void accept_neighbor_request2() { } void accept_report() { } void accept_neighbor_request() { } void accept_prune() { } void accept_graft() { } void accept_g_ack() { } void add_table_entry() { } void check_vif_state() { } void leave_group_message() { } void mtrace() { } diff --git a/usr.sbin/mrouted/mtrace.c b/usr.sbin/mrouted/mtrace.c index 5a8870e9c339..590fe948bfab 100644 --- a/usr.sbin/mrouted/mtrace.c +++ b/usr.sbin/mrouted/mtrace.c @@ -1,460 +1,450 @@ #include #include #include "defs.h" #define DEFAULT_TIMEOUT 10 /* How long to wait before retrying requests */ +#define JAN_1970 2208988800 /* 1970 - 1900 in seconds */ int timeout = DEFAULT_TIMEOUT; vifi_t numvifs; /* to keep loader happy */ /* (see COPY_TABLES macro called in kern.c) */ char * inet_name(addr) u_long addr; { struct hostent *e; e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); return e ? e->h_name : "?"; } u_long host_addr(name) 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; } char * proto_type(type) u_char type; { switch (type) { case PROTO_DVMRP: return ("PROTO_DVMRP"); case PROTO_MOSPF: return ("PROTO_MOSPF"); case PROTO_PIM: return ("PROTO_PIM"); case PROTO_CBT: return ("PROTO_CBT"); default: return ("PROTO_UNKNOWN"); } } char * flag_type(type) u_char type; { switch (type) { case TR_NO_ERR: return ("NO_ERR"); case TR_WRONG_IF: return ("WRONG_IF"); case TR_PRUNED: return ("PRUNED"); case TR_SCOPED: return ("SCOPED"); case TR_NO_RTE: return ("NO_RTE"); default: return ("INVALID ERR"); } } int -t_diff(a_sec, a_usec, b_sec, b_usec) - u_long a_sec, a_usec, b_sec, b_usec; +t_diff(a, b) + u_long a, b; { - int d = a_sec - b_sec; - int ms = a_usec - b_usec; + int d = a - b; - if ((d < 0) || - ((d == 0) && (ms < 0))) { - d = b_sec - a_sec; - ms = b_usec - a_usec; - } - - switch (d) { - case 0: - break; - case 2: - ms += 1000000; - case 1: - ms += 1000000; - break; - default: - ms += (1000000) * d; - } - return (ms/1000); + return ((d * 125) >> 13); } main(argc, argv) int argc; char *argv[]; { struct timeval tq; struct timezone tzp; - u_long resptime; + u_long querytime, resptime; int udp; struct sockaddr_in addr; int addrlen = sizeof(addr); u_long lcl_addr = 0; /* in NET order */ u_long qid = ((u_long)random() >> 8); u_long qsrc = NULL; u_long qgrp = NULL; u_long qdst = NULL; u_char qno = 0; u_long raddr = NULL; u_char qttl = 1; u_char rttl = 1; u_long dst = NULL; struct tr_query *query; struct tr_rlist *tr_rlist = NULL; char *p; int datalen = 0; int i; int done = 0; + if (geteuid() != 0) { + fprintf(stderr, "must be root\n"); + exit(1); + } + argv++, argc--; + if (argc == 0) goto usage; + while (argc > 0 && *argv[0] == '-') { switch (argv[0][1]) { case 's': if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; qsrc = host_addr(argv[0]); break; } else goto usage; case 'g': if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; qgrp = host_addr(argv[0]); break; } else goto usage; case 'd': if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; qdst = host_addr(argv[0]); break; } else goto usage; case 'x': if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; dst = host_addr(argv[0]); break; } else goto usage; case 't': if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; qttl = atoi(argv[0]); if (qttl < 1) qttl = 1; break; } else goto usage; case 'n': if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; qno = atoi(argv[0]); break; } else goto usage; case 'l': if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; rttl = atoi(argv[0]); break; } else goto usage; case 'r': if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; raddr = host_addr(argv[0]); break; } else goto usage; default: goto usage; } argv++, argc--; } if (argc > 0) { usage: printf("usage: mtrace -s -g -d -n <# reports> \n"); printf(" -t [-x ] [-r ] [-l ]\n"); exit(1); } printf("Mtrace src %s grp %s dst %s #%d\n", inet_fmt(qsrc, s1), inet_fmt(qgrp, s2), inet_fmt(qdst, s3), qno); printf(" resp ttl %d resp addr %s\n", rttl, inet_fmt(raddr, s1)); init_igmp(); /* Obtain the local address from which to send out packets */ addr.sin_family = AF_INET; addr.sin_len = sizeof addr; addr.sin_addr.s_addr = qgrp; addr.sin_port = htons(2000); 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); lcl_addr = addr.sin_addr.s_addr; /* Got the local address now */ /* Now, make up the IGMP packet to send */ query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); query->tr_src = qsrc; query->tr_dst = qdst; query->tr_qid = qid; if (raddr) query->tr_raddr = raddr; else query->tr_raddr = lcl_addr; query->tr_rttl = rttl; datalen += sizeof(struct tr_query); if (IN_MULTICAST(ntohl(qgrp))) k_set_ttl(qttl); else k_set_ttl(1); if (dst == NULL) dst = qgrp; /* * set timer to calculate delays & send query */ gettimeofday(&tq, &tzp); + querytime = ((tq.tv_sec + JAN_1970) << 16) + (tq.tv_usec << 10) / 15625; send_igmp(lcl_addr, dst, IGMP_MTRACE, qno, qgrp, datalen); /* * If the response is to be a multicast address, make sure we * are listening on that multicast address. */ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr); /* Wait for our reply now */ while (!done) { fd_set fds; struct timeval tv; struct timezone tzp; int count, recvlen, dummy = 0; - register u_long src, dst, group, smask; + register u_long src, group, smask; struct ip *ip; struct igmp *igmp; struct tr_resp *resp; int ipdatalen, iphdrlen, igmpdatalen; int rno; FD_ZERO(&fds); FD_SET(igmp_socket, &fds); /* need to input timeout as optional argument */ tv.tv_sec = timeout; tv.tv_usec = 0; count = select(igmp_socket + 1, &fds, 0, 0, &tv); if (count < 0) { if (errno != EINTR) perror("select"); continue; } else if (count == 0) { printf("Timed out receiving responses\n"); exit(1); } + gettimeofday(&tq, &tzp); + resptime = ((tq.tv_sec + JAN_1970) << 16) + (tq.tv_usec << 10) / 15625; + recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), 0, NULL, &dummy); if (recvlen <= 0) { if (recvlen && errno != EINTR) perror("recvfrom"); continue; } if (recvlen < sizeof(struct ip)) { log(LOG_WARNING, 0, "packet too short (%u bytes) for IP header", recvlen); continue; } ip = (struct ip *) recv_buf; iphdrlen = ip->ip_hl << 2; ipdatalen = ip->ip_len; if (iphdrlen + ipdatalen != recvlen) { printf("packet shorter (%u bytes) than hdr+data length (%u+%u)\n", recvlen, iphdrlen, ipdatalen); continue; } igmp = (struct igmp *) (recv_buf + iphdrlen); group = igmp->igmp_group.s_addr; igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { printf("IP data field too short (%u bytes) for IGMP, from %s\n", ipdatalen, inet_fmt(src, s1)); continue; } if (igmp->igmp_type != IGMP_MTRACE && igmp->igmp_type != IGMP_MTRACE_RESP) continue; if (igmpdatalen == QLEN) continue; if ((igmpdatalen - QLEN)%RLEN) { printf("packet with incorrect datalen\n"); continue; } query = (struct tr_query *)(igmp + 1); /* If this is query with a different id, ignore! */ if (query->tr_qid != qid) continue; /* * Most of the sanity checking done at this point. * This is the packet we have been waiting for all this time */ resp = (struct tr_resp *)(query + 1); rno = (igmpdatalen - QLEN)/RLEN; /* * print the responses out in reverse order (from src to dst) */ - printf("src: <%s> grp: <%s> dst: <%s>\n\n", inet_fmt(qsrc, s1), - inet_fmt(qgrp, s2), inet_fmt(qdst, s3)); + printf("src: <%s> grp: <%s> dst: <%s> rtt: %d ms\n\n", + inet_fmt(qsrc, s1), inet_fmt(qgrp, s2), inet_fmt(qdst, s3), + t_diff(resptime, querytime)); VAL_TO_MASK(smask, (resp+rno-1)->tr_smask); if (((resp+rno-1)->tr_inaddr & smask) == (qsrc & smask)) printf(" %-15s \n", inet_fmt(qsrc, s1)); else printf(" * * *\n"); - resptime = 0; while (rno--) { struct tr_resp *r = resp + rno; printf(" | \n"); printf(" %-15s ", inet_fmt(r->tr_inaddr, s1)); printf("ttl %d ", r->tr_fttl); - printf("cum: %d ms ", - t_diff(r->tr_qarr >> 16, (r->tr_qarr & 0xffff) << 4, - tq.tv_sec & 0xffff, tq.tv_usec)); - printf("hop: %d ms ", - t_diff(resptime >> 16, (resptime & 0xffff) << 4, - r->tr_qarr >> 16, (r->tr_qarr & 0xffff) << 4)); + printf("cum: %d ms ", t_diff(r->tr_qarr, querytime)); + printf("hop: %d ms ", t_diff(resptime, r->tr_qarr)); printf("%s ", proto_type(r->tr_rproto)); printf("%s\n", flag_type(r->tr_rflags)); printf(" %-15s ", inet_fmt(r->tr_outaddr, s1)); printf("v_in: %ld ", r->tr_vifin); printf("v_out: %ld ", r->tr_vifout); printf("pkts: %ld\n", r->tr_pktcnt); resptime = r->tr_qarr; } printf(" | \n"); printf(" %-15s \n", inet_fmt(qdst, s1)); /* * if the response was multicast back, leave the group */ if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr); /* If I don't expect any more replies, exit here */ exit(0); } } /* dummies */ void log() { } void accept_probe() { } void accept_group_report() { } void accept_neighbors() { } void accept_neighbors2() { } void accept_neighbor_request2() { } void accept_report() { } void accept_neighbor_request() { } void accept_prune() { } void accept_graft() { } void accept_g_ack() { } void add_table_entry() { } void check_vif_state() { } void mtrace() { } void leave_group_message() { } diff --git a/usr.sbin/mrouted/prune.c b/usr.sbin/mrouted/prune.c index dfab604fece0..b18530af55c4 100644 --- a/usr.sbin/mrouted/prune.c +++ b/usr.sbin/mrouted/prune.c @@ -1,1380 +1,1416 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: prune.c,v 1.2 1994/09/08 02:51:23 wollman Exp $ + * $Id: prune.c,v 1.3 1995/03/16 16:25:55 wollman Exp $ */ #include "defs.h" +#define JAN_1970 2208988800 /* 1970 - 1900 in seconds */ + extern int cache_lifetime; extern int max_prune_lifetime; /* * dither cache lifetime to obtain a value between x and 2*x */ #define CACHE_LIFETIME(x) ((x) + (random() % (x))) #define CHK_GS(x, y) { \ switch(x) { \ case 2: \ case 4: \ case 8: \ case 16: \ case 32: \ case 64: \ case 128: \ case 256: y = 1; \ break; \ default: y = 0; \ } \ } static struct ktable *kernel_rtable; /* ptr to list of kernel rt entries */ unsigned int kroutes; /* current number of cache entries */ /* * Initialize the kernel table structure */ void init_ktable() { kernel_rtable = NULL; kroutes = 0; } /* * Determine if mcastgrp has a listener on vifi */ int grplst_mem(vifi, mcastgrp) vifi_t vifi; u_long mcastgrp; { register struct listaddr *g; register struct uvif *v; v = &uvifs[vifi]; for (g = v->uv_groups; g != NULL; g = g->al_next) if (mcastgrp == g->al_addr) return 1; return 0; } /* * Updates the ttl values for each vif. */ void prun_add_ttls(kt) struct ktable *kt; { struct uvif *v; vifi_t vifi; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (VIFM_ISSET(vifi, kt->kt_grpmems)) kt->kt_ttls[vifi] = v->uv_threshold; else - kt->kt_ttls[vifi] = NULL; + kt->kt_ttls[vifi] = 0; } } /* * checks for scoped multicast addresses */ #define GET_SCOPE(kt) { \ register int _i; \ if (((kt)->kt_mcastgrp & 0xff000000) == 0xef000000) \ for (_i = 0; _i < numvifs; _i++) \ if (scoped_addr(_i, (kt)->kt_mcastgrp)) \ VIFM_SET(_i, (kt)->kt_scope); \ } int scoped_addr(vifi, addr) vifi_t vifi; u_long addr; { struct vif_acl *acl; for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next) if ((addr & acl->acl_mask) == acl->acl_addr) return 1; return 0; } /* * Add a new table entry for (origin, mcastgrp) */ void add_table_entry(origin, mcastgrp) u_long origin; u_long mcastgrp; { struct rtentry *r; struct ktable *kt; int i; if ((kt = find_src_grp(origin, mcastgrp)) != NULL) { log(LOG_DEBUG, 0, "kernel entry exists for (%s %s)", inet_fmt(origin, s1), inet_fmt(mcastgrp, s2)); return; } r = determine_route(origin); /* allocate space for the new entry */ kt = (struct ktable *)malloc(sizeof(struct ktable)); if (kt == NULL) log(LOG_ERR, 0, "ran out of memory"); /* fatal */ kroutes++; /* add the new values in */ if (r == NULL) { kt->kt_origin = origin; kt->kt_mcastgrp = mcastgrp; kt->kt_originmask = 0xffffffff; kt->kt_parent = NO_VIF; kt->kt_gateway = 0; kt->kt_children = 0; kt->kt_leaves = 0; kt->kt_timer = CACHE_LIFETIME(cache_lifetime); kt->kt_grpmems = 0; kt->kt_rlist = NULL; kt->kt_prsent_timer = 0; kt->kt_grftsnt = 0; kt->kt_prun_count = 0; kt->kt_scope = 0; } else { kt->kt_origin = r->rt_origin; kt->kt_mcastgrp = mcastgrp; kt->kt_originmask = r->rt_originmask; kt->kt_parent = r->rt_parent; kt->kt_gateway = r->rt_gateway; kt->kt_timer = CACHE_LIFETIME(cache_lifetime); kt->kt_grpmems = 0; kt->kt_rlist = NULL; kt->kt_prsent_timer = 0; kt->kt_grftsnt = 0; kt->kt_prun_count = 0; kt->kt_scope = 0; VIFM_COPY(r->rt_children, kt->kt_children); VIFM_COPY(r->rt_leaves, kt->kt_leaves); /* obtain the multicast group membership list */ for (i = 0; i < numvifs; i++) { if (VIFM_ISSET(i, kt->kt_children) && !(VIFM_ISSET(i, kt->kt_leaves))) VIFM_SET(i, kt->kt_grpmems); if (VIFM_ISSET(i, kt->kt_leaves) && grplst_mem(i, mcastgrp)) VIFM_SET(i, kt->kt_grpmems); } GET_SCOPE(kt); - if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) - kt->kt_grpmems = NULL; + if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) { + kt->kt_grpmems = 0; + kt->kt_scope = -1; /* make sure we don't forward */ + } else kt->kt_grpmems &= ~kt->kt_scope; } /* update the kernel_rtable pointer */ kt->kt_next = kernel_rtable; kernel_rtable = kt; /* update ttls and add entry into kernel */ prun_add_ttls(kt); k_add_rg(kt); - log(LOG_DEBUG, 0, "add entry s:%x g:%x gm:%x", - kt->kt_origin, kt->kt_mcastgrp, kt->kt_grpmems); + log(LOG_DEBUG, 0, "add entry (%s %s) vif-list:%x", + inet_fmt(kt->kt_origin, s1), + inet_fmt(kt->kt_mcastgrp, s2), + kt->kt_grpmems); /* If there are no leaf vifs * which have this group, then * mark this src-grp as a prune candidate. * One thing to do is to check if parent vif is the source * and not send a prune to that. */ if (!kt->kt_grpmems && kt->kt_gateway) send_prune(kt); } /* * An mrouter has gone down and come up on an interface * Forward on that interface immediately */ void reset_neighbor_state(vifi, addr) vifi_t vifi; u_long addr; { struct ktable *prev_kt, *kt; struct prunlst *prev_krl, *krl; /* Check each src-grp entry to see if it was pruned on that interface If so, forward on that interface */ for (prev_kt = (struct ktable *)&kernel_rtable, kt = kernel_rtable; kt; prev_kt = kt, kt = kt->kt_next) { for (prev_krl = (struct prunlst *)&kt->kt_rlist, krl = prev_krl->rl_next; krl; prev_krl = krl, krl = krl->rl_next) { if (krl->rl_router == addr) { prev_krl->rl_next = krl->rl_next; free(krl); krl = prev_krl; kt->kt_prun_count--; } } /* * If neighbor was the parent, remove the prune sent state * Don't send any grafts upstream. */ if (vifi == kt->kt_parent) { k_del_rg(kt); prev_kt->kt_next = kt->kt_next; while (krl = kt->kt_rlist) { kt->kt_rlist = krl->rl_next; free((char *)krl); } free((char *)kt); kt = prev_kt; kroutes--; continue; } /* * Neighbor was not the parent, send grafts to join the groups */ if (kt->kt_prsent_timer) { kt->kt_grftsnt = 1; send_graft(kt); kt->kt_prsent_timer = 0; } if (!VIFM_ISSET(vifi, kt->kt_grpmems)) { if (VIFM_ISSET(vifi, kt->kt_children) && !(VIFM_ISSET(vifi, kt->kt_leaves))) VIFM_SET(vifi, kt->kt_grpmems); if (VIFM_ISSET(vifi, kt->kt_leaves) && grplst_mem(vifi, kt->kt_mcastgrp)) VIFM_SET(vifi, kt->kt_grpmems); kt->kt_grpmems &= ~kt->kt_scope; prun_add_ttls(kt); k_add_rg(kt); } } } /* * Delete table entry from the kernel * del_flag determines how many entries to delete */ void del_table_entry(r, mcastgrp, del_flag) struct rtentry *r; u_long mcastgrp; u_int del_flag; { - struct mfcctl mc; struct ktable *kt, *prev_kt; struct prunlst *krl; if (del_flag == DEL_ALL_ROUTES) { for (prev_kt = (struct ktable *)&kernel_rtable; kt = prev_kt->kt_next; prev_kt = kt) { if ((kt->kt_origin & r->rt_originmask) == r->rt_origin) { - log(LOG_DEBUG, 0, "delete all rtes %x grp %x", - kt->kt_origin, mcastgrp); + log(LOG_DEBUG, 0, "delete all rtes %s", + inet_fmt(kt->kt_origin, s1)); k_del_rg(kt); /* free prun list entries */ while (kt->kt_rlist) { krl = kt->kt_rlist; kt->kt_rlist = krl->rl_next; free((char *)krl); } /* free the source mcastgrp entry */ prev_kt->kt_next = kt->kt_next; free((char *)kt); kroutes--; kt = prev_kt; } } } if (del_flag == DEL_RTE_GROUP) { for (prev_kt = (struct ktable *)&kernel_rtable; (prev_kt) && (kt = prev_kt->kt_next); prev_kt = kt) { if ((kt->kt_origin & r->rt_originmask) == r->rt_origin && kt->kt_mcastgrp == mcastgrp) { - log(LOG_DEBUG, 0, "delete src %x grp %x", - kt->kt_origin, mcastgrp); + log(LOG_DEBUG, 0, "delete (%s, %s)", + inet_fmt(kt->kt_origin, s1), inet_fmt(mcastgrp, s2)); k_del_rg(kt); /* free prun list entries */ while (kt->kt_rlist) { krl = kt->kt_rlist; kt->kt_rlist = krl->rl_next; free((char *)krl); } /* free the source mcastgrp entry */ prev_kt->kt_next = kt->kt_next; free((char *)kt); kroutes--; break; } } } } /* * update kernel table entry when a route entry changes */ void update_table_entry(r) struct rtentry *r; { struct ktable *kt; struct prunlst *krl; int i; int changed; for (kt = kernel_rtable; kt; kt = kt->kt_next) if ((kt->kt_origin & r->rt_originmask)== r->rt_origin) { changed = 0; if (kt->kt_leaves != r->rt_leaves) - changed++; + changed |= 0x1; if (kt->kt_children != r->rt_children) - changed++; + changed |= 0x2; if (kt->kt_parent != r->rt_parent) - changed++; + changed |= 0x4; if (!changed) continue; - log(LOG_DEBUG, 0, "update entry: s %-15s g %-15s", - inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2)); + log(LOG_DEBUG, 0, "updating entry: (%s %s) code:%x", + inet_fmt(kt->kt_origin, s1), + inet_fmt(kt->kt_mcastgrp, s2), changed); /* free prun list entries */ while (kt->kt_rlist) { krl = kt->kt_rlist; kt->kt_rlist = krl->rl_next; free((char *)krl); } kt->kt_parent = r->rt_parent; kt->kt_gateway = r->rt_gateway; kt->kt_grpmems = 0; kt->kt_prun_count = 0; VIFM_COPY(r->rt_children, kt->kt_children); VIFM_COPY(r->rt_leaves, kt->kt_leaves); /* obtain the multicast group membership list */ for (i = 0; i < numvifs; i++) { if (VIFM_ISSET(i, kt->kt_children) && !(VIFM_ISSET(i, kt->kt_leaves))) VIFM_SET(i, kt->kt_grpmems); if (VIFM_ISSET(i, kt->kt_leaves) && grplst_mem(i, kt->kt_mcastgrp)) VIFM_SET(i, kt->kt_grpmems); } - if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) - kt->kt_grpmems = NULL; + if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) { + kt->kt_grpmems = 0; + kt->kt_scope = -1; + } else kt->kt_grpmems &= ~kt->kt_scope; if (kt->kt_grpmems && kt->kt_prsent_timer) { kt->kt_grftsnt = 1; send_graft(kt); kt->kt_prsent_timer = 0; } /* update ttls and add entry into kernel */ prun_add_ttls(kt); k_add_rg(kt); if (!kt->kt_grpmems && kt->kt_gateway) { kt->kt_timer = CACHE_LIFETIME(cache_lifetime); send_prune(kt); } } } /* * set the forwarding flag for all mcastgrps on this vifi */ void update_lclgrp(vifi, mcastgrp) vifi_t vifi; u_long mcastgrp; { struct ktable *kt; - log(LOG_DEBUG, 0, "group %x joined at vif %d", mcastgrp, vifi); + log(LOG_DEBUG, 0, "group %s joined at vif %d", + inet_fmt(mcastgrp, s1), vifi); for (kt = kernel_rtable; kt; kt = kt->kt_next) if (kt->kt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, kt->kt_children)) { VIFM_SET(vifi, kt->kt_grpmems); kt->kt_grpmems &= ~kt->kt_scope; - if (kt->kt_grpmems == NULL) + if (kt->kt_grpmems == 0) continue; prun_add_ttls(kt); k_add_rg(kt); } } /* * reset forwarding flag for all mcastgrps on this vifi */ void delete_lclgrp(vifi, mcastgrp) vifi_t vifi; u_long mcastgrp; { - struct ktable *kt; - log(LOG_DEBUG, 0, "group %x left at vif %d", mcastgrp, vifi); + log(LOG_DEBUG, 0, "group %s left at vif %d", + inet_fmt(mcastgrp, s1), vifi); for (kt = kernel_rtable; kt; kt = kt->kt_next) if (kt->kt_mcastgrp == mcastgrp) { - VIFM_CLR(vifi, kt->kt_grpmems); - prun_add_ttls(kt); - k_add_rg(kt); - - /* - * If there are no more members of this particular group, - * send prune upstream - */ - if (kt->kt_grpmems == NULL && kt->kt_gateway) - send_prune(kt); + struct listaddr *vr; + int stop_sending = 1; + + for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next) + if (no_entry_exists(vr->al_addr, kt)) { + stop_sending = 0; + break; + } + + if (stop_sending) { + VIFM_CLR(vifi, kt->kt_grpmems); + prun_add_ttls(kt); + k_add_rg(kt); + + /* + * If there are no more members of this particular group, + * send prune upstream + */ + if (kt->kt_grpmems == NULL && kt->kt_gateway) + send_prune(kt); + } } } /* * Check if the neighbor supports pruning */ int pruning_neighbor(vifi, addr) vifi_t vifi; u_long addr; { struct listaddr *u; for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next) if ((u->al_addr == addr) && (u->al_pv > 2)) return 1; return 0; } /* * Send a prune message to the upstream router * given by the kt->kt_gateway argument. The origin and * multicast group can be determined from the kt * structure. * * Also, record an entry that a prune was sent for this group */ void send_prune(kt) struct ktable *kt; { struct prunlst *krl; char *p; int i; int datalen; u_long src; u_long dst; /* Don't process any prunes if router is not pruning */ if (pruning == 0) return; /* Don't send a prune to a non-pruning router */ if (!pruning_neighbor(kt->kt_parent, kt->kt_gateway)) return; /* * sends a prune message to the router upstream. */ src = uvifs[kt->kt_parent].uv_lcl_addr; dst = kt->kt_gateway; p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; datalen = 0; /* * determine prune lifetime */ kt->kt_prsent_timer = kt->kt_timer; for (krl = kt->kt_rlist; krl; krl = krl->rl_next) if (krl->rl_timer < kt->kt_prsent_timer) kt->kt_prsent_timer = krl->rl_timer; for (i = 0; i < 4; i++) *p++ = ((char *)&(kt->kt_origin))[i]; for (i = 0; i < 4; i++) *p++ = ((char *)&(kt->kt_mcastgrp))[i]; #if BYTE_ORDER == BIG_ENDIAN for (i = 0; i < 4; i++) #endif #if BYTE_ORDER == LITTLE_ENDIAN for (i = 3; i >= 0; i--) #endif *p++ = ((char *)&(kt->kt_prsent_timer))[i]; datalen += 12; send_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE, htonl(MROUTED_LEVEL), datalen); /* log(LOG_DEBUG, 0, "send prune for src:%x, grp:%x up to %x", kt->kt_origin, kt->kt_mcastgrp, kt->kt_gateway);*/ } /* * Takes the prune message received and then strips it to * determine the (src, grp) pair to be pruned. * * Adds the router to the (src, grp) entry then. * * Determines if further packets have to be sent down that vif * * Determines if a corresponding prune message has to be generated */ void accept_prune(src, dst, p, datalen) u_long src; u_long dst; char *p; int datalen; { u_long prun_src; u_long prun_dst; u_long prun_tmr; vifi_t vifi; int i; int stop_sending; struct ktable *kt; struct prunlst *pr_recv; - struct prunlst *krl; struct listaddr *vr; /* Don't process any prunes if router is not pruning */ if (pruning == 0) return; if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, - "ignoring prune report from non-neighbor %s", inet_fmt(src, s1)); + "ignoring prune report from non-neighbor %s", + inet_fmt(src, s1)); return; } - if (datalen < 0 || datalen > 12) - { - log(LOG_WARNING, 0, - "received non-decipherable prune report from %s", inet_fmt(src, s1)); - return; - } + /* Check if enough data is present */ + if (datalen < 12) { + log(LOG_WARNING, 0, + "non-decipherable prune from %s", + inet_fmt(src, s1)); + return; + } for (i = 0; i< 4; i++) ((char *)&prun_src)[i] = *p++; for (i = 0; i< 4; i++) ((char *)&prun_dst)[i] = *p++; #if BYTE_ORDER == BIG_ENDIAN for (i = 0; i< 4; i++) #endif #if BYTE_ORDER == LITTLE_ENDIAN for (i = 3; i >= 0; i--) #endif ((char *)&prun_tmr)[i] = *p++; kt = find_src_grp(prun_src, prun_dst); if (kt == NULL) { log(LOG_WARNING, 0, "prune message received incorrectly"); return; } if (!VIFM_ISSET(vifi, kt->kt_children)) { log(LOG_INFO, 0, "ignoring prune report from non-child %s", inet_fmt(src, s1)); return; } if (VIFM_ISSET(vifi, kt->kt_scope)) { log(LOG_INFO, 0, "ignoring prune report from %s on scoped vif %d", inet_fmt(src, s1), vifi); return; } /* check if prune has been received from this source */ if (!no_entry_exists(src, kt)) { log(LOG_INFO, 0, "duplicate prune from %s", inet_fmt(src, s1)); return; } log(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s) tmr %d", inet_fmt(src, s1), vifi, inet_fmt(prun_src, s2), inet_fmt(prun_dst, s3), prun_tmr); /* allocate space for the prune structure */ pr_recv = (struct prunlst *)(malloc(sizeof(struct prunlst))); if (pr_recv == NULL) log(LOG_ERR, 0, "pr_recv: ran out of memory"); pr_recv->rl_vifi = vifi; pr_recv->rl_router = src; pr_recv->rl_timer = prun_tmr; /* * add this prune message to the list of prunes received * for this src group pair */ pr_recv->rl_next = kt->kt_rlist; kt->kt_rlist = pr_recv; kt->kt_prun_count++; kt->kt_timer = CACHE_LIFETIME(cache_lifetime); if (kt->kt_timer < prun_tmr) kt->kt_timer = prun_tmr; /* * check if any more packets need to be sent on the * vif which sent this message */ for (vr = uvifs[vifi].uv_neighbors, stop_sending = 1; vr; vr = vr->al_next) if (no_entry_exists(vr->al_addr, kt)) { stop_sending = 0; break; } if (stop_sending && !grplst_mem(vifi, prun_dst)) { VIFM_CLR(vifi, kt->kt_grpmems); prun_add_ttls(kt); k_add_rg(kt); } /* * check if all the child routers have expressed no interest * in this group and if this group does not exist in the * interface * Send a prune message then upstream */ if(kt->kt_grpmems == NULL && kt->kt_gateway) { log(LOG_DEBUG, 0, "snt prun up %d %d", kt->kt_prun_count, rtr_cnt(kt)); send_prune(kt); } } /* * Returns 1 if router vr is not present in the prunlist of kt */ int no_entry_exists(vr, kt) u_long vr; struct ktable *kt; { struct prunlst *krl; for (krl = kt->kt_rlist; krl; krl = krl->rl_next) if (krl->rl_router == vr) return 0; return 1; } /* * Finds the entry for the source group pair in the table */ struct ktable *find_src_grp(src, grp) u_long src; u_long grp; { struct ktable *kt; for (kt = kernel_rtable; kt; kt = kt->kt_next) if ((kt->kt_origin == (src & kt->kt_originmask)) && (kt->kt_mcastgrp == grp)) return kt; return NULL; } /* * scans through the neighbor list of this router and then * determines the total no. of child routers present */ int rtr_cnt(kt) struct ktable *kt; { int ri; int rcount = 0; struct listaddr *u; for (ri = 0; ri < numvifs; ri++) if (VIFM_ISSET(ri, kt->kt_children)) for(u = uvifs[ri].uv_neighbors; u; u = u->al_next) rcount++; return rcount; } /* * Checks if this mcastgrp is present in the kernel table * If so and if a prune was sent, it sends a graft upwards */ void chkgrp_graft(vifi, mcastgrp) vifi_t vifi; u_long mcastgrp; { struct ktable *kt; for (kt = kernel_rtable; kt; kt = kt->kt_next) if (kt->kt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, kt->kt_children)) if (kt->kt_prsent_timer) { VIFM_SET(vifi, kt->kt_grpmems); /* * If the vif that was joined was a scoped vif, * ignore it ; don't graft back */ kt->kt_grpmems &= ~kt->kt_scope; - if (kt->kt_grpmems == NULL) + if (kt->kt_grpmems == 0) continue; /* set the flag for graft retransmission */ kt->kt_grftsnt = 1; /* send graft upwards */ send_graft(kt); /* reset the prune timer and update cache timer*/ kt->kt_prsent_timer = 0; kt->kt_timer = max_prune_lifetime; prun_add_ttls(kt); k_add_rg(kt); } } /* determine the multicast group and src * * if it does, then determine if a prune was sent * upstream. * if prune sent upstream, send graft upstream and send * ack downstream. * * if no prune sent upstream, change the forwarding bit * for this interface and send ack downstream. * * if no entry exists for this group just ignore the message * [this may not be the right thing to do. but lets see what * happens for the time being and then we might decide to do * a modification to the code depending on the type of behaviour * that we see in this] */ void accept_graft(src, dst, p, datalen) u_long src; u_long dst; char *p; int datalen; { vifi_t vifi; u_long prun_src; u_long prun_dst; struct ktable *kt; int i; struct prunlst *krl; struct prunlst *prev_krl; if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, - "ignoring graft report from non-neighbor %s", inet_fmt(src, s1)); + "ignoring graft from non-neighbor %s", + inet_fmt(src, s1)); return; } - if (datalen < 0 || datalen > 8) { + if (datalen < 8) { log(LOG_WARNING, 0, - "received non-decipherable graft report from %s", inet_fmt(src, s1)); + "received non-decipherable graft from %s", + inet_fmt(src, s1)); return; } for (i = 0; i< 4; i++) ((char *)&prun_src)[i] = *p++; for (i = 0; i< 4; i++) ((char *)&prun_dst)[i] = *p++; log(LOG_DEBUG, 0, "%s on vif %d grafts (%s %s)", inet_fmt(src, s1), vifi, inet_fmt(prun_src, s2), inet_fmt(prun_dst, s3)); kt = find_src_grp(prun_src, prun_dst); if (kt == NULL) { log(LOG_DEBUG, 0, "incorrect graft received from %s", inet_fmt(src, s1)); return; } if (VIFM_ISSET(vifi, kt->kt_scope)) { log(LOG_INFO, 0, "incorrect graft received from %s on scoped vif %d", inet_fmt(src, s1), vifi); return; } /* remove prune entry from the list * allow forwarding on that vif, make change in the kernel */ for (prev_krl = (struct prunlst *)&kt->kt_rlist; krl = prev_krl->rl_next; prev_krl = krl) if ((krl->rl_vifi) == vifi && (krl->rl_router == src)) { prev_krl->rl_next = krl->rl_next; free((char *)krl); krl = prev_krl; kt->kt_prun_count--; VIFM_SET(vifi, kt->kt_grpmems); prun_add_ttls(kt); k_add_rg(kt); break; } /* send ack downstream */ send_graft_ack(kt, src); kt->kt_timer = max_prune_lifetime; if (kt->kt_prsent_timer) { /* set the flag for graft retransmission */ kt->kt_grftsnt = 1; /* send graft upwards */ send_graft(kt); /* reset the prune sent timer */ kt->kt_prsent_timer = 0; } } /* * Send an ack that a graft was received */ void send_graft_ack(kt, to) struct ktable *kt; u_long to; { register char *p; register int i; int datalen; u_long src; u_long dst; src = uvifs[kt->kt_parent].uv_lcl_addr; dst = to; p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; datalen = 0; for (i = 0; i < 4; i++) *p++ = ((char *)&(kt->kt_origin))[i]; for (i = 0; i < 4; i++) *p++ = ((char *)&(kt->kt_mcastgrp))[i]; datalen += 8; send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK, htonl(MROUTED_LEVEL), datalen); - log(LOG_DEBUG, 0, "send graft ack for src:%x, grp:%x to %x", - kt->kt_origin, kt->kt_mcastgrp, dst); + log(LOG_DEBUG, 0, "sent graft ack (%s, %s) to %s", + inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2), + inet_fmt(dst, s3)); + } /* * a prune was sent upstream * so, a graft has to be sent to annul the prune * set up a graft timer so that if an ack is not * heard within that time, another graft request * is sent out. */ void send_graft(kt) struct ktable *kt; { register char *p; register int i; int datalen; u_long src; u_long dst; src = uvifs[kt->kt_parent].uv_lcl_addr; dst = kt->kt_gateway; p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; datalen = 0; for (i = 0; i < 4; i++) *p++ = ((char *)&(kt->kt_origin))[i]; for (i = 0; i < 4; i++) *p++ = ((char *)&(kt->kt_mcastgrp))[i]; datalen += 8; if (datalen != 0) { send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT, htonl(MROUTED_LEVEL), datalen); } - log(LOG_DEBUG, 0, "send graft for src:%x, grp:%x up to %x", - kt->kt_origin, kt->kt_mcastgrp, kt->kt_gateway); + log(LOG_DEBUG, 0, "sent graft (%s, %s) to %s", + inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2), + inet_fmt(kt->kt_gateway, s3)); } /* * find out which group is involved first of all * then determine if a graft was sent. * if no graft sent, ignore the message * if graft was sent and the ack is from the right * source, remove the graft timer so that we don't * have send a graft again */ void accept_g_ack(src, dst, p, datalen) u_long src; u_long dst; char *p; int datalen; { vifi_t vifi; u_long grft_src; u_long grft_dst; struct ktable *kt; int i; if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, - "ignoring graft ack report from non-neighbor %s", inet_fmt(src, s1)); + "ignoring graft ack report from non-neighbor %s", + inet_fmt(src, s1)); return; } - if (datalen < 0 || datalen > 8) { + if (datalen < 8) { log(LOG_WARNING, 0, - "received non-decipherable graft ack report from %s", inet_fmt(src, s1)); + "received non-decipherable graft ack report from %s", + inet_fmt(src, s1)); return; } for (i = 0; i< 4; i++) ((char *)&grft_src)[i] = *p++; for (i = 0; i< 4; i++) ((char *)&grft_dst)[i] = *p++; log(LOG_DEBUG, 0, "%s on vif %d acks graft (%s %s)", inet_fmt(src, s1), vifi, inet_fmt(grft_src, s2), inet_fmt(grft_dst, s3)); kt = find_src_grp(grft_src, grft_dst); if (kt == NULL) { log(LOG_WARNING, 0, "received wrong graft ack from %s", inet_fmt(src, s1)); return; } if (kt->kt_grftsnt) kt->kt_grftsnt = 0; } /* * free all prune entries */ void free_all_prunes() { register struct ktable *kt; register struct prunlst *krl; while (kernel_rtable != NULL) { kt = kernel_rtable; kernel_rtable = kt->kt_next; while (kt->kt_rlist != NULL) { krl = kt->kt_rlist; kt->kt_rlist = krl->rl_next; free((char *)krl); } free((char *)kt); kroutes--; } } /* * Advance the timers on all the cache entries. * If there are any entries whose timers have expired, * remove these entries from the kernel cache. */ void age_table_entry() { struct ktable *kt; struct ktable *prev_kt; struct prunlst *krl; struct prunlst *prev_krl; log(LOG_DEBUG, 0, "kr:%x pr:%x", kernel_rtable, (struct ktable *)&kernel_rtable); for (prev_kt = (struct ktable *)&kernel_rtable; kt = prev_kt->kt_next; prev_kt = kt) { /* advance the timer for the kernel entry */ kt->kt_timer -= ROUTE_MAX_REPORT_DELAY; - /* decrement prune timer if need be */ - if (kt->kt_prsent_timer) - kt->kt_prsent_timer -= ROUTE_MAX_REPORT_DELAY; - /* retransmit graft if graft sent flag is still set */ if (kt->kt_grftsnt) { register int y; CHK_GS(kt->kt_grftsnt++, y); if (y) send_graft(kt); } /* delete the entry only if there are no subordinate routers Now, if there are subordinate routers, then, what we - have to do is to decrement each and every router's + have to do is to decrement each and every router's prune time entry too and decide if we want to forward on that link basically */ for (prev_krl = (struct prunlst *)&kt->kt_rlist, - krl = prev_krl->rl_next; - krl; + krl = prev_krl->rl_next; krl; prev_krl = krl, krl = krl->rl_next) { + + /* decrement prune timer received from downstream routers */ if ((krl->rl_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) { - log(LOG_DEBUG, 0, "forw again s %x g %x on vif %d", - kt->kt_origin, kt->kt_mcastgrp, krl->rl_vifi); + log(LOG_DEBUG, 0, "forw again (%s, %s) on vif %d", + inet_fmt(kt->kt_origin, s1), + inet_fmt(kt->kt_mcastgrp, s2), + krl->rl_vifi); + /* + * forwarding now, so entry is not pruned anymore + * reset the cache timer to a largish value also + */ + kt->kt_prsent_timer = 0; + + /* modify the kernel entry to forward packets */ if (!VIFM_ISSET(krl->rl_vifi, kt->kt_grpmems)) { VIFM_SET(krl->rl_vifi, kt->kt_grpmems); prun_add_ttls(kt); k_add_rg(kt); } + /* remove the router's prune entry and await new one */ kt->kt_prun_count--; prev_krl->rl_next = krl->rl_next; free((char *)krl); krl = prev_krl; if (krl == NULL) break; } } if (kt->kt_timer <= 0) { /* * If there are prune entries still outstanding, * update the cache timer otherwise expire entry. */ if (kt->kt_rlist) { kt->kt_timer = CACHE_LIFETIME(cache_lifetime); } else { - log(LOG_DEBUG, 0, "age route s %x g %x", - kt->kt_origin, kt->kt_mcastgrp); + log(LOG_DEBUG, 0, "aging entry (%s, %s)", + inet_fmt(kt->kt_origin, s1), + inet_fmt(kt->kt_mcastgrp, s2)); k_del_rg(kt); prev_kt->kt_next = kt->kt_next; /* free all the prune list entries */ krl = kt->kt_rlist; while(krl) { prev_krl = krl; krl = krl->rl_next; free((char *)prev_krl); } free((char *)kt); kroutes--; kt = prev_kt; } } } } /* * Print the contents of the routing table on file 'fp'. */ void dump_cache(fp2) FILE *fp2; { register struct ktable *kt; register struct prunlst *krl; register int i; register int count; fprintf(fp2, "Multicast Routing Cache Table (%d entries)\n%s", kroutes, " Origin-Subnet Mcast-group CTmr IVif Prcv# Psnt Forwvifs\n"); for (kt = kernel_rtable, count = 0; kt != NULL; kt = kt->kt_next) { fprintf(fp2, " %-15s %-15s", inet_fmts(kt->kt_origin, kt->kt_originmask, s1), inet_fmt(kt->kt_mcastgrp, s2)); if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) { fprintf(fp2, " %5u %2ub %3u %c ", kt->kt_timer, kt->kt_parent, kt->kt_prun_count, kt->kt_prsent_timer ? 'P' : ' '); fprintf(fp2, "\n"); continue; } else fprintf(fp2, " %5u %2u %3u %c ", kt->kt_timer, kt->kt_parent, kt->kt_prun_count, kt->kt_prsent_timer ? 'P' : ' '); for (i = 0; i < numvifs; ++i) { if (VIFM_ISSET(i, kt->kt_grpmems)) fprintf(fp2, " %u ", i); else if (VIFM_ISSET(i, kt->kt_children) && !VIFM_ISSET(i, kt->kt_leaves) && VIFM_ISSET(i, kt->kt_scope)) fprintf(fp2, " %u%c", i, 'b'); else if (VIFM_ISSET(i, kt->kt_children) && !VIFM_ISSET(i, kt->kt_leaves)) fprintf(fp2, " %u%c", i, 'p'); } fprintf(fp2, "\n"); count++; } } /* * Checks if there are any routers that can understand traceroute * downstream */ int can_forward(vifi) vifi_t vifi; { struct listaddr *u; for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next) if (((u->al_pv > 2) && (u->al_mv > 2)) || (u->al_pv > 3)) return 1; return 0; } /* * Traceroute function which returns traceroute replies to the requesting * router. Also forwards the request to downstream routers. */ void mtrace(src, dst, group, data, no, datalen) u_long src; u_long dst; u_long group; char *data; u_char no; int datalen; { u_char type; struct rtentry *rt; struct tr_query *qry; struct tr_resp *resp; struct uvif *v; int vifi; char *p; struct ktable *kt; int rcount; struct timeval tp; struct timezone tzp; struct sioc_vif_req v_req; struct sioc_sg_req sg_req; /* timestamp the request/response */ gettimeofday(&tp, &tzp); /* * Check if it is a query or a response */ if (datalen == QLEN) { type = QUERY; printf("Traceroute query rcvd\n"); } else if ((datalen - QLEN)%RLEN == 0) { type = RESP; printf("Traceroute response rcvd\n"); } else { printf("Non decipherable trace request %s", inet_fmt(src, s1)); return; } qry = (struct tr_query *)data; /* * if it is a multicast packet with all reports filled, drop it */ if ((rcount = (datalen - QLEN)/RLEN) == no) { printf("multicast packet with reports filled in\n"); return; } printf("s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1), inet_fmt(group, s2), inet_fmt(qry->tr_dst, s3)); printf("rttl: %d rd: %s\n", qry->tr_rttl, inet_fmt(qry->tr_raddr, s1)); printf("rcount:%d\n", rcount); /* determine the routing table entry for this traceroute */ rt = determine_route(qry->tr_src); /* * Query type packet - check if rte exists * Check if the query destination is a vif connected to me. * and if so, whether I should start response back */ if (type == QUERY) { if (rt == NULL) { printf("Mcast traceroute: no route entry %s\n", inet_fmt(qry->tr_src, s1)); if (IN_MULTICAST(ntohl(dst))) return; } for (v = uvifs, vifi = 0; vifi < numvifs; ++vifi, ++v) if (!(v->uv_flags & VIFF_TUNNEL) && ((qry->tr_dst & v->uv_subnetmask) == v->uv_subnet)) break; if (vifi == numvifs) { printf("Destination %s not an interface\n", inet_fmt(qry->tr_dst, s1)); return; } if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) { printf("Destination %s not on forwarding tree for src %s\n", inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2)); return; } } else { /* * determine which interface the packet came in on */ if ((vifi = find_vif(src, dst)) == NO_VIF) { printf("Wrong interface for packet\n"); return; } } printf("Sending traceroute response\n"); /* copy the packet to the sending buffer */ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; bcopy(data, p, datalen); p += datalen; /* * fill in initial response fields */ resp = (struct tr_resp *)p; - resp->tr_qarr = ((tp.tv_sec & 0xffff) << 16) + - ((tp.tv_usec >> 4) & 0xffff); + resp->tr_qarr = ((tp.tv_sec + JAN_1970) << 16) + + ((tp.tv_usec << 10) / 15625); resp->tr_vifin = 0; /* default values */ resp->tr_pktcnt = 0; /* default values */ resp->tr_rproto = PROTO_DVMRP; resp->tr_smask = 0; resp->tr_outaddr = uvifs[vifi].uv_lcl_addr; resp->tr_fttl = uvifs[vifi].uv_threshold; resp->tr_rflags = TR_NO_ERR; /* * obtain # of packets out on interface */ v_req.vifi = vifi; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0) resp->tr_vifout = v_req.ocount; /* * fill in scoping & pruning information */ kt = find_src_grp(qry->tr_src, group); if (kt != NULL) { sg_req.src.s_addr = qry->tr_src; sg_req.grp.s_addr = group; if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0) resp->tr_pktcnt = sg_req.count; if (VIFM_ISSET(vifi, kt->kt_scope)) resp->tr_rflags = TR_SCOPED; else if (kt->kt_prsent_timer) resp->tr_rflags = TR_PRUNED; } /* * if no rte exists, set NO_RTE error */ if (rt == NULL) { src = dst; /* the dst address of resp. pkt */ - resp->tr_inaddr = NULL; + resp->tr_inaddr = 0; resp->tr_rflags = TR_NO_RTE; - resp->tr_rmtaddr = NULL; + resp->tr_rmtaddr = 0; } else { /* get # of packets in on interface */ v_req.vifi = rt->rt_parent; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0) resp->tr_vifin = v_req.icount; MASK_TO_VAL(rt->rt_originmask, resp->tr_smask); src = uvifs[rt->rt_parent].uv_lcl_addr; resp->tr_inaddr = src; resp->tr_rmtaddr = rt->rt_gateway; if (!VIFM_ISSET(vifi, rt->rt_children)) { printf("Destination %s not on forwarding tree for src %s\n", inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2)); resp->tr_rflags = TR_WRONG_IF; } } /* * if metric is 1 or no. of reports is 1, send response to requestor * else send to upstream router. */ printf("rcount:%d, no:%d\n", rcount, no); - if ((rcount + 1 == no) || (rt->rt_metric == 1)) + if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1)) dst = qry->tr_raddr; else dst = rt->rt_gateway; if (IN_MULTICAST(ntohl(dst))) { k_set_ttl(qry->tr_rttl); - send_igmp(src, dst, + send_igmp(INADDR_ANY, dst, IGMP_MTRACE_RESP, no, group, datalen + RLEN); k_set_ttl(1); } else send_igmp(src, dst, IGMP_MTRACE, no, group, datalen + RLEN); return; } diff --git a/usr.sbin/mrouted/vif.c b/usr.sbin/mrouted/vif.c index b71a043f3bbc..75115ad7c80b 100644 --- a/usr.sbin/mrouted/vif.c +++ b/usr.sbin/mrouted/vif.c @@ -1,1138 +1,1172 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: vif.c,v 1.8 1994/08/24 23:54:45 thyagara Exp $ + * $Id: vif.c,v 1.2 1994/09/08 02:51:27 wollman Exp $ */ #include "defs.h" /* * Exported variables. */ struct uvif uvifs[MAXVIFS]; /* array of virtual interfaces */ vifi_t numvifs; /* number of vifs in use */ int vifs_down; /* 1=>some interfaces are down */ int udp_socket; /* Since the honkin' kernel doesn't support */ /* ioctls on raw IP sockets, we need a UDP */ /* socket as well as our IGMP (raw) socket. */ /* How dumb. */ /* * Forward declarations. */ static void start_vif(); static void stop_vif(); +static void age_old_hosts(); /* * Initialize the virtual interfaces. */ void init_vifs() { vifi_t vifi; struct uvif *v; int enabled_vifs, enabled_phyints; numvifs = 0; vifs_down = FALSE; /* * Configure the vifs based on the interface configuration of the * the kernel and the contents of the configuration file. * (Open a UDP socket for ioctl use in the config procedures.) */ if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) log(LOG_ERR, errno, "UDP socket"); config_vifs_from_kernel(); config_vifs_from_file(); /* * Quit if there are fewer than two enabled vifs. */ enabled_vifs = 0; enabled_phyints = 0; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (!(v->uv_flags & VIFF_DISABLED)) { ++enabled_vifs; if (!(v->uv_flags & VIFF_TUNNEL)) ++enabled_phyints; } } if (enabled_vifs < 2) log(LOG_ERR, 0, "can't forward: %s", enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif"); if (enabled_phyints == 0) log(LOG_WARNING, 0, "no enabled interfaces, forwarding via tunnels only"); /* * Start routing on all virtual interfaces that are not down or * administratively disabled. */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (!(v->uv_flags & VIFF_DISABLED)) { if (!(v->uv_flags & VIFF_DOWN)) start_vif(vifi); else log(LOG_INFO, 0, "%s is not yet up; vif #%u not in service", v->uv_name, vifi); } } } /* * See if any interfaces have changed from up state to down, or vice versa, * including any non-multicast-capable interfaces that are in use as local * tunnel end-points. Ignore interfaces that have been administratively * disabled. */ void check_vif_state() { register vifi_t vifi; register struct uvif *v; struct ifreq ifr; vifs_down = FALSE; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (v->uv_flags & VIFF_DISABLED) continue; strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ); if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name); if (v->uv_flags & VIFF_DOWN) { if (ifr.ifr_flags & IFF_UP) { v->uv_flags &= ~VIFF_DOWN; start_vif(vifi); log(LOG_INFO, 0, "%s has come up; vif #%u now in service", v->uv_name, vifi); } else vifs_down = TRUE; } else { if (!(ifr.ifr_flags & IFF_UP)) { stop_vif(vifi); v->uv_flags |= VIFF_DOWN; log(LOG_INFO, 0, "%s has gone down; vif #%u taken out of service", v->uv_name, vifi); vifs_down = TRUE; } } } } /* * Start routing on the specified virtual interface. */ static void start_vif(vifi) vifi_t vifi; { struct uvif *v; u_long src, dst; int i; char *p; int datalen; struct listaddr *nbr; v = &uvifs[vifi]; src = v->uv_lcl_addr; dst = (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr : dvmrp_group; /* * Install the interface in the kernel's vif structure. */ + log(LOG_DEBUG, 0, "Installing vif %d in kernel\n", vifi); k_add_vif(vifi, &uvifs[vifi]); /* * Update the existing route entries to take into account the new vif. */ add_vif_to_routes(vifi); if (!(v->uv_flags & VIFF_TUNNEL)) { /* * Join the DVMRP multicast group on the interface. * (This is not strictly necessary, since the kernel promiscuously * receives IGMP packets addressed to ANY IP multicast group while * multicast routing is enabled. However, joining the group allows * this host to receive non-IGMP packets as well, such as 'pings'.) */ k_join(dvmrp_group, src); /* * Install an entry in the routing table for the subnet to which * the interface is connected. */ start_route_updates(); update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi); /* * Until neighbors are discovered, assume responsibility for sending * periodic group membership queries to the subnet. Send the first * query. */ v->uv_flags |= VIFF_QUERIER; send_igmp(src, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY, - GROUP_EXPIRE_TIME * 10 / ROUTE_MAX_REPORT_DELAY, 0, 0); + IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0); age_old_hosts(); } /* * Send a probe via the new vif to look for neighbors. */ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; datalen = 0; for (i = 0; i < 4; i++) *p++ = ((char *)&(dvmrp_genid))[i]; datalen += 4; /* * add the neighbor list on the interface to the message */ nbr = v->uv_neighbors; while (nbr) { for (i = 0; i < 4; i++) *p++ = ((char *)&nbr->al_addr)[i]; datalen +=4; nbr = nbr->al_next; } send_igmp(src, dst, IGMP_DVMRP, DVMRP_PROBE, htonl(MROUTED_LEVEL), datalen); } /* * Stop routing on the specified virtual interface. */ static void stop_vif(vifi) vifi_t vifi; { struct uvif *v; struct listaddr *a; v = &uvifs[vifi]; if (!(v->uv_flags & VIFF_TUNNEL)) { /* * Depart from the DVMRP multicast group on the interface. */ k_leave(dvmrp_group, v->uv_lcl_addr); /* * Update the entry in the routing table for the subnet to which * the interface is connected, to take into account the interface * failure. */ start_route_updates(); update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi); /* * Discard all group addresses. (No need to tell kernel; * the k_del_vif() call, below, will clean up kernel state.) */ while (v->uv_groups != NULL) { a = v->uv_groups; v->uv_groups = a->al_next; free((char *)a); } v->uv_flags &= ~VIFF_QUERIER; } /* * Update the existing route entries to take into account the vif failure. */ delete_vif_from_routes(vifi); /* * Delete the interface from the kernel's vif structure. */ k_del_vif(vifi); /* * Discard all neighbor addresses. */ while (v->uv_neighbors != NULL) { a = v->uv_neighbors; v->uv_neighbors = a->al_next; free((char *)a); } } /* * stop routing on all vifs */ void stop_all_vifs() { vifi_t vifi; struct uvif *v; struct listaddr *a; struct vif_acl *acl; for (vifi = 0; vifi < numvifs; vifi++) { v = &uvifs[vifi]; while (v->uv_groups != NULL) { a = v->uv_groups; v->uv_groups = a->al_next; free((char *)a); } while (v->uv_neighbors != NULL) { a = v->uv_neighbors; v->uv_neighbors = a->al_next; free((char *)a); } while (v->uv_acl != NULL) { acl = v->uv_acl; v->uv_acl = acl->acl_next; free((char *)acl); } } } /* * Find the virtual interface from which an incoming packet arrived, * based on the packet's source and destination IP addresses. */ vifi_t find_vif(src, dst) register u_long src; register u_long dst; { register vifi_t vifi; register struct uvif *v; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { if (v->uv_flags & VIFF_TUNNEL) { if (src == v->uv_rmt_addr && dst == v->uv_lcl_addr) return(vifi); } else { if ((src & v->uv_subnetmask) == v->uv_subnet && src != v->uv_subnetbcast) return(vifi); } } } return (NO_VIF); } -age_old_hosts() +static void age_old_hosts() { register vifi_t vifi; register struct uvif *v; register struct listaddr *g; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { /* -*- increment the time since an old report was heard */ for (g = v->uv_groups; g != NULL; g = g->al_next) { g->al_last ++; if (g->al_last >= OLD_AGE_THRESHOLD){ g->al_old = 0; g->al_last = OLD_AGE_THRESHOLD; } } } } /* * Send group membership queries to all subnets for which I am querier. */ void query_groups() { register vifi_t vifi; register struct uvif *v; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (v->uv_flags & VIFF_QUERIER) { send_igmp(v->uv_lcl_addr, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY, - GROUP_EXPIRE_TIME * 10 / ROUTE_MAX_REPORT_DELAY, 0, 0); + IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0); } } age_old_hosts(); } /* * Process an incoming group membership report. */ void accept_group_report(src, dst, group, r_type) u_long src, dst, group; int r_type; { register vifi_t vifi; register struct uvif *v; register struct listaddr *g; if ((vifi = find_vif(src, dst)) == NO_VIF || (uvifs[vifi].uv_flags & VIFF_TUNNEL)) { log(LOG_INFO, 0, "ignoring group membership report from non-adjacent host %s", inet_fmt(src, s1)); return; } v = &uvifs[vifi]; /* * Look for the group in our group list; if found, reset its timer. */ for (g = v->uv_groups; g != NULL; g = g->al_next) { if (group == g->al_addr) { if (r_type == IGMP_HOST_NEW_MEMBERSHIP_REPORT) { g->al_last = OLD_AGE_THRESHOLD; g->al_old = 0; } else { g->al_last = 0; g->al_old = 1; } /** delete old timer set a timer for expiration **/ g->al_timer= GROUP_EXPIRE_TIME; if (g->al_query) g->al_query = DeleteTimer(g->al_query); if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); g->al_timerid = SetTimer(vifi, g); break; } } /* * If not found, add it to the list and update kernel cache. */ if (g == NULL) { g = (struct listaddr *)malloc(sizeof(struct listaddr)); if (g == NULL) log(LOG_ERR, 0, "ran out of memory"); /* fatal */ g->al_addr = group; if (r_type == IGMP_HOST_NEW_MEMBERSHIP_REPORT){ g->al_last = OLD_AGE_THRESHOLD; g->al_old = 0; } else { g->al_last = 0; g->al_old = 1; } /** set a timer for expiration **/ g->al_query = 0; g->al_timer = GROUP_EXPIRE_TIME; g->al_timerid = SetTimer(vifi, g); g->al_next = v->uv_groups; v->uv_groups = g; update_lclgrp(vifi, group); } /* * Check if a graft is necessary for this group */ chkgrp_graft(vifi, group); } void leave_group_message( src, dst, group) u_long src, dst, group; { register vifi_t vifi; register struct uvif *v; register struct listaddr *g; if ((vifi = find_vif(src, dst)) == NO_VIF || (uvifs[vifi].uv_flags & VIFF_TUNNEL)) { log(LOG_INFO, 0, "ignoring group membership report from non-adjacent host %s", inet_fmt(src, s1)); return; } v = &uvifs[vifi]; /* * Look for the group in our group list; if found, reset its timer. */ for (g = v->uv_groups; g != NULL; g = g->al_next) { if (group == g->al_addr) { log(LOG_DEBUG, 0, "[vif.c, _leave_group_message] %d %d \n", g->al_old, g->al_query); if (g->al_old) return; /** delete old timer set a timer for expiration **/ if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); if (g->al_query) return; /** send a group specific querry **/ g->al_timer = GROUP_EXPIRE_TIME / 10; send_igmp(v->uv_lcl_addr, g->al_addr, IGMP_HOST_MEMBERSHIP_QUERY, - IGMP_MAX_HOST_REPORT_DELAY * 10 / (2*TIMER_INTERVAL), - 0, 0); + GROUP_EXPIRE_TIME / 30 * IGMP_TIMER_SCALE, + g->al_addr, 0); g->al_query = SetQueryTimer(g, vifi, g->al_timer / 3 , - IGMP_MAX_HOST_REPORT_DELAY / 2); + GROUP_EXPIRE_TIME / 30 * IGMP_TIMER_SCALE); g->al_timerid = SetTimer(vifi, g); break; } } } /* * Send a periodic probe on all vifs. * Useful to determine one-way interfaces. * Detect neighbor loss faster. */ void probe_for_neighbors() { register vifi_t vifi; register struct uvif *v; int i; register char *p; register int datalen = 0; struct listaddr *nbr; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; for (i = 0; i < 4; i++) *p++ = ((char *)&(dvmrp_genid))[i]; datalen += 4; /* * add the neighbor list on the interface to the message */ nbr = v->uv_neighbors; while (nbr) { for (i = 0; i < 4; i++) *p++ = ((char *)&nbr->al_addr)[i]; datalen +=4; nbr = nbr->al_next; } send_igmp(v->uv_lcl_addr, (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr : dvmrp_group, IGMP_DVMRP, DVMRP_PROBE, htonl(MROUTED_LEVEL), datalen); } } } /* * Send a list of all of our neighbors to the requestor, `src'. */ void accept_neighbor_request(src, dst) u_long src, dst; { vifi_t vifi; struct uvif *v; u_char *p, *ncount; struct listaddr *la; int datalen; u_long temp_addr, us, them = src; /* Determine which of our addresses to use as the source of our response * to this query. */ if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */ int udp; /* find best interface to reply on */ struct sockaddr_in addr; int addrlen = sizeof(addr); addr.sin_family = AF_INET; addr.sin_len = sizeof addr; addr.sin_addr.s_addr = dst; 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) { log(LOG_WARNING, errno, "Determining local address"); close(udp); return; } close(udp); us = addr.sin_addr.s_addr; } else /* query sent to us alone */ us = dst; #define PUT_ADDR(a) temp_addr = ntohl(a); \ *p++ = temp_addr >> 24; \ *p++ = (temp_addr >> 16) & 0xFF; \ *p++ = (temp_addr >> 8) & 0xFF; \ *p++ = temp_addr & 0xFF; p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (v->uv_flags & VIFF_DISABLED) continue; ncount = 0; for (la = v->uv_neighbors; la; la = la->al_next) { /* Make sure that there's room for this neighbor... */ if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) { send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(MROUTED_LEVEL), datalen); p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; ncount = 0; } /* Put out the header for this neighbor list... */ if (ncount == 0) { PUT_ADDR(v->uv_lcl_addr); *p++ = v->uv_metric; *p++ = v->uv_threshold; ncount = p; *p++ = 0; datalen += 4 + 3; } PUT_ADDR(la->al_addr); datalen += 4; (*ncount)++; } } if (datalen != 0) send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(MROUTED_LEVEL), datalen); } /* * Send a list of all of our neighbors to the requestor, `src'. */ void accept_neighbor_request2(src, dst) u_long src, dst; { vifi_t vifi; struct uvif *v; u_char *p, *ncount; struct listaddr *la; int datalen; - u_long temp_addr, us, them = src; + u_long us, them = src; /* Determine which of our addresses to use as the source of our response * to this query. */ if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */ int udp; /* find best interface to reply on */ struct sockaddr_in addr; int addrlen = sizeof(addr); addr.sin_family = AF_INET; addr.sin_len = sizeof addr; addr.sin_addr.s_addr = dst; 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) { log(LOG_WARNING, errno, "Determining local address"); close(udp); return; } close(udp); us = addr.sin_addr.s_addr; } else /* query sent to us alone */ us = dst; p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { register u_short vflags = v->uv_flags; register u_char rflags = 0; if (vflags & VIFF_TUNNEL) rflags |= DVMRP_NF_TUNNEL; if (vflags & VIFF_SRCRT) rflags |= DVMRP_NF_SRCRT; if (vflags & VIFF_DOWN) rflags |= DVMRP_NF_DOWN; if (vflags & VIFF_DISABLED) rflags |= DVMRP_NF_DISABLED; if (vflags & VIFF_QUERIER) rflags |= DVMRP_NF_QUERIER; ncount = 0; la = v->uv_neighbors; if (la == NULL) { /* * include down & disabled interfaces and interfaces on * leaf nets. */ if (rflags & DVMRP_NF_TUNNEL) rflags |= DVMRP_NF_DOWN; if (datalen > MAX_DVMRP_DATA_LEN - 12) { send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL), datalen); p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; } *(u_int*)p = v->uv_lcl_addr; p += 4; *p++ = v->uv_metric; *p++ = v->uv_threshold; *p++ = rflags; *p++ = 1; *(u_int*)p = v->uv_rmt_addr; p += 4; datalen += 12; } else { for ( ; la; la = la->al_next) { /* Make sure that there's room for this neighbor... */ if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) { send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL), datalen); p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; ncount = 0; } /* Put out the header for this neighbor list... */ if (ncount == 0) { *(u_int*)p = v->uv_lcl_addr; p += 4; *p++ = v->uv_metric; *p++ = v->uv_threshold; *p++ = rflags; ncount = p; *p++ = 0; datalen += 4 + 4; } *(u_int*)p = la->al_addr; p += 4; datalen += 4; (*ncount)++; } } } if (datalen != 0) send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL), datalen); } /* * Process an incoming neighbor-list message. */ void accept_neighbors(src, dst, p, datalen, level) u_long src, dst, level; char *p; int datalen; { log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming neighbor-list message. */ void accept_neighbors2(src, dst, p, datalen, level) u_long src, dst, level; char *p; int datalen; { log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Update the neighbor entry for neighbor 'addr' on vif 'vifi'. * 'msgtype' is the type of DVMRP message received from the neighbor. * Return TRUE if 'addr' is a valid neighbor, FALSE otherwise. */ int update_neighbor(vifi, addr, msgtype, p, datalen, level) vifi_t vifi; u_long addr; int msgtype; char *p; int datalen; u_long level; { register struct uvif *v; register struct listaddr *n; - u_long genid; + u_long genid = 0; u_long router; int he_hears_me = TRUE; v = &uvifs[vifi]; /* * Confirm that 'addr' is a valid neighbor address on vif 'vifi'. * IT IS ASSUMED that this was preceded by a call to find_vif(), which * checks that 'addr' is either a valid remote tunnel endpoint or a * non-broadcast address belonging to a directly-connected subnet. * Therefore, here we check only that 'addr' is not our own address * (due to an impostor or erroneous loopback) or an address of the form * {subnet,0} ("the unknown host"). These checks are not performed in * find_vif() because those types of address are acceptable for some * types of IGMP message (such as group membership reports). */ if (!(v->uv_flags & VIFF_TUNNEL) && (addr == v->uv_lcl_addr || addr == v->uv_subnet )) { log(LOG_WARNING, 0, "received DVMRP message from 'the unknown host' or self: %s", inet_fmt(addr, s1)); return (FALSE); } /* * If we have received a route report from a neighbor, and we believed * that we had no neighbors on this vif, send a full route report to * all neighbors on the vif. */ if (msgtype == DVMRP_REPORT && v->uv_neighbors == NULL) report(ALL_ROUTES, vifi, (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group); /* * Check if the router gen-ids are the same. * Need to reset the prune state of the router if not. */ if (msgtype == DVMRP_PROBE) { - int i; - - if (datalen < 4) { - log(LOG_WARNING, 0, - "received truncated probe message from %s", - inet_fmt(addr, s1)); - return; - } - - for (i = 0; i < 4; i++) - ((char *)&genid)[i] = *p++; - datalen -=4; - /* - * loop through router list and check for one-way ifs. - */ + /* if mrouted level > 3.2, analyze further */ + if ((level&0xff) > 3 || + (((level&0xff) == 3) && (((level>>8)&0xff) > 2))) { - he_hears_me = FALSE; + int i; - while (datalen > 0) { if (datalen < 4) { log(LOG_WARNING, 0, "received truncated probe message from %s", inet_fmt(addr, s1)); - return; + return FALSE; } + for (i = 0; i < 4; i++) - ((char *)&router)[i] = *p++; - datalen -= 4; - if (router == v->uv_lcl_addr) { - he_hears_me = TRUE; - break; + ((char *)&genid)[i] = *p++; + datalen -=4; + + /* + * loop through router list and check for one-way ifs. + */ + + he_hears_me = FALSE; + + while (datalen > 0) { + if (datalen < 4) { + log(LOG_WARNING, 0, + "received truncated probe message from %s", + inet_fmt(addr, s1)); + return (FALSE); + } + for (i = 0; i < 4; i++) + ((char *)&router)[i] = *p++; + datalen -= 4; + if (router == v->uv_lcl_addr) { + he_hears_me = TRUE; + break; + } } } } + /* * Look for addr in list of neighbors; if found, reset its timer. */ for (n = v->uv_neighbors; n != NULL; n = n->al_next) { if (addr == n->al_addr) { n->al_timer = 0; /* If probe message and version no >= 3.3 check genid */ if (msgtype == DVMRP_PROBE && ((n->al_pv >= 3 && n->al_mv > 2) || n->al_pv > 3)) { if (he_hears_me == TRUE && v->uv_flags & VIFF_ONEWAY) v->uv_flags &= ~VIFF_ONEWAY; if (he_hears_me == FALSE) v->uv_flags |= VIFF_ONEWAY; if ((n->al_genid != 0) && (n->al_genid != genid)) { log(LOG_DEBUG, 0, "old:%d new:%dreset neighbor %s", n->al_genid, genid, inet_fmt(addr, s1)); reset_neighbor_state(vifi, addr); n->al_genid = genid; - /* need to do a full route report here */ - break; + n->al_pv = level & 0xff; + n->al_mv = (level >> 8) & 0xff; + + /* + * need to do a full route report here + * it gets done by accept_probe() + */ + return (TRUE); } + } - /* recurring probe - so no need to do a route report */ - return FALSE; + /* + * update the neighbors version and protocol number + * if changed => router went down and came up, + * so take action immediately. + */ + if ((n->al_pv != (level & 0xff)) || + ((n->al_mv != (level >> 8)) & 0xff)) { + log(LOG_DEBUG, 0, + "resetting neighbor %s [old:%d.%d, new:%d.%d]", + inet_fmt(addr, s1), + n->al_pv, n->al_mv, level&0xff, (level>>8)&0xff); + + n->al_pv = level & 0xff; + n->al_mv = (level >> 8) & 0xff; + + reset_neighbor_state(vifi, addr); } - break; + /* recurring probe - so no need to do a route report */ + if (msgtype == DVMRP_PROBE) + return (FALSE); + else + return (TRUE); } } /* * If not found, add it to the list. If the neighbor has a lower * IP address than me, yield querier duties to it. */ if (n == NULL) { n = (struct listaddr *)malloc(sizeof(struct listaddr)); if (n == NULL) log(LOG_ERR, 0, "ran out of memory"); /* fatal */ n->al_addr = addr; n->al_pv = level & 0xff; n->al_mv = (level >> 8) & 0xff; if (msgtype == DVMRP_PROBE) n->al_genid = genid; else n->al_genid = 0; n->al_timer = 0; n->al_next = v->uv_neighbors; v->uv_neighbors = n; if (!(v->uv_flags & VIFF_TUNNEL) && ntohl(addr) < ntohl(v->uv_lcl_addr)) v->uv_flags &= ~VIFF_QUERIER; } return (TRUE); } /* * On every timer interrupt, advance the timer in each neighbor and * group entry on every vif. */ void age_vifs() { register vifi_t vifi; register struct uvif *v; register struct listaddr *a, *prev_a, *n; register u_long addr; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) { for (prev_a = (struct listaddr *)&(v->uv_neighbors), a = v->uv_neighbors; a != NULL; prev_a = a, a = a->al_next) { if ((a->al_timer += TIMER_INTERVAL) < NEIGHBOR_EXPIRE_TIME) continue; /* * Neighbor has expired; delete it from the neighbor list, * delete it from the 'dominants' and 'subordinates arrays of * any route entries and assume querier duties unless there is * another neighbor with a lower IP address than mine. */ addr = a->al_addr; prev_a->al_next = a->al_next; free((char *)a); a = prev_a; delete_neighbor_from_routes(addr, vifi); if (!(v->uv_flags & VIFF_TUNNEL)) { v->uv_flags |= VIFF_QUERIER; for (n = v->uv_neighbors; n != NULL; n = n->al_next) { if (ntohl(n->al_addr) < ntohl(v->uv_lcl_addr)) { v->uv_flags &= ~VIFF_QUERIER; break; } } } } } } /* * Print the contents of the uvifs array on file 'fp'. */ void dump_vifs(fp) FILE *fp; { register vifi_t vifi; register struct uvif *v; register struct listaddr *a; struct sioc_vif_req v_req; fprintf(fp, "\nVirtual Interface Table\n%s", "Vif Name Local-Address "); fprintf(fp, "M Thr Rate Flags\n"); for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { fprintf(fp, "%2u %6s %-15s %6s: %-15s %2u %3u %5u ", vifi, v->uv_name, inet_fmt(v->uv_lcl_addr, s1), (v->uv_flags & VIFF_TUNNEL) ? "tunnel": "subnet", (v->uv_flags & VIFF_TUNNEL) ? inet_fmt(v->uv_rmt_addr, s2) : inet_fmts(v->uv_subnet, v->uv_subnetmask, s3), v->uv_metric, v->uv_threshold, v->uv_rate_limit); if (v->uv_flags & VIFF_ONEWAY) fprintf(fp, " one-way"); if (v->uv_flags & VIFF_DOWN) fprintf(fp, " down"); if (v->uv_flags & VIFF_DISABLED) fprintf(fp, " disabled"); if (v->uv_flags & VIFF_QUERIER) fprintf(fp, " querier"); if (v->uv_flags & VIFF_SRCRT) fprintf(fp, " src-rt"); fprintf(fp, "\n"); if (v->uv_neighbors != NULL) { fprintf(fp, " peers: %s (%d.%d)\n", inet_fmt(v->uv_neighbors->al_addr, s1), v->uv_neighbors->al_pv, v->uv_neighbors->al_mv); for (a = v->uv_neighbors->al_next; a != NULL; a = a->al_next) { fprintf(fp, " %s (%d.%d)\n", inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv); } } if (v->uv_groups != NULL) { fprintf(fp, " groups: %-15s\n", inet_fmt(v->uv_groups->al_addr, s1)); for (a = v->uv_groups->al_next; a != NULL; a = a->al_next) { fprintf(fp, " %-15s\n", inet_fmt(a->al_addr, s1)); } } if (v->uv_acl != NULL) { struct vif_acl *acl; fprintf(fp, " boundaries: %-15s\n", inet_fmts(v->uv_acl->acl_addr, v->uv_acl->acl_mask, s1)); for (acl = v->uv_acl->acl_next; acl != NULL; acl = acl->acl_next) { fprintf(fp, " : %-15s\n", inet_fmts(acl->acl_addr, acl->acl_mask, s1)); } } v_req.vifi = vifi; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) < 0) { log(LOG_WARNING, 0, "SIOCGETVIFCNT fails"); } else { fprintf(fp, " pkts in : %d\n", v_req.icount); fprintf(fp, " pkts out: %d\n", v_req.ocount); } fprintf(fp, "\n"); } fprintf(fp, "\n"); } /**** the timeout routines ********/ typedef struct { vifi_t vifi; struct listaddr *g; int q_time; } cbk_t; static cbk_t *cbk; DelVif(cbk) cbk_t *cbk; { /* -*- make the list consistent */ register vifi_t vifi = cbk->vifi; register struct uvif *v; register struct listaddr *a, *prev_a, *g = cbk->g; v = &uvifs[vifi]; for (prev_a = (struct listaddr *)&(v->uv_groups), a = v->uv_groups; a != NULL; prev_a = a, a = a->al_next) { if (a != g) continue; /* * Group has expired * delete all kernel cache entries with this group */ if( g->al_query) DeleteTimer(g->al_query); delete_lclgrp(vifi, a->al_addr); prev_a->al_next = a->al_next; free((char *)a); a = prev_a; } free(cbk); } SetTimer( vifi, g) vifi_t vifi; struct listaddr *g; { cbk = (cbk_t *) malloc(sizeof(cbk_t)); cbk->g = g; cbk->vifi = vifi; return timer_setTimer(g->al_timer,DelVif,cbk); } DeleteTimer( id) int id; { timer_clearTimer(id); return 0; } SendQuery(cbk) cbk_t *cbk; { register struct uvif *v = &uvifs[cbk->vifi]; send_igmp(v->uv_lcl_addr, cbk->g->al_addr, IGMP_HOST_MEMBERSHIP_QUERY, - cbk->q_time * 10 / TIMER_INTERVAL, 0, 0); + cbk->q_time, 0, 0); cbk->g->al_query = 0; free(cbk); } SetQueryTimer(g , vifi, to_expire, q_time) struct listaddr *g; vifi_t vifi; int to_expire, q_time; { cbk = (cbk_t *) malloc(sizeof(cbk_t)); cbk->g = g; cbk->q_time = q_time; cbk-> vifi = vifi; return timer_setTimer(to_expire,SendQuery,cbk); }