diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /usr.sbin/mrouted |
initial import of NetBSD tree
Diffstat (limited to 'usr.sbin/mrouted')
-rw-r--r-- | usr.sbin/mrouted/LICENSE | 48 | ||||
-rw-r--r-- | usr.sbin/mrouted/Makefile | 11 | ||||
-rw-r--r-- | usr.sbin/mrouted/callout.c | 201 | ||||
-rw-r--r-- | usr.sbin/mrouted/cfparse.y | 512 | ||||
-rw-r--r-- | usr.sbin/mrouted/config.c | 145 | ||||
-rw-r--r-- | usr.sbin/mrouted/defs.h | 230 | ||||
-rw-r--r-- | usr.sbin/mrouted/dvmrp.h | 160 | ||||
-rw-r--r-- | usr.sbin/mrouted/igmp.c | 305 | ||||
-rw-r--r-- | usr.sbin/mrouted/inet.c | 202 | ||||
-rw-r--r-- | usr.sbin/mrouted/kern.c | 222 | ||||
-rw-r--r-- | usr.sbin/mrouted/main.c | 611 | ||||
-rw-r--r-- | usr.sbin/mrouted/mrouted.8 | 399 | ||||
-rw-r--r-- | usr.sbin/mrouted/pathnames.h | 17 | ||||
-rw-r--r-- | usr.sbin/mrouted/prune.c | 2259 | ||||
-rw-r--r-- | usr.sbin/mrouted/prune.h | 139 | ||||
-rw-r--r-- | usr.sbin/mrouted/route.c | 1139 | ||||
-rw-r--r-- | usr.sbin/mrouted/route.h | 50 | ||||
-rw-r--r-- | usr.sbin/mrouted/rsrr.c | 366 | ||||
-rw-r--r-- | usr.sbin/mrouted/rsrr.h | 140 | ||||
-rw-r--r-- | usr.sbin/mrouted/snmp.c | 1307 | ||||
-rw-r--r-- | usr.sbin/mrouted/snmp.h | 505 | ||||
-rw-r--r-- | usr.sbin/mrouted/vif.c | 1373 | ||||
-rw-r--r-- | usr.sbin/mrouted/vif.h | 76 |
23 files changed, 10417 insertions, 0 deletions
diff --git a/usr.sbin/mrouted/LICENSE b/usr.sbin/mrouted/LICENSE new file mode 100644 index 00000000000..ef7da470b11 --- /dev/null +++ b/usr.sbin/mrouted/LICENSE @@ -0,0 +1,48 @@ + +The mrouted program is covered by the following license. Use of the +mrouted program represents acceptance of these terms and conditions. + +1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license +to use, copy and modify the computer software ``mrouted'' (hereinafter +called the ``Program''), upon the terms and conditions hereinafter set +out and until Licensee discontinues use of the Licensed Program. + +2. LICENSEE acknowledges that the Program is a research tool still in +the development state, that it is being supplied ``as is,'' without any +accompanying services from STANFORD, and that this license is entered +into in order to encourage scientific collaboration aimed at further +development and application of the Program. + +3. LICENSEE may copy the Program and may sublicense others to use object +code copies of the Program or any derivative version of the Program. +All copies must contain all copyright and other proprietary notices found +in the Program as provided by STANFORD. Title to copyright to the +Program remains with STANFORD. + +4. LICENSEE may create derivative versions of the Program. LICENSEE +hereby grants STANFORD a royalty-free license to use, copy, modify, +distribute and sublicense any such derivative works. At the time +LICENSEE provides a copy of a derivative version of the Program to a +third party, LICENSEE shall provide STANFORD with one copy of the source +code of the derivative version at no charge to STANFORD. + +5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. +By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION +OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR +THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS, +COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable +for any liability nor for any direct, indirect or consequential damages +with respect to any claim by LICENSEE or any third party on account of or +arising from this Agreement or use of the Program. + +6. This agreement shall be construed, interpreted and applied in +accordance with the State of California and any legal action arising +out of this Agreement or use of the Program shall be filed in a court +in the State of California. + +7. Nothing in this Agreement shall be construed as conferring rights to +use in advertising, publicity or otherwise any trademark or the name +of ``Stanford''. + +The mrouted program is COPYRIGHT 1989 by The Board of Trustees of +Leland Stanford Junior University. diff --git a/usr.sbin/mrouted/Makefile b/usr.sbin/mrouted/Makefile new file mode 100644 index 00000000000..fc24ca1911d --- /dev/null +++ b/usr.sbin/mrouted/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.5 1995/10/09 03:51:32 thorpej Exp $ +# from: Id: Makefile,v 1.5 1993/06/24 05:11:16 deering Exp + +PROG= mrouted +SRCS= igmp.c inet.c kern.c config.c cfparse.c main.c route.c vif.c \ + prune.c callout.c +MAN= mrouted.8 + +CLEANFILES+= cfparse.c y.tab.h + +.include <bsd.prog.mk> diff --git a/usr.sbin/mrouted/callout.c b/usr.sbin/mrouted/callout.c new file mode 100644 index 00000000000..943ace50507 --- /dev/null +++ b/usr.sbin/mrouted/callout.c @@ -0,0 +1,201 @@ +/* $NetBSD: callout.c,v 1.2 1995/10/09 03:51:34 thorpej Exp $ */ + +/* + * 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. + */ + +#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*/ +}; + +static void print_Q(); + +void callout_init() +{ + Q = (struct timeout_q *) 0; +} + + +/* + * signal handler for SIGALARM that is called once every second + */ +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 -1; + + in_callout = 1; + + /* create a node */ + node = (struct timeout_q *)malloc(sizeof(struct timeout_q)); + if (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( timer_id) + int timer_id; +{ + struct timeout_q *ptr, *prev; + + if (in_callout) return; + in_callout = 1; + + + if ( !timer_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 == timer_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 + */ +static void print_Q() +{ +#ifdef IGMP_DEBUG + struct timeout_q *ptr; + + 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/cfparse.y b/usr.sbin/mrouted/cfparse.y new file mode 100644 index 00000000000..9538200f814 --- /dev/null +++ b/usr.sbin/mrouted/cfparse.y @@ -0,0 +1,512 @@ +%{ +/* $NetBSD: cfparse.y,v 1.3 1995/10/09 03:51:35 thorpej Exp $ */ + +/* + * Configuration file parser for mrouted. + * + * Written by Bill Fenner, NRL, 1994 + */ +#include <stdio.h> +#include <string.h> +#include <varargs.h> +#include "defs.h" + +static FILE *f; + +extern int udp_socket; +char *configfilename = _PATH_MROUTED_CONF; + +extern int cache_lifetime; +extern int max_prune_lifetime; + +static int lineno; +static struct ifreq ifbuf[32]; +static struct ifconf ifc; + +static struct uvif *v; + +static int order; + +struct addrmask { + u_int32_t addr; + int mask; +}; + +struct boundnam { + char *name; + struct addrmask bound; +}; + +#define MAXBOUNDS 20 + +struct boundnam boundlist[MAXBOUNDS]; /* Max. of 20 named boundaries */ +int numbounds = 0; /* Number of named boundaries */ + +%} + +%union +{ + int num; + char *ptr; + struct addrmask addrmask; + u_int32_t addr; +}; + +%token CACHE_LIFETIME PRUNING +%token PHYINT TUNNEL NAME +%token DISABLE METRIC THRESHOLD RATE_LIMIT SRCRT BOUNDARY NETMASK ALTNET +%token <num> BOOLEAN +%token <num> NUMBER +%token <ptr> STRING +%token <addrmask> ADDRMASK +%token <addr> ADDR + +%type <addr> interface +%type <addrmask> bound boundary addrmask + +%start conf + +%% + +conf : stmts + ; + +stmts : /* Empty */ + | stmts stmt + ; + +stmt : error + | PHYINT interface { + + vifi_t vifi; + + if (order) + fatal("phyints must appear before tunnels"); + + for (vifi = 0, v = uvifs; + vifi < numvifs; + ++vifi, ++v) + if (!(v->uv_flags & VIFF_TUNNEL) && + $2 == v->uv_lcl_addr) + break; + + if (vifi == numvifs) + fatal("%s is not a configured interface", + inet_fmt($2,s1)); + + /*log(LOG_INFO, 0, "phyint: %x\n", v);*/ + } + ifmods + | TUNNEL interface ADDR { + + struct ifreq *ifr; + struct ifreq ffr; + vifi_t vifi; + + order++; + + ifr = ifconfaddr(&ifc, $2); + if (ifr == 0) + fatal("Tunnel local address %s is not mine", + inet_fmt($2, s1)); + + strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ); + if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr)<0) + fatal("ioctl SIOCGIFFLAGS on %s",ffr.ifr_name); + if (ffr.ifr_flags & IFF_LOOPBACK) + fatal("Tunnel local address %s is a loopback interface", + inet_fmt($2, s1)); + + if (ifconfaddr(&ifc, $3) != 0) + fatal("Tunnel remote address %s is one of mine", + inet_fmt($3, s1)); + + for (vifi = 0, v = uvifs; + vifi < numvifs; + ++vifi, ++v) + if (v->uv_flags & VIFF_TUNNEL) { + if ($3 == v->uv_rmt_addr) + fatal("Duplicate tunnel to %s", + inet_fmt($3, s1)); + } else if (!(v->uv_flags & VIFF_DISABLED)) { + if (($3 & v->uv_subnetmask) == v->uv_subnet) + fatal("Unnecessary tunnel to %s", + inet_fmt($3,s1)); + } + + if (numvifs == MAXVIFS) + fatal("too many vifs"); + + v = &uvifs[numvifs]; + v->uv_flags = VIFF_TUNNEL; + v->uv_metric = DEFAULT_METRIC; + v->uv_rate_limit= DEFAULT_TUN_RATE_LIMIT; + v->uv_threshold = DEFAULT_THRESHOLD; + v->uv_lcl_addr = $2; + v->uv_rmt_addr = $3; + v->uv_subnet = 0; + v->uv_subnetmask= 0; + v->uv_subnetbcast= 0; + strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ); + v->uv_groups = NULL; + v->uv_neighbors = NULL; + v->uv_acl = NULL; + v->uv_addrs = NULL; + + if (!(ffr.ifr_flags & IFF_UP)) { + v->uv_flags |= VIFF_DOWN; + vifs_down = TRUE; + } + /*log(LOG_INFO, 0, "tunnel: %x\n", v);*/ + } + tunnelmods + { + log(LOG_INFO, 0, + "installing tunnel from %s to %s as vif #%u - rate=%d", + inet_fmt($2, s1), inet_fmt($3, s2), + numvifs, v->uv_rate_limit); + + ++numvifs; + } + | PRUNING BOOLEAN { pruning = $2; } + | CACHE_LIFETIME NUMBER { cache_lifetime = $2; + max_prune_lifetime = cache_lifetime * 2; + } + | NAME STRING boundary { if (numbounds >= MAXBOUNDS) { + fatal("Too many named boundaries (max %d)", MAXBOUNDS); + } + + boundlist[numbounds].name = malloc(strlen($2) + 1); + strcpy(boundlist[numbounds].name, $2); + boundlist[numbounds++].bound = $3; + } + ; + +tunnelmods : /* empty */ + | tunnelmods /*{ log(LOG_INFO, 0, "tunnelmod: %x", v); }*/ tunnelmod + ; + +tunnelmod : mod + | SRCRT { fatal("Source-route tunnels not supported"); } + ; + +ifmods : /* empty */ + | ifmods /*{ log(LOG_INFO, 0, "ifmod: %x", v); }*/ ifmod + ; + +ifmod : mod + | DISABLE { v->uv_flags |= VIFF_DISABLED; } + | NETMASK ADDR { v->uv_subnetmask = $2; } + | ALTNET addrmask { + + struct phaddr *ph; + + ph = (struct phaddr *)malloc(sizeof(struct phaddr)); + if (ph == NULL) + fatal("out of memory"); + if ($2.mask) { + VAL_TO_MASK(ph->pa_mask, $2.mask); + } else + ph->pa_mask = v->uv_subnetmask; + ph->pa_addr = $2.addr & ph->pa_mask; + if ($2.addr & ~ph->pa_mask) + warn("Extra addr %s/%d has host bits set", + inet_fmt($2.addr,s1), $2.mask); + ph->pa_next = v->uv_addrs; + v->uv_addrs = ph; + + } + ; + +mod : THRESHOLD NUMBER { if ($2 < 1 || $2 > 255) + fatal("Invalid threshold %d",$2); + v->uv_threshold = $2; + } + | THRESHOLD { + + warn("Expected number after threshold keyword"); + + } + | METRIC NUMBER { if ($2 < 1 || $2 > UNREACHABLE) + fatal("Invalid metric %d",$2); + v->uv_metric = $2; + } + | METRIC { + + warn("Expected number after metric keyword"); + + } + | RATE_LIMIT NUMBER { if ($2 > MAX_RATE_LIMIT) + fatal("Invalid rate_limit %d",$2); + v->uv_rate_limit = $2; + } + | RATE_LIMIT { + + warn("Expected number after rate_limit keyword"); + + } + | BOUNDARY bound { + + struct vif_acl *v_acl; + + v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl)); + if (v_acl == NULL) + fatal("out of memory"); + VAL_TO_MASK(v_acl->acl_mask, $2.mask); + v_acl->acl_addr = $2.addr & v_acl->acl_mask; + if ($2.addr & ~v_acl->acl_mask) + warn("Boundary spec %s/%d has host bits set", + inet_fmt($2.addr,s1),$2.mask); + v_acl->acl_next = v->uv_acl; + v->uv_acl = v_acl; + + } + | BOUNDARY { + + warn("Expected boundary spec after boundary keyword"); + + } + ; + +interface : ADDR { $$ = $1; } + | STRING { + $$ = valid_if($1); + if ($$ == 0) + fatal("Invalid interface name %s",$1); + } + ; + +bound : boundary { $$ = $1; } + | STRING { int i; + + for (i=0; i < numbounds; i++) { + if (!strcmp(boundlist[i].name, $1)) { + $$ = boundlist[i].bound; + break; + } + } + if (i == numbounds) { + fatal("Invalid boundary name %s",$1); + } + } + ; + +boundary : ADDRMASK { + + if ((ntohl($1.addr) & 0xff000000) != 0xef000000) { + fatal("Boundaries must be 239.x.x.x, not %s/%d", + inet_fmt($1.addr, s1), $1.mask); + } + $$ = $1; + + } + ; + +addrmask : ADDRMASK { $$ = $1; } + | ADDR { $$.addr = $1; $$.mask = 0; } + ; +%% +/*VARARGS1*/ +static void fatal(fmt, va_alist) +char *fmt; +va_dcl +{ + va_list ap; + char buf[200]; + + va_start(ap); + vsprintf(buf, fmt, ap); + va_end(ap); + + log(LOG_ERR,0,"%s: %s near line %d", configfilename, buf, lineno); +} + +/*VARARGS1*/ +static void warn(fmt, va_alist) +char *fmt; +va_dcl +{ + va_list ap; + char buf[200]; + + va_start(ap); + vsprintf(buf, fmt, ap); + va_end(ap); + + log(LOG_WARNING,0,"%s: %s near line %d", configfilename, buf, lineno); +} + +void yyerror(s) +char *s; +{ + log(LOG_ERR, 0, "%s: %s near line %d", configfilename, s, lineno); +} + +char *next_word() +{ + static char buf[1024]; + static char *p=NULL; + extern FILE *f; + char *q; + + while (1) { + if (!p || !*p) { + lineno++; + if (fgets(buf, sizeof(buf), f) == NULL) + return NULL; + p = buf; + } + while (*p && (*p == ' ' || *p == '\t')) /* skip whitespace */ + p++; + if (*p == '#') { + p = NULL; /* skip comments */ + continue; + } + q = p; + while (*p && *p != ' ' && *p != '\t' && *p != '\n') + p++; /* find next whitespace */ + *p++ = '\0'; /* null-terminate string */ + + if (!*q) { + p = NULL; + continue; /* if 0-length string, read another line */ + } + + return q; + } +} + +int yylex() +{ + int n; + u_int32_t addr; + char *q; + + if ((q = next_word()) == NULL) { + return 0; + } + + if (!strcmp(q,"cache_lifetime")) + return CACHE_LIFETIME; + if (!strcmp(q,"pruning")) + return PRUNING; + if (!strcmp(q,"phyint")) + return PHYINT; + if (!strcmp(q,"tunnel")) + return TUNNEL; + if (!strcmp(q,"disable")) + return DISABLE; + if (!strcmp(q,"metric")) + return METRIC; + if (!strcmp(q,"threshold")) + return THRESHOLD; + if (!strcmp(q,"rate_limit")) + return RATE_LIMIT; + if (!strcmp(q,"srcrt") || !strcmp(q,"sourceroute")) + return SRCRT; + if (!strcmp(q,"boundary")) + return BOUNDARY; + if (!strcmp(q,"netmask")) + return NETMASK; + if (!strcmp(q,"name")) + return NAME; + if (!strcmp(q,"altnet")) + return ALTNET; + if (!strcmp(q,"on") || !strcmp(q,"yes")) { + yylval.num = 1; + return BOOLEAN; + } + if (!strcmp(q,"off") || !strcmp(q,"no")) { + yylval.num = 0; + return BOOLEAN; + } + if (sscanf(q,"%[.0-9]/%d%c",s1,&n,s2) == 2) { + if ((addr = inet_parse(s1)) != 0xffffffff) { + yylval.addrmask.mask = n; + yylval.addrmask.addr = addr; + return ADDRMASK; + } + /* fall through to returning STRING */ + } + if (sscanf(q,"%[.0-9]%c",s1,s2) == 1) { + if ((addr = inet_parse(s1)) != 0xffffffff && + inet_valid_host(addr)) { + yylval.addr = addr; + return ADDR; + } + } + if (sscanf(q,"0x%8x%c",&n,s1) == 1) { + yylval.addr = n; + return ADDR; + } + if (sscanf(q,"%d%c",&n,s1) == 1) { + yylval.num = n; + return NUMBER; + } + yylval.ptr = q; + return STRING; +} + +void config_vifs_from_file() +{ + extern FILE *f; + + order = 0; + numbounds = 0; + lineno = 0; + + if ((f = fopen(configfilename, "r")) == NULL) { + if (errno != ENOENT) + log(LOG_ERR, errno, "can't open %s", configfilename); + return; + } + + ifc.ifc_buf = (char *)ifbuf; + ifc.ifc_len = sizeof(ifbuf); + if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0) + log(LOG_ERR, errno, "ioctl SIOCGIFCONF"); + + yyparse(); + + close(f); +} + +static u_int32_t +valid_if(s) +char *s; +{ + register vifi_t vifi; + register struct uvif *v; + + for (vifi=0, v=uvifs; vifi<numvifs; vifi++, v++) + if (!strcmp(v->uv_name, s)) + return v->uv_lcl_addr; + + return 0; +} + +static struct ifreq * +ifconfaddr(ifcp, a) + struct ifconf *ifcp; + u_int32_t a; +{ + int n; + struct ifreq *ifrp = (struct ifreq *)ifcp->ifc_buf; + struct ifreq *ifend = (struct ifreq *)((char *)ifrp + ifcp->ifc_len); + + while (ifrp < ifend) { + if (ifrp->ifr_addr.sa_family == AF_INET && + ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr == a) + return (ifrp); +#if (defined(BSD) && (BSD >= 199006)) + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + ++ifrp; + else + ifrp = (struct ifreq *)((char *)ifrp + n); +#else + ++ifrp; +#endif + } + return (0); +} diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c new file mode 100644 index 00000000000..3a4e1bd2f60 --- /dev/null +++ b/usr.sbin/mrouted/config.c @@ -0,0 +1,145 @@ +/* $NetBSD: config.c,v 1.5 1995/10/09 03:51:37 thorpej Exp $ */ + +/* + * 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. + */ + + +#include "defs.h" + + +/* + * Query the kernel to find network interfaces that are multicast-capable + * and install them in the uvifs array. + */ +void config_vifs_from_kernel() +{ + struct ifreq ifbuf[32]; + struct ifreq *ifrp, *ifend; + struct ifconf ifc; + register struct uvif *v; + register vifi_t vifi; + int n; + u_int32_t addr, mask, subnet; + short flags; + + ifc.ifc_buf = (char *)ifbuf; + ifc.ifc_len = sizeof(ifbuf); + if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0) + log(LOG_ERR, errno, "ioctl SIOCGIFCONF"); + + ifrp = (struct ifreq *)ifbuf; + ifend = (struct ifreq *)((char *)ifbuf + ifc.ifc_len); + /* + * Loop through all of the interfaces. + */ + for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) { + struct ifreq ifr; +#if BSD >= 199006 + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + n = sizeof(*ifrp); +#else + n = sizeof(*ifrp); +#endif + /* + * Ignore any interface for an address family other than IP. + */ + if (ifrp->ifr_addr.sa_family != AF_INET) + continue; + + addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr; + + /* + * Need a template to preserve address info that is + * used below to locate the next entry. (Otherwise, + * SIOCGIFFLAGS stomps over it because the requests + * are returned in a union.) + */ + bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); + + /* + * Ignore loopback interfaces and interfaces that do not support + * multicast. + */ + if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) + log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name); + flags = ifr.ifr_flags; + if ((flags & (IFF_LOOPBACK|IFF_MULTICAST)) != IFF_MULTICAST) continue; + + /* + * Ignore any interface whose address and mask do not define a + * valid subnet number, or whose address is of the form {subnet,0} + * or {subnet,-1}. + */ + if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0) + log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", ifr.ifr_name); + mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + subnet = addr & mask; + if (!inet_valid_subnet(subnet, mask) || + addr == subnet || + addr == (subnet | ~mask)) { + log(LOG_WARNING, 0, + "ignoring %s, has invalid address (%s) and/or mask (%s)", + ifr.ifr_name, inet_fmt(addr, s1), inet_fmt(mask, s2)); + continue; + } + + /* + * Ignore any interface that is connected to the same subnet as + * one already installed in the uvifs array. + */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if ((addr & v->uv_subnetmask) == v->uv_subnet || + (v->uv_subnet & mask) == subnet) { + log(LOG_WARNING, 0, "ignoring %s, same subnet as %s", + ifr.ifr_name, v->uv_name); + break; + } + } + if (vifi != numvifs) continue; + + /* + * If there is room in the uvifs array, install this interface. + */ + if (numvifs == MAXVIFS) { + log(LOG_WARNING, 0, "too many vifs, ignoring %s", ifr.ifr_name); + continue; + } + v = &uvifs[numvifs]; + v->uv_flags = 0; + v->uv_metric = DEFAULT_METRIC; + v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT; + v->uv_threshold = DEFAULT_THRESHOLD; + v->uv_lcl_addr = addr; + v->uv_rmt_addr = 0; + v->uv_subnet = subnet; + v->uv_subnetmask = mask; + v->uv_subnetbcast = subnet | ~mask; + strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ); + v->uv_groups = NULL; + v->uv_neighbors = NULL; + v->uv_acl = NULL; + v->uv_addrs = NULL; + + log(LOG_INFO,0,"installing %s (%s on subnet %s) as vif #%u - rate=%d", + v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2), + numvifs, v->uv_rate_limit); + + ++numvifs; + + /* + * If the interface is not yet up, set the vifs_down flag to + * remind us to check again later. + */ + if (!(flags & IFF_UP)) { + v->uv_flags |= VIFF_DOWN; + vifs_down = TRUE; + } + } +} diff --git a/usr.sbin/mrouted/defs.h b/usr.sbin/mrouted/defs.h new file mode 100644 index 00000000000..8d5aa9c6d3a --- /dev/null +++ b/usr.sbin/mrouted/defs.h @@ -0,0 +1,230 @@ +/* $NetBSD: defs.h,v 1.5 1995/10/09 03:51:38 thorpej Exp $ */ + +/* + * 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. + */ + + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/igmp.h> +#include <netinet/ip_mroute.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <syslog.h> +#include <signal.h> +#ifdef RSRR +#include <sys/un.h> +#endif /* RSRR */ + +#include "dvmrp.h" +#include "vif.h" +#include "route.h" +#include "prune.h" +#include "pathnames.h" +#ifdef RSRR +#include "rsrr.h" +#endif /* RSRR */ + +/* + * 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 5 /* increment on local changes or bug fixes, */ + /* reset to 0 whever PROTOCOL_VERSION increments */ + +#define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION | \ + ((NF_PRUNE | NF_GENID | NF_MTRACE) << 16)) + /* for IGMP 'group' field of DVMRP messages */ + +#define LEAF_FLAGS (( vifs_with_neighbors == 1 ) ? 0x010000 : 0) + /* more for IGMP 'group' field of DVMRP messages */ +#define DEL_RTE_GROUP 0 +#define DEL_ALL_ROUTES 1 + /* for Deleting kernel table entries */ + +/* obnoxious gcc gives an extraneous warning about this constant... */ +#if defined(__STDC__) || defined(__GNUC__) +#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ +#else +#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */ +#endif + +#ifdef RSRR +#define BIT_ZERO(X) ((X) = 0) +#define BIT_SET(X,n) ((X) |= 1 << (n)) +#define BIT_CLR(X,n) ((X) &= ~(1 << (n))) +#define BIT_TST(X,n) ((X) & 1 << (n)) +#endif /* RSRR */ + +/* + * External declarations for global variables and functions. + */ +#define RECV_BUF_SIZE MAX_IP_PACKET_LEN +extern char *recv_buf; +extern char *send_buf; +extern int igmp_socket; +#ifdef RSRR +extern int rsrr_socket; +#endif /* RSRR */ +extern u_int32_t allhosts_group; +extern u_int32_t allrtrs_group; +extern u_int32_t dvmrp_group; +extern u_int32_t 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 int vifs_with_neighbors; + +extern char s1[]; +extern char s2[]; +extern char s3[]; +extern char s4[]; + +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 struct listaddr *neighbor_info(); + +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 accept_neighbor_request2(); +extern void accept_neighbors(); +extern void accept_neighbors2(); + +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_int32_t inet_parse(); +extern int inet_cksum(); + +extern struct rtentry * determine_route(); + +extern void init_ktable(); +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 accept_prune(); +extern int no_entry_exists(); +extern int rtr_cnt(); +extern void free_all_prunes(); +extern void age_table_entry(); +extern void dump_cache(); + +#ifdef SNMP +extern struct rtentry * snmp_find_route(); +extern struct gtable * find_grp(); +extern struct stable * find_grp_src(); +#endif + +extern void chkgrp_graft(); +extern void accept_graft(); +extern void accept_g_ack(); +extern void accept_mtrace(); +extern void accept_leave_message(); +extern void accept_membership_query(); +#ifdef RSRR +extern struct gtable *kernel_table; +extern struct gtable *gtp; +extern int find_src_grp(); +extern int grplst_mem(); +extern int scoped_addr(); +#endif /* RSRR */ + +extern void k_set_rcvbuf(); +extern void k_hdr_include(); +extern void k_set_ttl(); +extern void k_set_loop(); +extern void k_set_if(); +extern void k_join(); +extern void k_leave(); +extern void k_init_dvmrp(); +extern void k_stop_dvmrp(); +extern void k_add_vif(); +extern void k_del_vif(); +extern void k_add_rg(); +extern int k_del_rg(); +extern int k_get_version(); + +extern char * malloc(); +extern char * fgets(); +extern FILE * fopen(); + +#if !defined(htonl) && !defined(__osf__) +extern u_long htonl(); +extern u_long ntohl(); +#endif + +#ifdef RSRR +extern void rsrr_init(); +extern void rsrr_read(); +#endif /* RSRR */ diff --git a/usr.sbin/mrouted/dvmrp.h b/usr.sbin/mrouted/dvmrp.h new file mode 100644 index 00000000000..c008dd37151 --- /dev/null +++ b/usr.sbin/mrouted/dvmrp.h @@ -0,0 +1,160 @@ +/* $NetBSD: dvmrp.h,v 1.4 1995/10/09 03:51:39 thorpej Exp $ */ + +/* + * 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. + */ + +/* + * A DVMRP message consists of an IP header + an IGMP header + (for some types) + * zero or more bytes of data. + * + * For REPORT messages, the data is route information; the route information + * consists of one or more lists of the following form: + * + * (mask, (origin, metric), (origin, metric), ...) + * + * where: + * + * "mask" is the subnet mask for all the origins in the list. + * It is always THREE bytes long, containing the low-order + * three bytes of the mask (the high-order byte is always + * 0xff and therefore need not be transmitted). + * + * "origin" is the number of a subnet from which multicast datagrams + * may originate. It is from one to four bytes long, + * depending on the value of "mask": + * if all bytes of the mask are zero + * the subnet number is one byte long + * else if the low-order two bytes of the mask are zero + * the subnet number is two bytes long + * else if the lowest-order byte of the mask is zero + * the subnet number is three bytes long, + * else + * the subnet number is four bytes long. + * + * "metric" is a one-byte value consisting of two subfields: + * - the high-order bit is a flag which, when set, indicates + * the last (origin, metric) pair of a list. + * - the low-order seven bits contain the routing metric for + * the corresponding origin, relative to the sender of the + * DVMRP report. The metric may have the value of UNREACHABLE + * added to it as a "split horizon" indication (so called + * "poisoned reverse"). + * + * Within a list, the origin subnet numbers must be in ascending order, and + * the lists themselves are in order of increasing mask value. A message may + * not exceed 576 bytes, the default maximum IP reassembly size, including + * the IP and IGMP headers; the route information may be split across more + * than one message if necessary, by terminating a list in one message and + * starting a new list in the next message (repeating the same mask value, + * if necessary). + * + * For NEIGHBORS messages, the data is neighboring-router information + * consisting of one or more lists of the following form: + * + * (local-addr, metric, threshold, ncount, neighbor, neighbor, ...) + * + * where: + * + * "local-addr" is the sending router's address as seen by the neighbors + * in this list; it is always four bytes long. + * "metric" is a one-byte unsigned value, the TTL `cost' of forwarding + * packets to any of the neighbors on this list. + * "threshold" is a one-byte unsigned value, a lower bound on the TTL a + * packet must have to be forwarded to any of the neighbors on + * this list. + * "ncount" is the number of neighbors in this list. + * "neighbor" is the address of a neighboring router, four bytes long. + * + * As with REPORT messages, NEIGHBORS messages should not exceed 576 bytes, + * including the IP and IGMP headers; split longer messages by terminating the + * list in one and continuing in another, repeating the local-addr, etc., if + * necessary. + * + * For NEIGHBORS2 messages, the data is identical to NEIGHBORS except + * there is a flags byte before the neighbor count: + * + * (local-addr, metric, threshold, flags, ncount, neighbor, neighbor, ...) + */ + +/* + * DVMRP message types (carried in the "code" field of an IGMP header) + */ +#define DVMRP_PROBE 1 /* for finding neighbors */ +#define DVMRP_REPORT 2 /* for reporting some or all routes */ +#define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */ + /* of this router's neighbors. */ +#define DVMRP_NEIGHBORS 4 /* response to such a request */ +#define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */ +#define DVMRP_NEIGHBORS2 6 +#define DVMRP_PRUNE 7 /* prune message */ +#define DVMRP_GRAFT 8 /* graft message */ +#define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */ + +/* + * 'flags' byte values in DVMRP_NEIGHBORS2 reply. + */ +#define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */ +#define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */ +#define DVMRP_NF_PIM 0x04 /* neighbor is a PIM neighbor */ +#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */ +#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */ +#define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */ +#define DVMRP_NF_LEAF 0x80 /* Neighbor reports that it is a leaf */ + +/* + * Limit on length of route data + */ +#define MAX_IP_PACKET_LEN 576 +#define MIN_IP_HEADER_LEN 20 +#define MAX_IP_HEADER_LEN 60 +#define MAX_DVMRP_DATA_LEN \ + ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN ) + +/* + * Various protocol constants (all times in seconds) + */ + /* address for multicast DVMRP msgs */ +#define INADDR_DVMRP_GROUP (u_int32_t)0xe0000004 /* 224.0.0.4 */ + /* address for multicast mtrace msg */ +#define INADDR_ALLRTRS_GROUP (u_int32_t)0xe0000002 /* 224.0.0.2 */ + +#define ROUTE_MAX_REPORT_DELAY 5 /* max delay for reporting changes */ + /* (This is the timer interrupt */ + /* interval; all times must be */ + /* multiples of this value.) */ + +#define ROUTE_REPORT_INTERVAL 60 /* periodic route report interval */ +#define ROUTE_SWITCH_TIME 140 /* time to switch to equivalent gw */ +#define ROUTE_EXPIRE_TIME 200 /* time to mark route invalid */ +#define ROUTE_DISCARD_TIME 340 /* time to garbage collect route */ + +#define LEAF_CONFIRMATION_TIME 200 /* time to consider subnet a leaf */ + +#define NEIGHBOR_PROBE_INTERVAL 10 /* periodic neighbor probe interval */ +#define NEIGHBOR_EXPIRE_TIME 140 /* time to consider neighbor gone */ + +#define GROUP_QUERY_INTERVAL 125 /* periodic group query interval */ +#define GROUP_EXPIRE_TIME 270 /* time to consider group gone */ +#define LEAVE_EXPIRE_TIME 3 /* " " after receiving a leave */ +/* Note: LEAVE_EXPIRE_TIME should ideally be shorter, but the resolution of + * the timer in mrouted doesn't allow us to follow the spec and make it any + * shorter. */ + +#define UNREACHABLE 32 /* "infinity" metric, must be <= 64 */ +#define DEFAULT_METRIC 1 /* default subnet/tunnel metric */ +#define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */ + +#define MAX_RATE_LIMIT 100000 /* max rate limit */ +#define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */ +#define DEFAULT_TUN_RATE_LIMIT 500 /* default tunnel rate limit */ + +#define DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */ +#define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */ + +#define OLD_AGE_THRESHOLD 2 diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c new file mode 100644 index 00000000000..f4c19d85b53 --- /dev/null +++ b/usr.sbin/mrouted/igmp.c @@ -0,0 +1,305 @@ +/* $NetBSD: igmp.c,v 1.4 1995/10/09 03:51:40 thorpej Exp $ */ + +/* + * 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. + */ + + +#include "defs.h" + + +/* + * Exported variables. + */ +char *recv_buf; /* input packet buffer */ +char *send_buf; /* output packet buffer */ +int igmp_socket; /* socket for all network I/O */ +u_int32_t allhosts_group; /* All hosts addr in net order */ +u_int32_t allrtrs_group; /* All-Routers " in net order */ +u_int32_t dvmrp_group; /* DVMRP grp addr in net order */ +u_int32_t 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; + + recv_buf = malloc(RECV_BUF_SIZE); + send_buf = malloc(RECV_BUF_SIZE); + + 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); + allrtrs_group = htonl(INADDR_ALLRTRS_GROUP); +} + +#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_v1_HOST_MEMBERSHIP_REPORT: return "membership report "; + case IGMP_v2_HOST_MEMBERSHIP_REPORT: return "new member 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: + 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_QUERY: return "IGMP trace query "; + case IGMP_MTRACE_REPLY: 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 u_int32_t 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 == 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: + accept_membership_query(src, dst, group, igmp->igmp_code); + return; + + case IGMP_v1_HOST_MEMBERSHIP_REPORT: + case IGMP_v2_HOST_MEMBERSHIP_REPORT: + accept_group_report(src, dst, group, igmp->igmp_type); + return; + + case IGMP_HOST_LEAVE_MESSAGE: + accept_leave_message(src, dst, group); + return; + + case IGMP_DVMRP: + group = ntohl(group); + + switch (igmp->igmp_code) { + case DVMRP_PROBE: + accept_probe(src, dst, + (char *)(igmp+1), igmpdatalen, group); + return; + + case DVMRP_REPORT: + accept_report(src, dst, + (char *)(igmp+1), igmpdatalen, 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: + return; + + case IGMP_MTRACE_REPLY: + return; + + case IGMP_MTRACE_QUERY: + accept_mtrace(src, dst, group, (char *)(igmp+1), + igmp->igmp_code, igmpdatalen); + return; + + default: + log(LOG_INFO, 0, + "ignoring unknown IGMP message type %x 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_int32_t src, dst; + int type, code; + u_int32_t group; + int datalen; +{ + static struct sockaddr_in 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); + + bzero(&sdst, sizeof(sdst)); + sdst.sin_family = AF_INET; +#if (defined(BSD) && (BSD >= 199103)) + sdst.sin_len = sizeof(sdst); +#endif + 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 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/inet.c b/usr.sbin/mrouted/inet.c new file mode 100644 index 00000000000..b6c6a01788d --- /dev/null +++ b/usr.sbin/mrouted/inet.c @@ -0,0 +1,202 @@ +/* $NetBSD: inet.c,v 1.3 1995/10/09 03:51:42 thorpej Exp $ */ + +/* + * 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. + */ + + +#include "defs.h" + + +/* + * Exported variables. + */ +char s1[19]; /* buffers to hold the string representations */ +char s2[19]; /* of IP addresses, to be passed to inet_fmt() */ +char s3[19]; /* or inet_fmts(). */ +char s4[19]; + + +/* + * Verify that a given IP address is credible as a host address. + * (Without a mask, cannot detect addresses of the form {subnet,0} or + * {subnet,-1}.) + */ +int inet_valid_host(naddr) + u_int32_t naddr; +{ + register u_int32_t addr; + + addr = ntohl(naddr); + + return (!(IN_MULTICAST(addr) || + IN_BADCLASS (addr) || + (addr & 0xff000000) == 0)); +} + + +/* + * Verify that a given subnet number and mask pair are credible. + * + * With CIDR, almost any subnet and mask are credible. mrouted still + * can't handle aggregated class A's, so we still check that, but + * otherwise the only requirements are that the subnet address is + * within the [ABC] range and that the host bits of the subnet + * are all 0. + */ +int inet_valid_subnet(nsubnet, nmask) + u_int32_t nsubnet, nmask; +{ + register u_int32_t subnet, mask; + + subnet = ntohl(nsubnet); + mask = ntohl(nmask); + + if ((subnet & mask) != subnet) return (FALSE); + + if (subnet == 0 && mask == 0) + return (TRUE); + + if (IN_CLASSA(subnet)) { + if (mask < 0xff000000 || + (subnet & 0xff000000) == 0x7f000000) return (FALSE); + } + else if (IN_CLASSD(subnet) || IN_BADCLASS(subnet)) { + /* Above Class C address space */ + return (FALSE); + } + else if (subnet & ~mask) { + /* Host bits are set in the subnet */ + return (FALSE); + } + + return (TRUE); +} + + +/* + * Convert an IP address in u_long (network) format into a printable string. + */ +char *inet_fmt(addr, s) + u_int32_t addr; + char *s; +{ + register u_char *a; + + a = (u_char *)&addr; + sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]); + return (s); +} + + +/* + * Convert an IP subnet number in u_long (network) format into a printable + * string including the netmask as a number of bits. + */ +char *inet_fmts(addr, mask, s) + u_int32_t addr, mask; + char *s; +{ + register u_char *a, *m; + int bits; + + if ((addr == 0) && (mask == 0)) { + sprintf(s, "default"); + return (s); + } + a = (u_char *)&addr; + m = (u_char *)&mask; + bits = 33 - ffs(ntohl(mask)); + + if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3], + bits); + else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d", a[0], a[1], a[2], bits); + else if (m[1] != 0) sprintf(s, "%u.%u/%d", a[0], a[1], bits); + else sprintf(s, "%u/%d", a[0], bits); + + return (s); +} + +/* + * Convert the printable string representation of an IP address into the + * u_long (network) format. Return 0xffffffff on error. (To detect the + * legal address with that value, you must explicitly compare the string + * with "255.255.255.255".) + */ +u_int32_t inet_parse(s) + char *s; +{ + u_int32_t a = 0; + u_int a0, a1, a2, a3; + char c; + + if (sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c) != 4 || + a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255) + return (0xffffffff); + + ((u_char *)&a)[0] = a0; + ((u_char *)&a)[1] = a1; + ((u_char *)&a)[2] = a2; + ((u_char *)&a)[3] = a3; + + return (a); +} + + +/* + * inet_cksum extracted from: + * P I N G . C + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * Modified at Uc Berkeley + * + * (ping.c) Status - + * Public Domain. Distribution Unlimited. + * + * I N _ C K S U M + * + * Checksum routine for Internet Protocol family headers (C Version) + * + */ +int inet_cksum(addr, len) + u_short *addr; + u_int len; +{ + register int nleft = (int)len; + register u_short *w = addr; + u_short answer = 0; + register int sum = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while( nleft > 1 ) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if( nleft == 1 ) { + *(u_char *) (&answer) = *(u_char *)w ; + sum += answer; + } + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} diff --git a/usr.sbin/mrouted/kern.c b/usr.sbin/mrouted/kern.c new file mode 100644 index 00000000000..0dbae18624a --- /dev/null +++ b/usr.sbin/mrouted/kern.c @@ -0,0 +1,222 @@ +/* $NetBSD: kern.c,v 1.3 1995/10/09 03:51:43 thorpej Exp $ */ + +/* + * 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. + */ + + +#include "defs.h" + + +void k_set_rcvbuf(bufsize) + int bufsize; +{ + if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF, + (char *)&bufsize, sizeof(bufsize)) < 0) + log(LOG_ERR, errno, "setsockopt SO_RCVBUF %u", bufsize); +} + + +void k_hdr_include(bool) + int bool; +{ +#ifdef IP_HDRINCL + if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL, + (char *)&bool, sizeof(bool)) < 0) + log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool); +#endif +} + + +void k_set_ttl(t) + int t; +{ + u_char ttl; + + ttl = t; + if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&ttl, sizeof(ttl)) < 0) + log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl); +} + + +void k_set_loop(l) + int l; +{ + u_char loop; + + loop = l; + if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&loop, sizeof(loop)) < 0) + log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop); +} + + +void k_set_if(ifa) + u_int32_t ifa; +{ + struct in_addr adr; + + adr.s_addr = ifa; + if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&adr, sizeof(adr)) < 0) + log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s", + inet_fmt(ifa, s1)); +} + + +void k_join(grp, ifa) + u_int32_t grp; + u_int32_t ifa; +{ + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = grp; + mreq.imr_interface.s_addr = ifa; + + if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)) < 0) + log(LOG_WARNING, errno, "can't join group %s on interface %s", + inet_fmt(grp, s1), inet_fmt(ifa, s2)); +} + + +void k_leave(grp, ifa) + u_int32_t grp; + u_int32_t ifa; +{ + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = grp; + mreq.imr_interface.s_addr = ifa; + + if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)) < 0) + log(LOG_WARNING, errno, "can't leave group %s on interface %s", + inet_fmt(grp, s1), inet_fmt(ifa, s2)); +} + + +void k_init_dvmrp() +{ +#ifdef OLD_KERNEL + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT, + (char *)NULL, 0) < 0) +#else + int v=1; + + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT, + (char *)&v, sizeof(int)) < 0) +#endif + log(LOG_ERR, errno, "can't enable Multicast routing in kernel"); +} + + +void k_stop_dvmrp() +{ + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DONE, + (char *)NULL, 0) < 0) + log(LOG_WARNING, errno, "can't disable Multicast routing in kernel"); +} + + +void k_add_vif(vifi, v) + vifi_t vifi; + struct uvif *v; +{ + struct vifctl vc; + + vc.vifc_vifi = vifi; + vc.vifc_flags = v->uv_flags & VIFF_KERNEL_FLAGS; + vc.vifc_threshold = v->uv_threshold; + vc.vifc_rate_limit = v->uv_rate_limit; + vc.vifc_lcl_addr.s_addr = v->uv_lcl_addr; + vc.vifc_rmt_addr.s_addr = v->uv_rmt_addr; + + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_VIF, + (char *)&vc, sizeof(vc)) < 0) + log(LOG_ERR, errno, "setsockopt MRT_ADD_VIF"); +} + + +void k_del_vif(vifi) + vifi_t vifi; +{ + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_VIF, + (char *)&vifi, sizeof(vifi)) < 0) + log(LOG_ERR, errno, "setsockopt MRT_DEL_VIF"); +} + + +/* + * Adds a (source, mcastgrp) entry to the kernel + */ +void k_add_rg(origin, g) + u_long origin; + struct gtable *g; +{ + struct mfcctl mc; + int i; + + /* copy table values so that setsockopt can process it */ + mc.mfcc_origin.s_addr = origin; +#ifdef OLD_KERNEL + mc.mfcc_originmask.s_addr = 0xffffffff; +#endif + mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp; + mc.mfcc_parent = g->gt_route ? g->gt_route->rt_parent : NO_VIF; + for (i = 0; i < numvifs; i++) + mc.mfcc_ttls[i] = g->gt_ttls[i]; + + /* write to kernel space */ + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_MFC, + (char *)&mc, sizeof(mc)) < 0) + log(LOG_WARNING, errno, "setsockopt MRT_ADD_MFC"); +} + + +/* + * Deletes a (source, mcastgrp) entry from the kernel + */ +int k_del_rg(origin, g) + u_long origin; + struct gtable *g; +{ + struct mfcctl mc; + int retval, i; + + /* copy table values so that setsockopt can process it */ + mc.mfcc_origin.s_addr = origin; +#ifdef OLD_KERNEL + mc.mfcc_originmask.s_addr = 0xffffffff; +#endif + mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp; + + /* write to kernel space */ + if ((retval = setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_MFC, + (char *)&mc, sizeof(mc))) < 0) + log(LOG_WARNING, errno, "setsockopt MRT_DEL_MFC"); + + return retval; +} + +/* + * Get the kernel's idea of what version of mrouted needs to run with it. + */ +int k_get_version() +{ + int vers; + int len = sizeof(vers); + + if (getsockopt(igmp_socket, IPPROTO_IP, MRT_VERSION, + (char *)&vers, &len) < 0) + log(LOG_ERR, errno, + "getsockopt MRT_VERSION: perhaps your kernel is too old"); + + return vers; +} diff --git a/usr.sbin/mrouted/main.c b/usr.sbin/mrouted/main.c new file mode 100644 index 00000000000..71d9ee36f43 --- /dev/null +++ b/usr.sbin/mrouted/main.c @@ -0,0 +1,611 @@ +/* $NetBSD: main.c,v 1.5 1995/10/09 03:51:44 thorpej Exp $ */ + +/* + * 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. + */ + +/* + * Written by Steve Deering, Stanford University, February 1989. + * + * (An earlier version of DVMRP was implemented by David Waitzman of + * BBN STC by extending Berkeley's routed program. Some of Waitzman's + * extensions have been incorporated into mrouted, but none of the + * original routed code has been adopted.) + */ + + +#include "defs.h" +#include <string.h> +#include <varargs.h> + +#ifdef SNMP +#include "snmp.h" +#endif + +extern char *configfilename; + +static char pidfilename[] = _PATH_MROUTED_PID; +static char dumpfilename[] = _PATH_MROUTED_DUMP; +static char cachefilename[] = _PATH_MROUTED_CACHE; +static char genidfilename[] = _PATH_MROUTED_GENID; + +int cache_lifetime = DEFAULT_CACHE_LIFETIME; +int max_prune_lifetime = DEFAULT_CACHE_LIFETIME * 2; + +int debug = 0; +u_char pruning = 1; /* Enable pruning by default */ + +#define NHANDLERS 2 + +static struct ihandler { + int fd; /* File descriptor */ + void (*func)(); /* Function to call with &fd_set */ +} ihandlers[NHANDLERS]; +static int nhandlers = 0; + +/* + * Forward declarations. + */ +static void fasttimer(); +static void timer(); +static void cleanup(); +static void done(); +static void dump(); +static void fdump(); +static void cdump(); +static void restart(); + +int +register_input_handler(fd, func) + int fd; + void (*func)(); +{ + if (nhandlers >= NHANDLERS) + return -1; + + ihandlers[nhandlers].fd = fd; + ihandlers[nhandlers++].func = func; + + return 0; +} + +int main(argc, argv) + int argc; + char *argv[]; +{ + register int recvlen; + register int omask; + int dummy; + FILE *fp; + extern uid_t geteuid(); + struct timeval tv; + u_long prev_genid; + int vers; + fd_set rfds, readers; + int nfds, n, i; +#ifdef SNMP + char *myname; + fd_set wfds; + + + if (myname = strrchr(argv[0], '/')) + myname++; + if (myname == NULL || *myname == 0) + myname = argv[0]; + isodetailor (myname, 0); +#endif + +#ifdef SYSV + setvbuf(stderr, NULL, _IOLBF, 0); +#else + setlinebuf(stderr); +#endif + + if (geteuid() != 0) { + fprintf(stderr, "must be root\n"); + exit(1); + } + + argv++, argc--; + while (argc > 0 && *argv[0] == '-') { + if (strcmp(*argv, "-d") == 0) { + if (argc > 1 && isdigit(*(argv + 1)[0])) { + argv++, argc--; + debug = atoi(*argv); + } else + debug = DEFAULT_DEBUG; + } else if (strcmp(*argv, "-c") == 0) { + if (argc > 1) { + argv++, argc--; + configfilename = *argv; + } else + goto usage; + } else if (strcmp(*argv, "-p") == 0) { + pruning = 0; + } else + goto usage; + argv++, argc--; + } + + if (argc > 0) { +usage: fprintf(stderr, + "usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n"); + exit(1); + } + + if (debug == 0) { + /* + * Detach from the terminal + */ + int t; + + if (fork()) exit(0); + (void)close(0); + (void)close(1); + (void)close(2); + (void)open("/", 0); + (void)dup2(0, 1); + (void)dup2(0, 2); +#ifdef TIOCNOTTY + t = open("/dev/tty", 2); + if (t >= 0) { + (void)ioctl(t, TIOCNOTTY, (char *)0); + (void)close(t); + } +#else + if (setsid() < 0) + perror("setsid"); +#endif + } + else + fprintf(stderr, "debug level %u\n", debug); + +#ifdef LOG_DAEMON + (void)openlog("mrouted", LOG_PID, LOG_DAEMON); + (void)setlogmask(LOG_UPTO(LOG_NOTICE)); +#else + (void)openlog("mrouted", LOG_PID); +#endif + log(LOG_NOTICE, 0, "mrouted version %d.%d", + PROTOCOL_VERSION, MROUTED_VERSION); + +#ifdef SYSV + srand48(time(NULL)); +#else + srandom(gethostid()); +#endif + + /* + * Get generation id + */ + gettimeofday(&tv, 0); + dvmrp_genid = tv.tv_sec; + + fp = fopen(genidfilename, "r"); + if (fp != NULL) { + fscanf(fp, "%d", &prev_genid); + if (prev_genid == dvmrp_genid) + dvmrp_genid++; + (void) fclose(fp); + } + + fp = fopen(genidfilename, "w"); + if (fp != NULL) { + fprintf(fp, "%d", dvmrp_genid); + (void) fclose(fp); + } + + callout_init(); + +#ifdef SNMP + snmp_init(); +#endif + + init_igmp(); + k_init_dvmrp(); /* enable DVMRP routing in kernel */ + +#ifndef OLD_KERNEL + vers = k_get_version(); + if ((((vers >> 8) & 0xff) != PROTOCOL_VERSION) || + ((vers & 0xff) != MROUTED_VERSION)) + log(LOG_ERR, 0, "kernel (v%d.%d)/mrouted (v%d.%d) version mismatch", + (vers >> 8) & 0xff, vers & 0xff, + PROTOCOL_VERSION, MROUTED_VERSION); +#endif + + init_routes(); + init_ktable(); + init_vifs(); +#ifdef RSRR + rsrr_init(); +#endif /* RSRR */ + +#if defined(__STDC__) || defined(__GNUC__) + /* Allow cleanup if unexpected exit. Apparently some architectures + * have a kernel bug where closing the socket doesn't do an + * ip_mrouter_done(), so we attempt to do it on exit. + */ + atexit(cleanup); +#endif + + if (debug) + fprintf(stderr, "pruning %s\n", pruning ? "on" : "off"); + + fp = fopen(pidfilename, "w"); + if (fp != NULL) { + fprintf(fp, "%d\n", getpid()); + (void) fclose(fp); + } + + if (debug >= 2) dump(); + + (void)signal(SIGALRM, fasttimer); + + (void)signal(SIGHUP, restart); + (void)signal(SIGTERM, done); + (void)signal(SIGINT, done); + (void)signal(SIGUSR1, fdump); + (void)signal(SIGUSR2, cdump); + if (debug != 0) + (void)signal(SIGQUIT, dump); + + FD_ZERO(&readers); + FD_SET(igmp_socket, &readers); + nfds = igmp_socket + 1; + for (i = 0; i < nhandlers; i++) { + FD_SET(ihandlers[i].fd, &readers); + if (ihandlers[i].fd >= nfds) + nfds = ihandlers[i].fd + 1; + } + + (void)alarm(1); /* schedule first timer interrupt */ + + /* + * Main receive loop. + */ + dummy = 0; + for(;;) { + bcopy((char *)&readers, (char *)&rfds, sizeof(rfds)); +#ifdef SNMP + FD_ZERO(&wfds); + + if (smux_fd != NOTOK) { + if (rock_and_roll) + FD_SET(smux_fd, &rfds); + else + FD_SET(smux_fd, &wfds); + if (smux_fd >= nfds) + nfds = smux_fd + 1; + } + + if ((n = xselect(nfds, &rfds, &wfds, NULLFD, NOTOK))==NOTOK) { +#else + if ((n = select(nfds, &rfds, NULL, NULL, NULL)) < 0) { +#endif + if (errno != EINTR) /* SIGALRM is expected */ + log(LOG_WARNING, errno, "select failed"); + continue; + } + + if (FD_ISSET(igmp_socket, &rfds)) { + recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, + 0, NULL, &dummy); + if (recvlen < 0) { + if (errno != EINTR) log(LOG_ERR, errno, "recvfrom"); + continue; + } + omask = sigblock(sigmask(SIGALRM)); + accept_igmp(recvlen); + (void)sigsetmask(omask); + } + + for (i = 0; i < nhandlers; i++) { + if (FD_ISSET(ihandlers[i].fd, &rfds)) { + (*ihandlers[i].func)(&rfds); + } + } + +#ifdef SNMP + if (smux_fd != NOTOK) { + if (rock_and_roll) { + if (FD_ISSET(smux_fd, &rfds)) + doit_smux(); + } else if (FD_ISSET(smux_fd, &wfds)) + start_smux(); + } +#endif + } +} + + +/* + * routine invoked every second. Its main goal is to cycle through + * the routing table and send partial updates to all neighbors at a + * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL + * seconds. Also, every TIMER_INTERVAL seconds it calls timer() to + * do all the other time-based processing. + */ +static void +fasttimer() +{ + static unsigned int tlast; + static unsigned int nsent; + register unsigned int t = tlast + 1; + register int n; + + /* + * if we're in the last second, send everything that's left. + * otherwise send at least the fraction we should have sent by now. + */ + if (t >= ROUTE_REPORT_INTERVAL) { + register int nleft = nroutes - nsent; + while (nleft > 0) { + if ((n = report_next_chunk()) <= 0) + break; + nleft -= n; + } + tlast = 0; + nsent = 0; + } else { + register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL; + while (nsent < ncum) { + if ((n = report_next_chunk()) <= 0) + break; + nsent += n; + } + tlast = t; + } + if ((t % TIMER_INTERVAL) == 0) + timer(); + + age_callout_queue();/* Advance the timer for the callout queue + for groups */ + alarm(1); +} + +/* + * The 'virtual_time' variable is initialized to a value that will cause the + * first invocation of timer() to send a probe or route report to all vifs + * and send group membership queries to all subnets for which this router is + * querier. This first invocation occurs approximately TIMER_INTERVAL seconds + * after the router starts up. Note that probes for neighbors and queries + * for group memberships are also sent at start-up time, as part of initial- + * ization. This repetition after a short interval is desirable for quickly + * building up topology and membership information in the presence of possible + * packet loss. + * + * 'virtual_time' advances at a rate that is only a crude approximation of + * real time, because it does not take into account any time spent processing, + * and because the timer intervals are sometimes shrunk by a random amount to + * avoid unwanted synchronization with other routers. + */ + +static u_long virtual_time = 0; + + +/* + * Timer routine. Performs periodic neighbor probing, route reporting, and + * group querying duties, and drives various timers in routing entries and + * virtual interface data structures. + */ +static void +timer() +{ + age_routes(); /* Advance the timers in the route entries */ + age_vifs(); /* Advance the timers for neighbors */ + age_table_entry(); /* Advance the timers for the cache entries */ + + if (virtual_time % GROUP_QUERY_INTERVAL == 0) { + /* + * Time to query the local group memberships on all subnets + * for which this router is the elected querier. + */ + query_groups(); + } + + if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) { + /* + * Time to send a probe on all vifs from which no neighbors have + * been heard. Also, check if any inoperative interfaces have now + * come up. (If they have, they will also be probed as part of + * their initialization.) + */ + probe_for_neighbors(); + + if (vifs_down) + check_vif_state(); + } + + delay_change_reports = FALSE; + if (routes_changed) { + /* + * Some routes have changed since the last timer interrupt, but + * have not been reported yet. Report the changed routes to all + * neighbors. + */ + report_to_all_neighbors(CHANGED_ROUTES); + } + +#ifdef SNMP + if (smux_fd == NOTOK && !dont_bother_anymore + && virtual_time % SNMPD_RETRY_INTERVAL == 0) { + /* + * Time to check for snmpd running. + */ + try_smux_init(); + } +#endif + + /* + * Advance virtual time + */ + virtual_time += TIMER_INTERVAL; +} + + +/* + * On termination, let everyone know we're going away. + */ +static void +done() +{ + log(LOG_NOTICE, 0, "mrouted version %d.%d exiting", + PROTOCOL_VERSION, MROUTED_VERSION); + cleanup(); + _exit(1); +} + +static void +cleanup() +{ + static in_cleanup = 0; + + if (!in_cleanup) { + in_cleanup++; +#ifdef RSRR + rsrr_clean(); +#endif /* RSRR */ + expire_all_routes(); + report_to_all_neighbors(ALL_ROUTES); + k_stop_dvmrp(); + } +} + + +/* + * Dump internal data structures to stderr. + */ +static void +dump() +{ + dump_vifs(stderr); + dump_routes(stderr); +} + + +/* + * Dump internal data structures to a file. + */ +static void +fdump() +{ + FILE *fp; + + fp = fopen(dumpfilename, "w"); + if (fp != NULL) { + dump_vifs(fp); + dump_routes(fp); + (void) fclose(fp); + } +} + + +/* + * Dump local cache contents to a file. + */ +static void +cdump() +{ + FILE *fp; + + fp = fopen(cachefilename, "w"); + if (fp != NULL) { + dump_cache(fp); + (void) fclose(fp); + } +} + + +/* + * Restart mrouted + */ +static void +restart() +{ + register int omask; + + log(LOG_NOTICE, 0, "mrouted version %d.%d restart", + PROTOCOL_VERSION, MROUTED_VERSION); + + /* + * reset all the entries + */ + omask = sigblock(sigmask(SIGALRM)); + free_all_prunes(); + free_all_routes(); + stop_all_vifs(); + k_stop_dvmrp(); + close(igmp_socket); + close(udp_socket); + + /* + * start processing again + */ + dvmrp_genid++; + pruning = 1; + + init_igmp(); + k_init_dvmrp(); /* enable DVMRP routing in kernel */ + init_routes(); + init_ktable(); + init_vifs(); + + (void)sigsetmask(omask); +} + + +/* + * Log errors and other messages to the system log daemon and 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. + */ +/*VARARGS3*/ +void +log(severity, syserr, format, va_alist) + int severity, syserr; + char *format; + va_dcl +{ + va_list ap; + static char fmt[211] = "warning - "; + char *msg; + char tbuf[20]; + struct timeval now; + struct tm *thyme; + + va_start(ap); + vsprintf(&fmt[10], format, ap); + va_end(ap); + msg = (severity == LOG_WARNING) ? fmt : &fmt[10]; + + switch (debug) { + case 0: break; + case 1: if (severity > LOG_NOTICE) break; + case 2: if (severity > LOG_INFO ) break; + default: + gettimeofday(&now,NULL); + thyme = localtime((time_t *)&now.tv_sec); + strftime(tbuf, sizeof(tbuf), "%X.%%03d ", thyme); + fprintf(stderr, tbuf, now.tv_usec / 1000); + fprintf(stderr, "%s", msg); + if (syserr == 0) + fprintf(stderr, "\n"); + else + fprintf(stderr, ": %s\n", strerror(syserr)); + } + + if (severity <= LOG_NOTICE) { + if (syserr != 0) { + errno = syserr; + syslog(severity, "%s: %m", msg); + } else + syslog(severity, "%s", msg); + + if (severity <= LOG_ERR) exit(-1); + } +} diff --git a/usr.sbin/mrouted/mrouted.8 b/usr.sbin/mrouted/mrouted.8 new file mode 100644 index 00000000000..dd7c74f65c1 --- /dev/null +++ b/usr.sbin/mrouted/mrouted.8 @@ -0,0 +1,399 @@ +'\" $NetBSD: mrouted.8,v 1.5 1995/10/09 03:51:46 thorpej Exp $ +'\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University. +.TH MROUTED 8 +.UC 5 +.SH NAME +mrouted \- IP multicast routing daemon +.SH SYNOPSIS +.B /etc/mrouted +[ +.B \-p +] [ +.B \-c +.I config_file +] [ +.B \-d +[ +.I debug_level +]] +.SH DESCRIPTION +.I Mrouted +is an implementation of the Distance-Vector Multicast Routing +Protocol (DVMRP), an earlier version of which is specified in RFC-1075. +It maintains topological knowledge via a distance-vector routing protocol +(like RIP, described in RFC-1058), upon which it implements a multicast +datagram forwarding algorithm called Reverse Path Multicasting. +.PP +.I Mrouted +forwards a multicast datagram along a shortest (reverse) path tree +rooted at the subnet on which the datagram originates. The multicast +delivery tree may be thought of as a broadcast delivery tree that has +been pruned back so that it does not extend beyond those subnetworks +that have members of the destination group. Hence, datagrams +are not forwarded along those branches which have no listeners of the +multicast group. The IP time-to-live of a multicast datagram can be +used to limit the range of multicast datagrams. +.PP +In order to support multicasting among subnets that are separated by (unicast) +routers that do not support IP multicasting, +.I mrouted +includes support for +"tunnels", which are virtual point-to-point links between pairs of +.IR mrouted s +located anywhere in an internet. IP multicast packets are encapsulated for +transmission through tunnels, so that they look like normal unicast datagrams +to intervening routers and subnets. The encapsulation +is added on entry to a tunnel, and stripped off +on exit from a tunnel. +By default, the packets are encapsulated using the IP-in-IP protocol +(IP protocol number 4). +Older versions of +.I mrouted +tunnel using IP source routing, which puts a heavy load on some +types of routers. +This version does not support IP source route tunnelling. +.PP +The tunnelling mechanism allows +.I mrouted +to establish a virtual internet, for +the purpose of multicasting only, which is independent of the physical +internet, and which may span multiple Autonomous Systems. This capability +is intended for experimental support of internet multicasting only, pending +widespread support for multicast routing by the regular (unicast) routers. +.I Mrouted +suffers from the well-known scaling problems of any distance-vector +routing protocol, and does not (yet) support hierarchical multicast routing. +.PP +.I Mrouted +handles multicast routing only; there may or may not be unicast routing +software running on the same machine as +.IR mrouted . +With the use of tunnels, it +is not necessary for +.I mrouted +to have access to more than one physical subnet +in order to perform multicast forwarding. +.br +.ne 5 +.SH INVOCATION +.PP +If no "\-d" option is given, or if the debug level is specified as 0, +.I mrouted +detaches from the invoking terminal. Otherwise, it remains attached to the +invoking terminal and responsive to signals from that terminal. If "\-d" is +given with no argument, the debug level defaults to 2. Regardless of the +debug level, +.I mrouted +always writes warning and error messages to the system +log demon. Non-zero debug levels have the following effects: +.IP "level 1" +all syslog'ed messages are also printed to stderr. +.IP "level 2" +all level 1 messages plus notifications of "significant" +events are printed to stderr. +.IP "level 3" +all level 2 messages plus notifications of all packet +arrivals and departures are printed to stderr. +.PP +Upon startup, mrouted writes its pid to the file /etc/mrouted.pid . +.SH CONFIGURATION +.PP +.I Mrouted +automatically configures itself to forward on all multicast-capable +interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding +the loopback "interface"), and it finds other +.IR mrouted s +directly reachable +via those interfaces. To override the default configuration, or to add +tunnel links to other +.IR mrouted s, +configuration commands may be placed in +/etc/mrouted.conf (or an alternative file, specified by the "\-c" option). +There are four types of configuration commands: +.nf + + phyint <local-addr> [disable] [metric <m>] + [threshold <t>] [rate_limit <b>] + [boundary (<boundary-name>|<scoped-addr>/<mask-len>)] + [altnet <network>/<mask-len>] + + tunnel <local-addr> <remote-addr> [metric <m>] + [threshold <t>] [srcrt] [rate_limit <b>] + [boundary (<boundary-name>|<scoped-addr>/<mask-len>)] + + cache_lifetime <ct> + + pruning <off/on> + + name <boundary-name> <scoped-addr>/<mask-len> + +.fi +.PP +The file format is free-form; whitespace (including newlines) is not +significant. +The +.I boundary +and +.I altnet +options may be specified as many times as necessary. +.PP +The phyint command can be used to disable multicast routing on the physical +interface identified by local IP address <local-addr>, or to associate a +non-default metric or threshold with the specified physical interface. +The local IP address <local-addr> may be alternatively replaced by the +interface name (e.g le0). +If a phyint is attached to multiple IP subnets, describe each additional subnet +with the altnet keyword. +Phyint commands must precede tunnel commands. +.PP +The tunnel command can be used to establish a tunnel link between local +IP address <local-addr> and remote IP address <remote-addr>, and to associate +a non-default metric or threshold with that tunnel. The tunnel must be set +up in the mrouted.conf files of both routers before it can be used. +'\"For backwards compatibility with older +'\".IR mrouted s, +'\"the srcrt keyword specifies +'\"encapsulation using IP source routing. +.PP +The cache_lifetime is a value that determines the amount of time that a +cached multicast route stays in kernel before timing out. The value of this +entry should lie between 300 (5 min) and 86400 (1 day). It defaults to 300. +.PP +The pruning <off/on> option is provided for +.IR mrouted +to act as a non-pruning router. It is also possible to start +.IR mrouted +in a non-pruning mode using the "-p" option on the command line. It is +expected that a router would be configured in this manner for test +purposes only. The default mode is pruning enabled. +.PP +You may assign names to boundaries to make configuration easier with +the name keyword. The boundary option on phyint or tunnel commands +can accept either a name or a boundary. +.PP +The metric is the "cost" associated with sending a datagram on the given +interface or tunnel; it may be used to influence the choice of routes. +The metric defaults to 1. Metrics should be kept as small as possible, +because +.I mrouted +cannot route along paths with a sum of metrics greater +than 31. +.LP +The threshold is the minimum IP time-to-live required for a multicast datagram +to be forwarded to the given interface or tunnel. It is used to control the +scope of multicast datagrams. (The TTL of forwarded packets is only compared +to the threshold, it is not decremented by the threshold. Every multicast +router decrements the TTL by 1.) The default threshold is 1. +.LP +In general, all +.IR mrouted s +connected to a particular subnet or tunnel should +use the same metric and threshold for that subnet or tunnel. +.PP +The rate_limit option allows the network administrator to specify a +certain bandwidth in Kbits/second which would be allocated to multicast +traffic. It defaults to 500Kbps on tunnels, and 0 (unlimited) on physical +interfaces. +.PP +The boundary option allows an interface +to be configured as an administrative boundary for the specified +scoped address. Packets belonging to this address will not +be forwarded on a scoped interface. The boundary option accepts either +a name or a boundary spec. +.PP +.I Mrouted +will not initiate execution if it has fewer than two enabled vifs, +where a vif (virtual interface) is either a physical multicast-capable +interface or a tunnel. It will log a warning if all of its vifs are +tunnels; such an +.I mrouted +configuration would be better replaced by more +direct tunnels (i.e., eliminate the middle man). +.SH "EXAMPLE CONFIGURATION" +.PP +This is an example configuration for a mythical multicast router at a big +school. +.sp +.nf +# +# mrouted.conf example +# +# Name our boundaries to make it easier +name LOCAL 239.255.0.0/16 +name EE 239.254.0.0/16 +# +# le1 is our gateway to compsci, don't forward our +# local groups to them +phyint le1 boundary EE +# +# le2 is our interface on the classroom net, it has four +# different length subnets on it. +# note that you can use either an ip address or an +# interface name +phyint 172.16.12.38 boundary EE altnet 172.16.15.0/26 + altnet 172.16.15.128/26 altnet 172.16.48.0/24 +# +# atm0 is our ATM interface, which doesn't properly +# support multicasting. +phyint atm0 disable +# +# This is an internal tunnel to another EE subnet +# Remove the default tunnel rate limit, since this +# tunnel is over ethernets +tunnel 192.168.5.4 192.168.55.101 metric 1 threshold 1 + rate_limit 0 +# +# This is our tunnel to the outside world. +# Careful with those boundaries, Eugene. +tunnel 192.168.5.4 10.11.12.13 metric 1 threshold 32 + boundary LOCAL boundary EE +.fi +.SH SIGNALS +.PP +.I Mrouted +responds to the following signals: +.IP HUP +restarts +.I mrouted . +The configuration file is reread every time this signal is evoked. +.IP INT +terminates execution gracefully (i.e., by sending +good-bye messages to all neighboring routers). +.IP TERM +same as INT +.IP USR1 +dumps the internal routing tables to /usr/tmp/mrouted.dump. +.IP USR2 +dumps the internal cache tables to /usr/tmp/mrouted.cache. +.IP QUIT +dumps the internal routing tables to stderr (only if +.I mrouted +was invoked with a non-zero debug level). +.PP +For convenience in sending signals, +.I mrouted +writes its pid to /etc/mrouted.pid upon startup. +.bp +.SH EXAMPLE +.PP +The routing tables look like this: +.nf + +Virtual Interface Table + Vif Local-Address Metric Thresh Flags + 0 36.2.0.8 subnet: 36.2 1 1 querier + groups: 224.0.2.1 + 224.0.0.4 + pkts in: 3456 + pkts out: 2322323 + + 1 36.11.0.1 subnet: 36.11 1 1 querier + groups: 224.0.2.1 + 224.0.1.0 + 224.0.0.4 + pkts in: 345 + pkts out: 3456 + + 2 36.2.0.8 tunnel: 36.8.0.77 3 1 + peers: 36.8.0.77 (2.2) + boundaries: 239.0.1 + : 239.1.2 + pkts in: 34545433 + pkts out: 234342 + + 3 36.2.0.8 tunnel: 36.6.8.23 3 16 + +Multicast Routing Table (1136 entries) + Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs + 36.2 1 45 0 1* 2 3* + 36.8 36.8.0.77 4 15 2 0* 1* 3* + 36.11 1 20 1 0* 2 3* + . + . + . + +.fi +In this example, there are four vifs connecting to two subnets and two +tunnels. The vif 3 tunnel is not in use (no peer address). The vif 0 and +vif 1 subnets have some groups present; tunnels never have any groups. This +instance of +.I mrouted +is the one responsible for sending periodic group +membership queries on the vif 0 and vif 1 subnets, as indicated by the +"querier" flags. The list of boundaries indicate the scoped addresses on that +interface. A count of the no. of incoming and outgoing packets is also +shown at each interface. +.PP +Associated with each subnet from which a multicast datagram can originate +is the address of the previous hop router (unless the subnet is directly- +connected), the metric of the path back to the origin, the amount of time +since we last recieved an update for this subnet, the incoming vif for +multicasts from that origin, and a list of outgoing vifs. "*" means that +the outgoing vif is connected to a leaf of the broadcast tree rooted at the +origin, and a multicast datagram from that origin will be forwarded on that +outgoing vif only if there are members of the destination group on that leaf. +.bp +.PP +.I Mrouted +also maintains a copy of the kernel forwarding cache table. Entries +are created and deleted by +.I mrouted. +.PP +The cache tables look like this: +.nf + +Multicast Routing Cache Table (147 entries) + Origin Mcast-group CTmr Age Ptmr IVif Forwvifs + 13.2.116/22 224.2.127.255 3m 2m - 0 1 +>13.2.116.19 +>13.2.116.196 + 138.96.48/21 224.2.127.255 5m 2m - 0 1 +>138.96.48.108 + 128.9.160/20 224.2.127.255 3m 2m - 0 1 +>128.9.160.45 + 198.106.194/24 224.2.135.190 9m 28s 9m 0P +>198.106.194.22 + +.fi +Each entry is characterized by the origin subnet number and mask and the +destination multicast group. The 'CTmr' field indicates the lifetime +of the entry. The entry is deleted from the cache table +when the timer decrements to zero. The 'Age' field is the time since +this cache entry was originally created. Since cache entries get refreshed +if traffic is flowing, routing entries can grow very old. +The 'Ptmr' field is simply a dash if no prune was sent upstream, or the +amount of time until the upstream prune will time out. +The 'Ivif' field indicates the +incoming vif for multicast packets from that origin. Each router also +maintains a record of the number of prunes received from neighbouring +routers for a particular source and group. If there are no members of +a multicast group on any downward link of the multicast tree for a +subnet, a prune message is sent to the upstream router. They are +indicated by a "P" after the vif number. The Forwvifs field shows the +interfaces along which datagrams belonging to the source-group are +forwarded. A "p" indicates that no datagrams are being forwarded along +that interface. An unlisted interface is a leaf subnet with are no +members of the particular group on that subnet. A "b" on an interface +indicates that it is a boundary interface, i.e. traffic will not be +forwarded on the scoped address on that interface. +An additional line with a ">" as the first character is printed for +each source on the subnet. Note that there can be many sources in +one subnet. +.SH FILES +/etc/mrouted.conf +.br +/etc/mrouted.pid +.br +/usr/tmp/mrouted.dump +.br +/usr/tmp/mrouted.cache +.SH SEE ALSO +.BR mrinfo (8) , +.BR mtrace (8) , +.BR map-mbone (8) +.sp +DVMRP is described, along with other multicast routing algorithms, in the +paper "Multicast Routing in Internetworks and Extended LANs" by S. Deering, +in the Proceedings of the ACM SIGCOMM '88 Conference. +.SH AUTHORS +Steve Deering, Ajit Thyagarajan, Bill Fenner diff --git a/usr.sbin/mrouted/pathnames.h b/usr.sbin/mrouted/pathnames.h new file mode 100644 index 00000000000..c7939fde1c1 --- /dev/null +++ b/usr.sbin/mrouted/pathnames.h @@ -0,0 +1,17 @@ +/* $NetBSD: pathnames.h,v 1.3 1995/10/09 03:51:48 thorpej Exp $ */ + +/* + * 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. + */ + +#define _PATH_MROUTED_CONF "/etc/mrouted.conf" + +#define _PATH_MROUTED_PID "/var/run/mrouted.pid" +#define _PATH_MROUTED_GENID "/var/run/mrouted.genid" +#define _PATH_MROUTED_DUMP "/var/tmp/mrouted.dump" +#define _PATH_MROUTED_CACHE "/var/tmp/mrouted.cache" diff --git a/usr.sbin/mrouted/prune.c b/usr.sbin/mrouted/prune.c new file mode 100644 index 00000000000..aaba6c318ee --- /dev/null +++ b/usr.sbin/mrouted/prune.c @@ -0,0 +1,2259 @@ +/* $NetBSD: prune.c,v 1.2 1995/10/09 03:51:49 thorpej Exp $ */ + +/* + * 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. + */ + + +#include "defs.h" + +extern int cache_lifetime; +extern int max_prune_lifetime; +extern struct rtentry *routing_table; + +/* + * dither cache lifetime to obtain a value between x and 2*x + */ +#ifdef SYSV +#define CACHE_LIFETIME(x) ((x) + (lrand48() % (x))) +#else +#define CACHE_LIFETIME(x) ((x) + (random() % (x))) +#endif + +#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; \ + } \ + } + +struct gtable *kernel_table; /* ptr to list of kernel grp entries*/ +static struct gtable *kernel_no_route; /* list of grp entries w/o routes */ +struct gtable *gtp; /* pointer for kernel rt entries */ +unsigned int kroutes; /* current number of cache entries */ + +/**************************************************************************** + Functions that are local to prune.c +****************************************************************************/ + +/* + * Updates the ttl values for each vif. + */ +static void +prun_add_ttls(gt) + struct gtable *gt; +{ + struct uvif *v; + vifi_t vifi; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (VIFM_ISSET(vifi, gt->gt_grpmems)) + gt->gt_ttls[vifi] = v->uv_threshold; + else + gt->gt_ttls[vifi] = 0; + } +} + +/* + * checks for scoped multicast addresses + */ +#define GET_SCOPE(gt) { \ + register int _i; \ + if ((ntohl((gt)->gt_mcastgrp) & 0xff000000) == 0xef000000) \ + for (_i = 0; _i < numvifs; _i++) \ + if (scoped_addr(_i, (gt)->gt_mcastgrp)) \ + VIFM_SET(_i, (gt)->gt_scope); \ + } + +int +scoped_addr(vifi, addr) + vifi_t vifi; + u_int32_t 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; +} + +/* + * Determine if mcastgrp has a listener on vifi + */ +int +grplst_mem(vifi, mcastgrp) + vifi_t vifi; + u_int32_t 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; +} + +/* + * Finds the group entry with the specified source and netmask. + * If netmask is 0, it uses the route's netmask. + * + * Returns TRUE if found a match, and the global variable gtp is left + * pointing to entry before the found entry. + * Returns FALSE if no exact match found, gtp is left pointing to before + * the entry in question belongs, or is NULL if the it belongs at the + * head of the list. + */ +int +find_src_grp(src, mask, grp) + u_int32_t src; + u_int32_t mask; + u_int32_t grp; +{ + struct gtable *gt; + + gtp = NULL; + gt = kernel_table; + while (gt != NULL) { + if (grp == gt->gt_mcastgrp && + (mask ? (gt->gt_route->rt_origin == src && + gt->gt_route->rt_originmask == mask) : + ((src & gt->gt_route->rt_originmask) == + gt->gt_route->rt_origin))) + return TRUE; + if (ntohl(grp) > ntohl(gt->gt_mcastgrp) || + (grp == gt->gt_mcastgrp && + (ntohl(mask) < ntohl(gt->gt_route->rt_originmask) || + (mask == gt->gt_route->rt_originmask && + (ntohl(src) > ntohl(gt->gt_route->rt_origin)))))) { + gtp = gt; + gt = gt->gt_gnext; + } + else break; + } + return FALSE; +} + +/* + * Check if the neighbor supports pruning + */ +static int +pruning_neighbor(vifi, addr) + vifi_t vifi; + u_int32_t addr; +{ + struct listaddr *n = neighbor_info(vifi, addr); + int vers; + + if (n == NULL) + return 0; + + if (n->al_flags & NF_PRUNE) + return 1; + + /* + * Versions from 3.0 to 3.4 relied on the version number to identify + * that they could handle pruning. + */ + vers = NBR_VERS(n); + return (vers >= 0x0300 && vers <= 0x0304); +} + +/* + * Can the neighbor in question handle multicast traceroute? + */ +static int +can_mtrace(vifi, addr) + vifi_t vifi; + u_int32_t addr; +{ + struct listaddr *n = neighbor_info(vifi, addr); + int vers; + + if (n == NULL) + return 0; + + if (n->al_flags & NF_MTRACE) + return 1; + + /* + * Versions 3.3 and 3.4 relied on the version number to identify + * that they could handle traceroute. + */ + vers = NBR_VERS(n); + return (vers >= 0x0303 && vers <= 0x0304); +} + +/* + * Returns the prune entry of the router, or NULL if none exists + */ +static struct ptable * +find_prune_entry(vr, pt) + u_int32_t vr; + struct ptable *pt; +{ + while (pt) { + if (pt->pt_router == vr) + return pt; + pt = pt->pt_next; + } + + return NULL; +} + +/* + * Send a prune message to the dominant router for + * this source. + * + * Record an entry that a prune was sent for this group + */ +static void +send_prune(gt) + struct gtable *gt; +{ + struct ptable *pt; + char *p; + int i; + int datalen; + u_int32_t src; + u_int32_t dst; + u_int32_t tmp; + + /* Don't process any prunes if router is not pruning */ + if (pruning == 0) + return; + + /* Can't process a prune if we don't have an associated route */ + if (gt->gt_route == NULL) + return; + + /* Don't send a prune to a non-pruning router */ + if (!pruning_neighbor(gt->gt_route->rt_parent, gt->gt_route->rt_gateway)) + return; + + /* + * sends a prune message to the router upstream. + */ + src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr; + dst = gt->gt_route->rt_gateway; + + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + + /* + * determine prune lifetime + */ + gt->gt_prsent_timer = gt->gt_timer; + for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) + if (pt->pt_timer < gt->gt_prsent_timer) + gt->gt_prsent_timer = pt->pt_timer; + + /* + * If we have a graft pending, cancel graft retransmission + */ + gt->gt_grftsnt = 0; + + for (i = 0; i < 4; i++) + *p++ = ((char *)&(gt->gt_route->rt_origin))[i]; + for (i = 0; i < 4; i++) + *p++ = ((char *)&(gt->gt_mcastgrp))[i]; + tmp = htonl(gt->gt_prsent_timer); + for (i = 0; i < 4; i++) + *p++ = ((char *)&(tmp))[i]; + datalen += 12; + + send_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE, + htonl(MROUTED_LEVEL), datalen); + + log(LOG_DEBUG, 0, "sent prune for (%s %s)/%d on vif %d to %s", + inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2), + gt->gt_prsent_timer, gt->gt_route->rt_parent, + inet_fmt(gt->gt_route->rt_gateway, 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. + */ +static void +send_graft(gt) + struct gtable *gt; +{ + register char *p; + register int i; + int datalen; + u_int32_t src; + u_int32_t dst; + + /* Can't send a graft without an associated route */ + if (gt->gt_route == NULL) + return; + + src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr; + dst = gt->gt_route->rt_gateway; + + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + + for (i = 0; i < 4; i++) + *p++ = ((char *)&(gt->gt_route->rt_origin))[i]; + for (i = 0; i < 4; i++) + *p++ = ((char *)&(gt->gt_mcastgrp))[i]; + datalen += 8; + + if (datalen != 0) { + send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT, + htonl(MROUTED_LEVEL), datalen); + } + log(LOG_DEBUG, 0, "sent graft for (%s %s) to %s on vif %d", + inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2), + inet_fmt(gt->gt_route->rt_gateway, s3), gt->gt_route->rt_parent); +} + +/* + * Send an ack that a graft was received + */ +static void +send_graft_ack(src, dst, origin, grp) + u_int32_t src; + u_int32_t dst; + u_int32_t origin; + u_int32_t grp; +{ + register char *p; + register int i; + int datalen; + + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + + for (i = 0; i < 4; i++) + *p++ = ((char *)&(origin))[i]; + for (i = 0; i < 4; i++) + *p++ = ((char *)&(grp))[i]; + datalen += 8; + + send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK, + htonl(MROUTED_LEVEL), datalen); + + log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s", + inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3)); +} + +/* + * Update the kernel cache with all the routes hanging off the group entry + */ +static void +update_kernel(g) + struct gtable *g; +{ + struct stable *st; + + for (st = g->gt_srctbl; st; st = st->st_next) + k_add_rg(st->st_origin, g); +} + +/**************************************************************************** + Functions that are used externally +****************************************************************************/ + +#ifdef SNMP +#include <sys/types.h> +#include "snmp.h" + +/* + * Find a specific group entry in the group table + */ +struct gtable * +find_grp(grp) + u_long grp; +{ + struct gtable *gt; + + for (gt = kernel_table; gt; gt = gt->gt_gnext) { + if (ntohl(grp) < ntohl(gt->gt_mcastgrp)) + break; + if (gt->gt_mcastgrp == grp) + return gt; + } + return NULL; +} + +/* + * Given a group entry and source, find the corresponding source table + * entry + */ +struct stable * +find_grp_src(gt, src) + struct gtable *gt; + u_long src; +{ + struct stable *st; + u_long grp = gt->gt_mcastgrp; + struct gtable *gtcurr; + + for (gtcurr = gt; gtcurr->gt_mcastgrp == grp; gtcurr = gtcurr->gt_gnext) { + for (st = gtcurr->gt_srctbl; st; st = st->st_next) + if (st->st_origin == src) + return st; + } + return NULL; +} + +/* + * Find next entry > specification + */ +int +next_grp_src_mask(gtpp, stpp, grp, src, mask) + struct gtable **gtpp; /* ordered by group */ + struct stable **stpp; /* ordered by source */ + u_long grp; + u_long src; + u_long mask; +{ + struct gtable *gt, *gbest = NULL; + struct stable *st, *sbest = NULL; + + /* Find first group entry >= grp spec */ + (*gtpp) = kernel_table; + while ((*gtpp) && ntohl((*gtpp)->gt_mcastgrp) < ntohl(grp)) + (*gtpp)=(*gtpp)->gt_gnext; + if (!(*gtpp)) + return 0; /* no more groups */ + + for (gt = kernel_table; gt; gt=gt->gt_gnext) { + /* Since grps are ordered, we can stop when group changes from gbest */ + if (gbest && gbest->gt_mcastgrp != gt->gt_mcastgrp) + break; + for (st = gt->gt_srctbl; st; st=st->st_next) { + + /* Among those entries > spec, find "lowest" one */ + if (((ntohl(gt->gt_mcastgrp)> ntohl(grp)) + || (ntohl(gt->gt_mcastgrp)==ntohl(grp) + && ntohl(st->st_origin)> ntohl(src)) + || (ntohl(gt->gt_mcastgrp)==ntohl(grp) + && ntohl(st->st_origin)==src && 0xFFFFFFFF>ntohl(mask))) + && (!gbest + || (ntohl(gt->gt_mcastgrp)< ntohl(gbest->gt_mcastgrp)) + || (ntohl(gt->gt_mcastgrp)==ntohl(gbest->gt_mcastgrp) + && ntohl(st->st_origin)< ntohl(sbest->st_origin)))) { + gbest = gt; + sbest = st; + } + } + } + (*gtpp) = gbest; + (*stpp) = sbest; + return (*gtpp)!=0; +} + +/* + * Ensure that sg contains current information for the given group,source. + * This is fetched from the kernel as a unit so that counts for the entry + * are consistent, i.e. packet and byte counts for the same entry are + * read at the same time. + */ +void +refresh_sg(sg, gt, st) + struct sioc_sg_req *sg; + struct gtable *gt; + struct stable *st; +{ + static int lastq = -1; + + if (quantum != lastq || sg->src.s_addr!=st->st_origin + || sg->grp.s_addr!=gt->gt_mcastgrp) { + lastq = quantum; + sg->src.s_addr = st->st_origin; + sg->grp.s_addr = gt->gt_mcastgrp; + ioctl(udp_socket, SIOCGETSGCNT, (char *)sg); + } +} + +/* + * Return pointer to a specific route entry. This must be a separate + * function from find_route() which modifies rtp. + */ +struct rtentry * +snmp_find_route(src, mask) + register u_long src, mask; +{ + register struct rtentry *rt; + + for (rt = routing_table; rt; rt = rt->rt_next) { + if (src == rt->rt_origin && mask == rt->rt_originmask) + return rt; + } + return NULL; +} + +/* + * Find next route entry > specification + */ +int +next_route(rtpp, src, mask) + struct rtentry **rtpp; + u_long src; + u_long mask; +{ + struct rtentry *rt, *rbest = NULL; + + /* Among all entries > spec, find "lowest" one in order */ + for (rt = routing_table; rt; rt=rt->rt_next) { + if ((ntohl(rt->rt_origin) > ntohl(src) + || (ntohl(rt->rt_origin) == ntohl(src) + && ntohl(rt->rt_originmask) > ntohl(mask))) + && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin)) + || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin) + && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask)))) + rbest = rt; + } + (*rtpp) = rbest; + return (*rtpp)!=0; +} + +/* + * Given a routing table entry, and a vifi, find the next vifi/entry + */ +int +next_route_child(rtpp, src, mask, vifi) + struct rtentry **rtpp; + u_long src; + u_long mask; + vifi_t *vifi; /* vif at which to start looking */ +{ + struct rtentry *rt; + + /* Get (S,M) entry */ + if (!((*rtpp) = snmp_find_route(src,mask))) + if (!next_route(rtpp, src, mask)) + return 0; + + /* Continue until we get one with a valid next vif */ + do { + for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++) + if (VIFM_ISSET(*vifi, (*rtpp)->rt_children)) + return 1; + *vifi = 0; + } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) ); + + return 0; +} + +/* + * Given a routing table entry, and a vifi, find the next entry + * equal to or greater than those + */ +int +next_child(gtpp, stpp, grp, src, mask, vifi) + struct gtable **gtpp; + struct stable **stpp; + u_long grp; + u_long src; + u_long mask; + vifi_t *vifi; /* vif at which to start looking */ +{ + struct stable *st; + + /* Get (G,S,M) entry */ + if (mask!=0xFFFFFFFF + || !((*gtpp) = find_grp(grp)) + || !((*stpp) = find_grp_src((*gtpp),src))) + if (!next_grp_src_mask(gtpp, stpp, grp, src, mask)) + return 0; + + /* Continue until we get one with a valid next vif */ + do { + for (; (*gtpp)->gt_route->rt_children && *vifi<numvifs; (*vifi)++) + if (VIFM_ISSET(*vifi, (*gtpp)->gt_route->rt_children)) + return 1; + *vifi = 0; + } while (next_grp_src_mask(gtpp, stpp, (*gtpp)->gt_mcastgrp, + (*stpp)->st_origin, 0xFFFFFFFF) ); + + return 0; +} +#endif /* SNMP */ + +/* + * Initialize the kernel table structure + */ +void +init_ktable() +{ + kernel_table = NULL; + kernel_no_route = NULL; + kroutes = 0; +} + +/* + * Add a new table entry for (origin, mcastgrp) + */ +void +add_table_entry(origin, mcastgrp) + u_int32_t origin; + u_int32_t mcastgrp; +{ + struct rtentry *r; + struct gtable *gt,**gtnp,*prev_gt; + struct stable *st,**stnp; + int i; + + r = determine_route(origin); + prev_gt = NULL; + if (r == NULL) { + /* + * Look for it on the no_route table; if it is found then + * it will be detected as a duplicate below. + */ + for (gt = kernel_no_route; gt; gt = gt->gt_next) + if (mcastgrp == gt->gt_mcastgrp && + gt->gt_srctbl && gt->gt_srctbl->st_origin == origin) + break; + gtnp = &kernel_no_route; + } else { + gtnp = &r->rt_groups; + while ((gt = *gtnp) != NULL) { + if (gt->gt_mcastgrp >= mcastgrp) + break; + gtnp = >->gt_next; + prev_gt = gt; + } + } + + if (gt == NULL || gt->gt_mcastgrp != mcastgrp) { + gt = (struct gtable *)malloc(sizeof(struct gtable)); + if (gt == NULL) + log(LOG_ERR, 0, "ran out of memory"); + + gt->gt_mcastgrp = mcastgrp; + gt->gt_timer = CACHE_LIFETIME(cache_lifetime); + time(>->gt_ctime); + gt->gt_grpmems = 0; + gt->gt_scope = 0; + gt->gt_prsent_timer = 0; + gt->gt_grftsnt = 0; + gt->gt_srctbl = NULL; + gt->gt_pruntbl = NULL; + gt->gt_route = r; + + if (r != NULL) { + /* obtain the multicast group membership list */ + for (i = 0; i < numvifs; i++) { + if (VIFM_ISSET(i, r->rt_children) && + !(VIFM_ISSET(i, r->rt_leaves))) + VIFM_SET(i, gt->gt_grpmems); + + if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp)) + VIFM_SET(i, gt->gt_grpmems); + } + GET_SCOPE(gt); + if (VIFM_ISSET(r->rt_parent, gt->gt_scope)) + gt->gt_scope = -1; + gt->gt_grpmems &= ~gt->gt_scope; + } else { + gt->gt_scope = -1; + gt->gt_grpmems = 0; + } + + /* update ttls */ + prun_add_ttls(gt); + + gt->gt_next = *gtnp; + *gtnp = gt; + if (gt->gt_next) + gt->gt_next->gt_prev = gt; + gt->gt_prev = prev_gt; + + if (r) { + if (find_src_grp(r->rt_origin, r->rt_originmask, gt->gt_mcastgrp)) { + struct gtable *g; + + g = gtp ? gtp->gt_gnext : kernel_table; + log(LOG_WARNING, 0, "Entry for (%s %s) (rt:%x) exists (rt:%x)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), + r, g->gt_route); + } else { + if (gtp) { + gt->gt_gnext = gtp->gt_gnext; + gt->gt_gprev = gtp; + gtp->gt_gnext = gt; + } else { + gt->gt_gnext = kernel_table; + gt->gt_gprev = NULL; + kernel_table = gt; + } + if (gt->gt_gnext) + gt->gt_gnext->gt_gprev = gt; + } + } else { + gt->gt_gnext = gt->gt_prev = NULL; + } + } + + stnp = >->gt_srctbl; + while ((st = *stnp) != NULL) { + if (ntohl(st->st_origin) >= ntohl(origin)) + break; + stnp = &st->st_next; + } + + if (st == NULL || st->st_origin != origin) { + st = (struct stable *)malloc(sizeof(struct stable)); + if (st == NULL) + log(LOG_ERR, 0, "ran out of memory"); + + st->st_origin = origin; + st->st_pktcnt = 0; + st->st_next = *stnp; + *stnp = st; + } else { + log(LOG_WARNING, 0, "kernel entry already exists for (%s %s)", + inet_fmt(origin, s1), inet_fmt(mcastgrp, s2)); + return; + } + + kroutes++; + k_add_rg(origin, gt); + + log(LOG_DEBUG, 0, "add cache entry (%s %s) gm:%x, parent-vif:%d", + inet_fmt(origin, s1), + inet_fmt(mcastgrp, s2), + gt->gt_grpmems, r ? r->rt_parent : -1); + + /* If there are no leaf vifs + * which have this group, then + * mark this src-grp as a prune candidate. + */ + if (!gt->gt_prsent_timer && !gt->gt_grpmems && r && r->rt_gateway) + send_prune(gt); +} + +/* + * 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_int32_t addr; +{ + struct rtentry *r; + struct gtable *g; + struct ptable *pt, *prev_pt; + struct stable *st, *prev_st; + + for (g = kernel_table; g; g = g->gt_gnext) { + r = g->gt_route; + + /* + * If neighbor was the parent, remove the prune sent state + * Don't send any grafts upstream. + */ + if (vifi == r->rt_parent) { + if (addr == r->rt_gateway) { + log(LOG_DEBUG, 0, "reset_neighbor_state del prunes (%s %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2)); + + pt = g->gt_pruntbl; + while (pt) { + /* + * Expire prune, send again on this vif. + */ + VIFM_SET(pt->pt_vifi, g->gt_grpmems); + prev_pt = pt; + pt = prev_pt->pt_next; + free(prev_pt); + } + g->gt_pruntbl = NULL; + + st = g->gt_srctbl; + while (st) { + log(LOG_DEBUG, 0, "reset_neighbor_state del sg (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(g->gt_mcastgrp, s2)); + + if (k_del_rg(st->st_origin, g) < 0) { + log(LOG_WARNING, errno, + "reset_neighbor_state trying to delete (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(g->gt_mcastgrp, s2)); + } + kroutes--; + prev_st = st; + st = prev_st->st_next; + free(prev_st); + } + g->gt_srctbl = NULL; + /* + * Keep the group entries themselves around since the + * state will likely just come right back, and if not, + * the group entries will time out with no kernel entries + * and no prune state. + */ + g->gt_prsent_timer = 0; + g->gt_grftsnt = 0; + } + } else { + /* + * Neighbor was not the parent, send grafts to join the groups + */ + if (g->gt_prsent_timer) { + g->gt_grftsnt = 1; + send_graft(g); + g->gt_prsent_timer = 0; + } + + /* + * Remove any prunes that this router has sent us. + */ + prev_pt = (struct ptable *)&g->gt_pruntbl; + for (pt = g->gt_pruntbl; pt; pt = pt->pt_next) { + if (pt->pt_vifi == vifi && pt->pt_router == addr) { + prev_pt->pt_next = pt->pt_next; + free(pt); + } else + prev_pt = pt; + } + + /* + * And see if we want to forward again. + */ + if (!VIFM_ISSET(vifi, g->gt_grpmems)) { + if (VIFM_ISSET(vifi, r->rt_children) && + !(VIFM_ISSET(vifi, r->rt_leaves))) + VIFM_SET(vifi, g->gt_grpmems); + + if (VIFM_ISSET(vifi, r->rt_leaves) && + grplst_mem(vifi, g->gt_mcastgrp)) + VIFM_SET(vifi, g->gt_grpmems); + + g->gt_grpmems &= ~g->gt_scope; + prun_add_ttls(g); + + /* Update kernel state */ + update_kernel(g); + + log(LOG_DEBUG, 0, "reset member state (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); + } + } + } +} + +/* + * 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_int32_t mcastgrp; + u_int del_flag; +{ + struct gtable *g, *prev_g; + struct stable *st, *prev_st; + struct ptable *pt, *prev_pt; + + if (del_flag == DEL_ALL_ROUTES) { + g = r->rt_groups; + while (g) { + log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2)); + st = g->gt_srctbl; + while (st) { + if (k_del_rg(st->st_origin, g) < 0) { + log(LOG_WARNING, errno, + "del_table_entry trying to delete (%s, %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(g->gt_mcastgrp, s2)); + } + kroutes--; + prev_st = st; + st = st->st_next; + free(prev_st); + } + g->gt_srctbl = NULL; + + pt = g->gt_pruntbl; + while (pt) { + prev_pt = pt->pt_next; + free(pt); + pt = prev_pt; + } + g->gt_pruntbl = NULL; + + if (g->gt_gnext) + g->gt_gnext->gt_gprev = g->gt_gprev; + if (g->gt_gprev) + g->gt_gprev->gt_gnext = g->gt_gnext; + else + kernel_table = g->gt_gnext; + + prev_g = g->gt_next; + free(g); + g = prev_g; + } + r->rt_groups = NULL; + } + + /* + * Dummy routine - someday this may be needed, so it is just there + */ + if (del_flag == DEL_RTE_GROUP) { + prev_g = (struct gtable *)&r->rt_groups; + for (g = r->rt_groups; g; g = g->gt_next) { + if (g->gt_mcastgrp == mcastgrp) { + log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2)); + st = g->gt_srctbl; + while (st) { + if (k_del_rg(st->st_origin, g) < 0) { + log(LOG_WARNING, errno, + "del_table_entry trying to delete (%s, %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(g->gt_mcastgrp, s2)); + } + kroutes--; + prev_st = st->st_next; + free(st); + st = prev_st; + } + g->gt_srctbl = NULL; + + pt = g->gt_pruntbl; + while (pt) { + prev_pt = pt->pt_next; + free(pt); + pt = prev_pt; + } + g->gt_pruntbl = NULL; + + if (g->gt_gnext) + g->gt_gnext->gt_gprev = g->gt_gprev; + if (g->gt_gprev) + g->gt_gprev->gt_gnext = g->gt_gnext; + else + kernel_table = g->gt_gnext; + + if (prev_g != (struct gtable *)&r->rt_groups) + g->gt_next->gt_prev = prev_g; + else + g->gt_next->gt_prev = NULL; + prev_g->gt_next = g->gt_next; + + free(g); + g = prev_g; + } else { + prev_g = g; + } + } + } +} + +/* + * update kernel table entry when a route entry changes + */ +void +update_table_entry(r) + struct rtentry *r; +{ + struct gtable *g; + struct ptable *pt, *prev_pt; + int i; + + for (g = r->rt_groups; g; g = g->gt_next) { + pt = g->gt_pruntbl; + while (pt) { + prev_pt = pt->pt_next; + free(pt); + pt = prev_pt; + } + g->gt_pruntbl = NULL; + + g->gt_grpmems = 0; + + /* obtain the multicast group membership list */ + for (i = 0; i < numvifs; i++) { + if (VIFM_ISSET(i, r->rt_children) && + !(VIFM_ISSET(i, r->rt_leaves))) + VIFM_SET(i, g->gt_grpmems); + + if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, g->gt_mcastgrp)) + VIFM_SET(i, g->gt_grpmems); + } + if (VIFM_ISSET(r->rt_parent, g->gt_scope)) + g->gt_scope = -1; + g->gt_grpmems &= ~g->gt_scope; + + log(LOG_DEBUG, 0, "updating cache entries (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), + g->gt_grpmems); + + if (g->gt_grpmems && g->gt_prsent_timer) { + g->gt_grftsnt = 1; + send_graft(g); + g->gt_prsent_timer = 0; + } + + /* update ttls and add entry into kernel */ + prun_add_ttls(g); + update_kernel(g); + + /* Check if we want to prune this group */ + if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) { + g->gt_timer = CACHE_LIFETIME(cache_lifetime); + send_prune(g); + } + } +} + +/* + * set the forwarding flag for all mcastgrps on this vifi + */ +void +update_lclgrp(vifi, mcastgrp) + vifi_t vifi; + u_int32_t mcastgrp; +{ + struct rtentry *r; + struct gtable *g; + + log(LOG_DEBUG, 0, "group %s joined on vif %d", + inet_fmt(mcastgrp, s1), vifi); + + for (g = kernel_table; g; g = g->gt_gnext) { + if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp)) + break; + + r = g->gt_route; + if (g->gt_mcastgrp == mcastgrp && + VIFM_ISSET(vifi, r->rt_children)) { + + VIFM_SET(vifi, g->gt_grpmems); + g->gt_grpmems &= ~g->gt_scope; + if (g->gt_grpmems == 0) + continue; + + prun_add_ttls(g); + log(LOG_DEBUG, 0, "update lclgrp (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); + + update_kernel(g); + } + } +} + +/* + * reset forwarding flag for all mcastgrps on this vifi + */ +void +delete_lclgrp(vifi, mcastgrp) + vifi_t vifi; + u_int32_t mcastgrp; +{ + struct rtentry *r; + struct gtable *g; + + log(LOG_DEBUG, 0, "group %s left on vif %d", + inet_fmt(mcastgrp, s1), vifi); + + for (g = kernel_table; g; g = g->gt_gnext) { + if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp)) + break; + + if (g->gt_mcastgrp == mcastgrp) { + int stop_sending = 1; + + r = g->gt_route; + /* + * If this is not a leaf, then we have router neighbors on this + * vif. Only turn off forwarding if they have all pruned. + */ + if (!VIFM_ISSET(vifi, r->rt_leaves)) { + struct listaddr *vr; + + for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next) + if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) { + stop_sending = 0; + break; + } + } + + if (stop_sending) { + VIFM_CLR(vifi, g->gt_grpmems); + log(LOG_DEBUG, 0, "delete lclgrp (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); + + prun_add_ttls(g); + update_kernel(g); + + /* + * If there are no more members of this particular group, + * send prune upstream + */ + if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) + send_prune(g); + } + } + } +} + +/* + * 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_int32_t src; + u_int32_t dst; + char *p; + int datalen; +{ + u_int32_t prun_src; + u_int32_t prun_grp; + u_int32_t prun_tmr; + vifi_t vifi; + int i; + int stop_sending; + struct rtentry *r; + struct gtable *g; + struct ptable *pt; + 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)); + 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_grp)[i] = *p++; + for (i = 0; i< 4; i++) + ((char *)&prun_tmr)[i] = *p++; + + log(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s)/%d", + inet_fmt(src, s1), vifi, + inet_fmt(prun_src, s2), inet_fmt(prun_grp, s3), prun_tmr); + + /* + * Find the subnet for the prune + */ + if (find_src_grp(prun_src, 0, prun_grp)) { + g = gtp ? gtp->gt_gnext : kernel_table; + r = g->gt_route; + + if (!VIFM_ISSET(vifi, r->rt_children)) { + log(LOG_WARNING, 0, "prune received from non-child %s for (%s %s)", + inet_fmt(src, s1), inet_fmt(prun_src, s2), + inet_fmt(prun_grp, s3)); + return; + } + if (VIFM_ISSET(vifi, g->gt_scope)) { + log(LOG_WARNING, 0, "prune received from %s on scoped grp (%s %s)", + inet_fmt(src, s1), inet_fmt(prun_src, s2), + inet_fmt(prun_grp, s3)); + return; + } + if ((pt = find_prune_entry(src, g->gt_pruntbl)) != NULL) { + /* + * If it's about to expire, then it's only still around because + * of timer granularity, so don't warn about it. + */ + if (pt->pt_timer > 10) { + log(LOG_WARNING, 0, "%s %d from %s for (%s %s)/%d %s %d %s %x", + "duplicate prune received on vif", + vifi, inet_fmt(src, s1), inet_fmt(prun_src, s2), + inet_fmt(prun_grp, s3), prun_tmr, + "old timer:", pt->pt_timer, "cur gm:", g->gt_grpmems); + } + pt->pt_timer = prun_tmr; + } else { + /* allocate space for the prune structure */ + pt = (struct ptable *)(malloc(sizeof(struct ptable))); + if (pt == NULL) + log(LOG_ERR, 0, "pt: ran out of memory"); + + pt->pt_vifi = vifi; + pt->pt_router = src; + pt->pt_timer = prun_tmr; + + pt->pt_next = g->gt_pruntbl; + g->gt_pruntbl = pt; + } + + /* Refresh the group's lifetime */ + g->gt_timer = CACHE_LIFETIME(cache_lifetime); + if (g->gt_timer < prun_tmr) + g->gt_timer = prun_tmr; + + /* + * check if any more packets need to be sent on the + * vif which sent this message + */ + stop_sending = 1; + for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next) + if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) { + stop_sending = 0; + break; + } + + if (stop_sending && !grplst_mem(vifi, prun_grp)) { + VIFM_CLR(vifi, g->gt_grpmems); + log(LOG_DEBUG, 0, "prune (%s %s), stop sending on vif %d, gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), vifi, g->gt_grpmems); + + prun_add_ttls(g); + update_kernel(g); + } + + /* + * 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 (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) { + send_prune(g); + } + } else { + /* + * There is no kernel entry for this group. Therefore, we can + * simply ignore the prune, as we are not forwarding this traffic + * downstream. + */ + log(LOG_DEBUG, 0, "%s (%s %s)/%d from %s", + "prune message received with no kernel entry for", + inet_fmt(prun_src, s1), inet_fmt(prun_grp, s2), + prun_tmr, inet_fmt(src, s3)); + return; + } +} + +/* + * 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_int32_t mcastgrp; +{ + struct rtentry *r; + struct gtable *g; + + for (g = kernel_table; g; g = g->gt_gnext) { + if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp)) + break; + + r = g->gt_route; + if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, r->rt_children)) + if (g->gt_prsent_timer) { + VIFM_SET(vifi, g->gt_grpmems); + + /* + * If the vif that was joined was a scoped vif, + * ignore it ; don't graft back + */ + g->gt_grpmems &= ~g->gt_scope; + if (g->gt_grpmems == 0) + continue; + + /* set the flag for graft retransmission */ + g->gt_grftsnt = 1; + + /* send graft upwards */ + send_graft(g); + + /* reset the prune timer and update cache timer*/ + g->gt_prsent_timer = 0; + g->gt_timer = max_prune_lifetime; + + log(LOG_DEBUG, 0, "chkgrp graft (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); + + prun_add_ttls(g); + update_kernel(g); + } + } +} + +/* 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 send ack downstream. + */ +void +accept_graft(src, dst, p, datalen) + u_int32_t src; + u_int32_t dst; + char *p; + int datalen; +{ + vifi_t vifi; + u_int32_t graft_src; + u_int32_t graft_grp; + int i; + struct rtentry *r; + struct gtable *g; + struct ptable *pt, **ptnp; + + if ((vifi = find_vif(src, dst)) == NO_VIF) { + log(LOG_INFO, 0, + "ignoring graft from non-neighbor %s", + inet_fmt(src, s1)); + return; + } + + if (datalen < 8) { + log(LOG_WARNING, 0, + "received non-decipherable graft from %s", + inet_fmt(src, s1)); + return; + } + + for (i = 0; i< 4; i++) + ((char *)&graft_src)[i] = *p++; + for (i = 0; i< 4; i++) + ((char *)&graft_grp)[i] = *p++; + + log(LOG_DEBUG, 0, "%s on vif %d grafts (%s %s)", + inet_fmt(src, s1), vifi, + inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3)); + + /* + * Find the subnet for the graft + */ + if (find_src_grp(graft_src, 0, graft_grp)) { + g = gtp ? gtp->gt_gnext : kernel_table; + r = g->gt_route; + + if (VIFM_ISSET(vifi, g->gt_scope)) { + log(LOG_WARNING, 0, "graft received from %s on scoped grp (%s %s)", + inet_fmt(src, s1), inet_fmt(graft_src, s2), + inet_fmt(graft_grp, s3)); + return; + } + + ptnp = &g->gt_pruntbl; + while ((pt = *ptnp) != NULL) { + if ((pt->pt_vifi == vifi) && (pt->pt_router == src)) { + *ptnp = pt->pt_next; + free(pt); + + VIFM_SET(vifi, g->gt_grpmems); + log(LOG_DEBUG, 0, "accept graft (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); + + prun_add_ttls(g); + update_kernel(g); + break; + } else { + ptnp = &pt->pt_next; + } + } + + /* send ack downstream */ + send_graft_ack(dst, src, graft_src, graft_grp); + g->gt_timer = max_prune_lifetime; + + if (g->gt_prsent_timer) { + /* set the flag for graft retransmission */ + g->gt_grftsnt = 1; + + /* send graft upwards */ + send_graft(g); + + /* reset the prune sent timer */ + g->gt_prsent_timer = 0; + } + } else { + /* + * We have no state for the source and group in question. + * We can simply acknowledge the graft, since we know + * that we have no prune state, and grafts are requests + * to remove prune state. + */ + send_graft_ack(dst, src, graft_src, graft_grp); + log(LOG_DEBUG, 0, "%s (%s %s) from %s", + "graft received with no kernel entry for", + inet_fmt(graft_src, s1), inet_fmt(graft_grp, s2), + inet_fmt(src, s3)); + return; + } +} + +/* + * 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_int32_t src; + u_int32_t dst; + char *p; + int datalen; +{ + struct gtable *g; + vifi_t vifi; + u_int32_t grft_src; + u_int32_t grft_grp; + int i; + + if ((vifi = find_vif(src, dst)) == NO_VIF) { + log(LOG_INFO, 0, + "ignoring graft ack from non-neighbor %s", + inet_fmt(src, s1)); + return; + } + + if (datalen < 0 || datalen > 8) { + log(LOG_WARNING, 0, + "received non-decipherable graft ack 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_grp)[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_grp, s3)); + + /* + * Find the subnet for the graft ack + */ + if (find_src_grp(grft_src, 0, grft_grp)) { + g = gtp ? gtp->gt_gnext : kernel_table; + g->gt_grftsnt = 0; + } else { + log(LOG_WARNING, 0, "%s (%s, %s) from %s", + "rcvd graft ack with no kernel entry for", + inet_fmt(grft_src, s1), inet_fmt(grft_grp, s2), + inet_fmt(src, s3)); + return; + } +} + + +/* + * free all prune entries and kernel routes + * normally, this should inform the kernel that all of its routes + * are going away, but this is only called by restart(), which is + * about to call MRT_DONE which does that anyway. + */ +void +free_all_prunes() +{ + register struct rtentry *r; + register struct gtable *g, *prev_g; + register struct stable *s, *prev_s; + register struct ptable *p, *prev_p; + + for (r = routing_table; r; r = r->rt_next) { + g = r->rt_groups; + while (g) { + s = g->gt_srctbl; + while (s) { + prev_s = s->st_next; + free(s); + s = prev_s; + } + + p = g->gt_pruntbl; + while (p) { + prev_p = p->pt_next; + free(p); + p = prev_p; + } + + prev_g = g->gt_next; + free(g); + g = prev_g; + } + r->rt_groups = NULL; + } + kernel_table = NULL; + + g = kernel_no_route; + while (g) { + if (g->gt_srctbl) + free(g->gt_srctbl); + + prev_g = g->gt_next; + free(g); + g = prev_g; + } + kernel_no_route = NULL; +} + +/* + * When a new route is created, search + * a) The less-specific part of the routing table + * b) The route-less kernel table + * for sources that the new route might want to handle. + * + * "Inheriting" these sources might be cleanest, but simply deleting + * them is easier, and letting the kernel re-request them. + */ +void +steal_sources(rt) + struct rtentry *rt; +{ + register struct rtentry *rp; + register struct gtable *gt, **gtnp; + register struct stable *st, **stnp; + + for (rp = rt->rt_next; rp; rp = rp->rt_next) { + if ((rt->rt_origin & rp->rt_originmask) == rp->rt_origin) { + log(LOG_DEBUG, 0, "Route for %s stealing sources from %s", + inet_fmts(rt->rt_origin, rt->rt_originmask, s1), + inet_fmts(rp->rt_origin, rp->rt_originmask, s2)); + for (gt = rp->rt_groups; gt; gt = gt->gt_next) { + stnp = >->gt_srctbl; + while ((st = *stnp) != NULL) { + if ((st->st_origin & rt->rt_originmask) == rt->rt_origin) { + log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s", + inet_fmts(rt->rt_origin, rt->rt_originmask, s1), + inet_fmt(st->st_origin, s3), + inet_fmt(gt->gt_mcastgrp, s4), + inet_fmts(rp->rt_origin, rp->rt_originmask, s2)); + if (k_del_rg(st->st_origin, gt) < 0) { + log(LOG_WARNING, errno, "%s (%s, %s)", + "steal_sources trying to delete", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + *stnp = st->st_next; + kroutes--; + free(st); + } else { + stnp = &st->st_next; + } + } + } + } + } + + gtnp = &kernel_no_route; + while ((gt = *gtnp) != NULL) { + if (gt->gt_srctbl && ((gt->gt_srctbl->st_origin & rt->rt_originmask) + == rt->rt_origin)) { + log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s", + inet_fmts(rt->rt_origin, rt->rt_originmask, s1), + inet_fmt(gt->gt_srctbl->st_origin, s3), + inet_fmt(gt->gt_mcastgrp, s4), + "no_route table"); + if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) { + log(LOG_WARNING, errno, "%s (%s %s)", + "steal_sources trying to delete", + inet_fmt(gt->gt_srctbl->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + kroutes--; + free(gt->gt_srctbl); + *gtnp = gt->gt_next; + if (gt->gt_next) + gt->gt_next->gt_prev = gt->gt_prev; + free(gt); + } else { + gtnp = >->gt_next; + } + } +} + +/* + * 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 rtentry *r; + struct gtable *gt, **gtnptr; + struct stable *st, **stnp; + struct ptable *pt, **ptnp; + struct sioc_sg_req sg_req; + + log(LOG_DEBUG, 0, "ageing entries"); + + gtnptr = &kernel_table; + while ((gt = *gtnptr) != NULL) { + r = gt->gt_route; + + /* advance the timer for the kernel entry */ + gt->gt_timer -= ROUTE_MAX_REPORT_DELAY; + + /* decrement prune timer if need be */ + if (gt->gt_prsent_timer > 0) { + gt->gt_prsent_timer -= ROUTE_MAX_REPORT_DELAY; + if (gt->gt_prsent_timer <= 0) { + log(LOG_DEBUG, 0, "upstream prune tmo (%s %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + gt->gt_prsent_timer = -1; + } + } + + /* retransmit graft if graft sent flag is still set */ + if (gt->gt_grftsnt) { + register int y; + CHK_GS(gt->gt_grftsnt++, y); + if (y) + send_graft(gt); + } + + /* + * Age prunes + * + * If a prune expires, forward again on that vif. + */ + ptnp = >->gt_pruntbl; + while ((pt = *ptnp) != NULL) { + if ((pt->pt_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) { + log(LOG_DEBUG, 0, "expire prune (%s %s) from %s on vif %d", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2), + inet_fmt(pt->pt_router, s3), + pt->pt_vifi); + + /* + * No need to send a graft, any prunes that we sent + * will expire before any prunes that we have received. + */ + if (gt->gt_prsent_timer > 0) { + log(LOG_DEBUG, 0, "prune expired with %d left on %s", + gt->gt_prsent_timer, "prsent_timer"); + gt->gt_prsent_timer = 0; + } + + /* modify the kernel entry to forward packets */ + if (!VIFM_ISSET(pt->pt_vifi, gt->gt_grpmems)) { + VIFM_SET(pt->pt_vifi, gt->gt_grpmems); + log(LOG_DEBUG, 0, "forw again (%s %s) gm:%x vif:%d", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems, + pt->pt_vifi); + + prun_add_ttls(gt); + update_kernel(gt); + } + + /* remove the router's prune entry and await new one */ + *ptnp = pt->pt_next; + free(pt); + } else { + ptnp = &pt->pt_next; + } + } + + /* + * If the cache entry has expired, check for downstream prunes. + * + * If there are downstream prunes, refresh the cache entry's timer. + * Otherwise, check for traffic. If no traffic, delete this + * entry. + */ + if (gt->gt_timer <= 0) { + if (gt->gt_pruntbl) { + if (gt->gt_prsent_timer == -1) + gt->gt_prsent_timer = 0; + gt->gt_timer = CACHE_LIFETIME(cache_lifetime); + gtnptr = >->gt_gnext; + continue; + } + + /* + * If this entry was pruned, but all downstream prunes + * have expired, then it is safe to simply delete it. + * Otherwise, check for traffic before deleting. + */ + if (gt->gt_prsent_timer == 0) { + sg_req.grp.s_addr = gt->gt_mcastgrp; + stnp = >->gt_srctbl; + while ((st = *stnp) != NULL) { + sg_req.src.s_addr = st->st_origin; + if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) + < 0) { + log(LOG_WARNING, errno, "%s (%s %s)", + "age_table_entry: SIOCGETSGCNT failing for", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + /* Make sure it gets deleted below */ + sg_req.pktcnt = st->st_pktcnt; + } + if (sg_req.pktcnt == st->st_pktcnt) { + *stnp = st->st_next; + log(LOG_DEBUG, 0, + "age_table_entry deleting (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + if (k_del_rg(st->st_origin, gt) < 0) { + log(LOG_WARNING, errno, + "age_table_entry trying to delete (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + kroutes--; + free(st); + } else { + stnp = &st->st_next; + } + } + + if (gt->gt_srctbl) { + /* At least one source in the list still has traffic */ + gt->gt_timer = CACHE_LIFETIME(cache_lifetime); + gtnptr = >->gt_gnext; + continue; + } + } + + log(LOG_DEBUG, 0, "timeout cache entry (%s, %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + + /* free all the source entries */ + while (st = gt->gt_srctbl) { + log(LOG_DEBUG, 0, + "age_table_entry (P) deleting (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + if (k_del_rg(st->st_origin, gt) < 0) { + log(LOG_WARNING, errno, + "age_table_entry (P) trying to delete (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + kroutes--; + gt->gt_srctbl = st->st_next; + free(st); + } + + /* free all the prune list entries */ + while (gt->gt_pruntbl) { + gt->gt_pruntbl = pt->pt_next; + free(pt); + } + + if (gt->gt_prev) + gt->gt_prev->gt_next = gt->gt_next; + else + gt->gt_route->rt_groups = gt->gt_next; + if (gt->gt_next) + gt->gt_next->gt_prev = gt->gt_prev; + + if (gt->gt_gprev) { + gt->gt_gprev->gt_gnext = gt->gt_gnext; + gtnptr = >->gt_gprev->gt_gnext; + } else { + kernel_table = gt->gt_gnext; + gtnptr = &kernel_table; + } + if (gt->gt_gnext) + gt->gt_gnext->gt_gprev = gt->gt_gprev; + + free((char *)gt); + } else { + if (gt->gt_prsent_timer == -1) + gt->gt_prsent_timer = 0; + gtnptr = >->gt_gnext; + } + } + + /* + * When traversing the no_route table, the decision is much easier. + * Just delete it if it has timed out. + */ + gtnptr = &kernel_no_route; + while ((gt = *gtnptr) != NULL) { + /* advance the timer for the kernel entry */ + gt->gt_timer -= ROUTE_MAX_REPORT_DELAY; + + if (gt->gt_timer < 0) { + if (gt->gt_srctbl) { + if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) { + log(LOG_WARNING, errno, "%s (%s %s)", + "age_table_entry trying to delete no-route", + inet_fmt(gt->gt_srctbl->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + free(gt->gt_srctbl); + } + *gtnptr = gt->gt_next; + if (gt->gt_next) + gt->gt_next->gt_prev = gt->gt_prev; + + free((char *)gt); + } else { + gtnptr = >->gt_next; + } + } +} + +char * +scaletime(t) + u_long t; +{ + static char buf1[5]; + static char buf2[5]; + static char *buf=buf1; + char s; + char *p; + + p = buf; + if (buf == buf1) + buf = buf2; + else + buf = buf1; + + if (t < 120) { + s = 's'; + } else if (t < 3600) { + t /= 60; + s = 'm'; + } else if (t < 86400) { + t /= 3600; + s = 'h'; + } else if (t < 864000) { + t /= 86400; + s = 'd'; + } else { + t /= 604800; + s = 'w'; + } + if (t > 999) + return "*** "; + + sprintf(p,"%3d%c", t, s); + + return p; +} + +/* + * Print the contents of the cache table on file 'fp2'. + */ +void +dump_cache(fp2) + FILE *fp2; +{ + register struct rtentry *r; + register struct gtable *gt; + register struct stable *st; + register struct ptable *pt; + register int i; + register time_t thyme = time(0); + + fprintf(fp2, + "Multicast Routing Cache Table (%d entries)\n%s", kroutes, + " Origin Mcast-group CTmr Age Ptmr IVif Forwvifs\n"); + + for (gt = kernel_no_route; gt; gt = gt->gt_next) { + if (gt->gt_srctbl) { + fprintf(fp2, " %-18s %-15s %-4s %-4s - -1\n", + inet_fmts(gt->gt_srctbl->st_origin, 0xffffffff, s1), + inet_fmt(gt->gt_mcastgrp, s2), scaletime(gt->gt_timer), + scaletime(thyme - gt->gt_ctime)); + fprintf(fp2, ">%s\n", inet_fmt(gt->gt_srctbl->st_origin, s1)); + } + } + + for (gt = kernel_table; gt; gt = gt->gt_gnext) { + r = gt->gt_route; + fprintf(fp2, " %-18s %-15s", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + + fprintf(fp2, " %-4s", scaletime(gt->gt_timer)); + + fprintf(fp2, " %-4s %-4s ", scaletime(thyme - gt->gt_ctime), + gt->gt_prsent_timer ? scaletime(gt->gt_prsent_timer) : + " -"); + + fprintf(fp2, "%2u%c%c ", r->rt_parent, + gt->gt_prsent_timer ? 'P' : ' ', + VIFM_ISSET(r->rt_parent, gt->gt_scope) ? 'B' : ' '); + + for (i = 0; i < numvifs; ++i) { + if (VIFM_ISSET(i, gt->gt_grpmems)) + fprintf(fp2, " %u ", i); + else if (VIFM_ISSET(i, r->rt_children) && + !VIFM_ISSET(i, r->rt_leaves)) + fprintf(fp2, " %u%c", i, + VIFM_ISSET(i, gt->gt_scope) ? 'b' : 'p'); + } + fprintf(fp2, "\n"); + for (st = gt->gt_srctbl; st; st = st->st_next) { + fprintf(fp2, ">%s\n", inet_fmt(st->st_origin, s1)); + } +#ifdef DEBUG_PRUNES + for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) { + fprintf(fp2, "<r:%s v:%d t:%d\n", inet_fmt(pt->pt_router, s1), + pt->pt_vifi, pt->pt_timer); + } +#endif + } +} + +/* + * Traceroute function which returns traceroute replies to the requesting + * router. Also forwards the request to downstream routers. + */ +void +accept_mtrace(src, dst, group, data, no, datalen) + u_int32_t src; + u_int32_t dst; + u_int32_t group; + char *data; + u_char no; + int datalen; +{ + u_char type; + struct rtentry *rt; + struct gtable *gt; + struct tr_query *qry; + struct tr_resp *resp; + int vifi; + char *p; + int rcount; + int errcode = TR_NO_ERR; + int resptype; + struct timeval tp; + struct sioc_vif_req v_req; + struct sioc_sg_req sg_req; + + /* Remember qid across invocations */ + static u_int32_t oqid = 0; + + /* timestamp the request/response */ + gettimeofday(&tp, 0); + + /* + * Check if it is a query or a response + */ + if (datalen == QLEN) { + type = QUERY; + log(LOG_DEBUG, 0, "Traceroute query rcvd from %s to %s", + inet_fmt(src, s1), inet_fmt(dst, s2)); + } + else if ((datalen - QLEN) % RLEN == 0) { + type = RESP; + log(LOG_DEBUG, 0, "Traceroute response rcvd from %s to %s", + inet_fmt(src, s1), inet_fmt(dst, s2)); + if IN_MULTICAST(ntohl(dst)) { + log(LOG_DEBUG, 0, "Dropping multicast response"); + return; + } + } + else { + log(LOG_WARNING, 0, "%s from %s to %s", + "Non decipherable tracer request recieved", + inet_fmt(src, s1), inet_fmt(dst, s2)); + return; + } + + qry = (struct tr_query *)data; + + if (oqid == qry->tr_qid) { + /* + * If the multicast router is a member of the group being + * queried, and the query is multicasted, then the router can + * recieve multiple copies of the same query. If we have already + * replied to this traceroute, just ignore it this time. + * + * This is not a total solution, but since if this fails you + * only get N copies, N <= the number of interfaces on the router, + * it is not fatal. + */ + log(LOG_DEBUG, 0, "ignoring duplicate traceroute packet"); + return; + } else + oqid = qry->tr_qid; + + /* + * if it is a packet with all reports filled, drop it + */ + if ((rcount = (datalen - QLEN)/RLEN) == no) { + log(LOG_DEBUG, 0, "packet with all reports filled in"); + return; + } + + log(LOG_DEBUG, 0, "s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1), + inet_fmt(group, s2), inet_fmt(qry->tr_dst, s3)); + log(LOG_DEBUG, 0, "rttl: %d rd: %s", qry->tr_rttl, + inet_fmt(qry->tr_raddr, s1)); + log(LOG_DEBUG, 0, "rcount:%d", rcount); + + /* determine the routing table entry for this traceroute */ + rt = determine_route(qry->tr_src); + if (rt) { + log(LOG_DEBUG, 0, "rt parent vif: %d rtr: %s metric: %d", + rt->rt_parent, inet_fmt(rt->rt_gateway, s1), rt->rt_metric); + log(LOG_DEBUG, 0, "rt origin %s", + inet_fmts(rt->rt_origin, rt->rt_originmask, s1)); + } else + log(LOG_DEBUG, 0, "...no route"); + + /* + * 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) { + log(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s", + inet_fmt(qry->tr_src, s1)); + if (IN_MULTICAST(ntohl(dst))) + return; + } + vifi = find_vif(qry->tr_dst, 0); + + if (vifi == NO_VIF) { + /* The traceroute destination is not on one of my subnet vifs. */ + log(LOG_DEBUG, 0, "Destination %s not an interface", + inet_fmt(qry->tr_dst, s1)); + if (IN_MULTICAST(ntohl(dst))) + return; + errcode = TR_WRONG_IF; + } else if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) { + log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s", + inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2)); + if (IN_MULTICAST(ntohl(dst))) + return; + errcode = TR_WRONG_IF; + } + } + else { + /* + * determine which interface the packet came in on + * RESP packets travel hop-by-hop so this either traversed + * a tunnel or came from a directly attached mrouter. + */ + if ((vifi = find_vif(src, dst)) == NO_VIF) { + log(LOG_DEBUG, 0, "Wrong interface for packet"); + errcode = TR_WRONG_IF; + } + } + + log(LOG_DEBUG, 0, "Sending traceroute response"); + + /* copy the packet to the sending buffer */ + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + + bcopy(data, p, datalen); + + p += datalen; + + /* + * If there is no room to insert our reply, coopt the previous hop + * error indication to relay this fact. + */ + if (p + sizeof(struct tr_resp) > send_buf + RECV_BUF_SIZE) { + resp = (struct tr_resp *)p - 1; + resp->tr_rflags = TR_NO_SPACE; + rt = NULL; + goto sendit; + } + + /* + * fill in initial response fields + */ + resp = (struct tr_resp *)p; + bzero(resp, sizeof(struct tr_resp)); + datalen += RLEN; + + resp->tr_qarr = ((tp.tv_sec + JAN_1970) << 16) + + ((tp.tv_usec >> 4) & 0xffff); + + resp->tr_rproto = PROTO_DVMRP; + if (errcode != TR_NO_ERR) { + resp->tr_rflags = errcode; + rt = NULL; /* hack to enforce send straight to requestor */ + goto sendit; + } + 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 + */ + if (rt) + for (gt = rt->rt_groups; gt; gt = gt->gt_next) { + if (gt->gt_mcastgrp >= group) + break; + } + else + gt = NULL; + + if (gt && gt->gt_mcastgrp == group) { + 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.pktcnt; + + if (VIFM_ISSET(vifi, gt->gt_scope)) + resp->tr_rflags = TR_SCOPED; + else if (gt->gt_prsent_timer) + resp->tr_rflags = TR_PRUNED; + else if (!VIFM_ISSET(vifi, gt->gt_grpmems)) + if (VIFM_ISSET(vifi, rt->rt_children) && + !VIFM_ISSET(vifi, rt->rt_leaves)) + resp->tr_rflags = TR_OPRUNED; + else + resp->tr_rflags = TR_NO_FWD; + } else { + if (scoped_addr(vifi, group)) + resp->tr_rflags = TR_SCOPED; + else if (!VIFM_ISSET(vifi, rt->rt_children)) + resp->tr_rflags = TR_NO_FWD; + } + + /* + * if no rte exists, set NO_RTE error + */ + if (rt == NULL) { + src = dst; /* the dst address of resp. pkt */ + resp->tr_inaddr = 0; + resp->tr_rflags = TR_NO_RTE; + 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)) { + log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s", + inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2)); + resp->tr_rflags = TR_WRONG_IF; + } + if (rt->rt_metric >= UNREACHABLE) { + resp->tr_rflags = TR_NO_RTE; + /* Hack to send reply directly */ + rt = NULL; + } + } + +sendit: + /* + * if metric is 1 or no. of reports is 1, send response to requestor + * else send to upstream router. If the upstream router can't handle + * mtrace, set an error code and send to requestor anyway. + */ + log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no); + + if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1)) { + resptype = IGMP_MTRACE_REPLY; + dst = qry->tr_raddr; + } else + if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) { + dst = qry->tr_raddr; + resp->tr_rflags = TR_OLD_ROUTER; + resptype = IGMP_MTRACE_REPLY; + } else { + dst = rt->rt_gateway; + resptype = IGMP_MTRACE_QUERY; + } + + log(LOG_DEBUG, 0, "Sending %s to %s from %s", + resptype == IGMP_MTRACE_REPLY ? "response" : "request on", + inet_fmt(dst, s1), inet_fmt(src, s2)); + + if (IN_MULTICAST(ntohl(dst))) { + k_set_ttl(qry->tr_rttl); + /* Let the kernel pick the source address, since we might have picked + * a disabled phyint to multicast on. + */ + send_igmp(INADDR_ANY, dst, + resptype, no, group, + datalen); + k_set_ttl(1); + } else + send_igmp(src, dst, + resptype, no, group, + datalen); + return; +} diff --git a/usr.sbin/mrouted/prune.h b/usr.sbin/mrouted/prune.h new file mode 100644 index 00000000000..22df280b830 --- /dev/null +++ b/usr.sbin/mrouted/prune.h @@ -0,0 +1,139 @@ +/* $NetBSD: prune.h,v 1.2 1995/10/09 03:51:52 thorpej Exp $ */ + +/* + * 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. + */ + +/* + * Group table + * + * Each group entry is a member of two doubly-linked lists: + * + * a) A list hanging off of the routing table entry for this source (rt_groups) + * sorted by group address under the routing entry (gt_next, gt_prev) + * b) An independent list pointed to by kernel_table, which is a list of + * active source,group's (gt_gnext, gt_gprev). + * + */ +struct gtable { + struct gtable *gt_next; /* pointer to the next entry */ + struct gtable *gt_prev; /* back pointer for linked list */ + struct gtable *gt_gnext; /* fwd pointer for group list */ + struct gtable *gt_gprev; /* rev pointer for group list */ + u_int32_t gt_mcastgrp; /* multicast group associated */ + vifbitmap_t gt_scope; /* scoped interfaces */ + u_char gt_ttls[MAXVIFS]; /* ttl vector for forwarding */ + vifbitmap_t gt_grpmems; /* forw. vifs for src, grp */ + int gt_prsent_timer; /* prune timer for this group */ + int gt_timer; /* timer for this group entry */ + time_t gt_ctime; /* time of entry creation */ + u_char gt_grftsnt; /* graft sent/retransmit timer */ + struct stable *gt_srctbl; /* source table */ + struct ptable *gt_pruntbl; /* prune table */ + struct rtentry *gt_route; /* parent route */ +}; + +/* + * Source table + * + * When source-based prunes exist, there will be a struct ptable here as well. + */ +struct stable +{ + struct stable *st_next; /* pointer to the next entry */ + u_int32_t st_origin; /* host origin of multicasts */ + u_long st_pktcnt; /* packet count for src-grp entry */ +}; + +/* + * structure to store incoming prunes. Can hang off of either group or source. + */ +struct ptable +{ + struct ptable *pt_next; /* pointer to the next entry */ + u_int32_t pt_router; /* router that sent this prune */ + vifi_t pt_vifi; /* vif prune received on */ + int pt_timer; /* timer for prune */ +}; + +/* + * The packet format for a traceroute request. + */ +struct tr_query { + u_int32_t tr_src; /* traceroute source */ + u_int32_t tr_dst; /* traceroute destination */ + u_int32_t tr_raddr; /* traceroute response address */ +#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) + struct { + u_int qid : 24; /* traceroute query id */ + u_int ttl : 8; /* traceroute response ttl */ + } q; +#else + struct { + u_int ttl : 8; /* traceroute response ttl */ + u_int qid : 24; /* traceroute query id */ + } q; +#endif /* BYTE_ORDER */ +}; + +#define tr_rttl q.ttl +#define tr_qid q.qid + +/* + * Traceroute response format. A traceroute response has a tr_query at the + * beginning, followed by one tr_resp for each hop taken. + */ +struct tr_resp { + u_int32_t tr_qarr; /* query arrival time */ + u_int32_t tr_inaddr; /* incoming interface address */ + u_int32_t tr_outaddr; /* outgoing interface address */ + u_int32_t tr_rmtaddr; /* parent address in source tree */ + u_int32_t tr_vifin; /* input packet count on interface */ + u_int32_t tr_vifout; /* output packet count on interface */ + u_int32_t tr_pktcnt; /* total incoming packets for src-grp */ + u_char tr_rproto; /* routing protocol deployed on router */ + u_char tr_fttl; /* ttl required to forward on outvif */ + u_char tr_smask; /* subnet mask for src addr */ + u_char tr_rflags; /* forwarding error codes */ +}; + +/* defs within mtrace */ +#define QUERY 1 +#define RESP 2 +#define QLEN sizeof(struct tr_query) +#define RLEN sizeof(struct tr_resp) + +/* fields for tr_rflags (forwarding error codes) */ +#define TR_NO_ERR 0 +#define TR_WRONG_IF 1 +#define TR_PRUNED 2 +#define TR_OPRUNED 3 +#define TR_SCOPED 4 +#define TR_NO_RTE 5 +#define TR_NO_FWD 7 +#define TR_NO_SPACE 0x81 +#define TR_OLD_ROUTER 0x82 + +/* fields for tr_rproto (routing protocol) */ +#define PROTO_DVMRP 1 +#define PROTO_MOSPF 2 +#define PROTO_PIM 3 +#define PROTO_CBT 4 + +#define MASK_TO_VAL(x, i) { \ + u_int32_t _x = ntohl(x); \ + (i) = 0; \ + while ((_x) << (i)) \ + (i)++; \ + }; + +#define VAL_TO_MASK(x, i) { \ + x = htonl(~((1 << (32 - (i))) - 1)); \ + }; + +#define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv) diff --git a/usr.sbin/mrouted/route.c b/usr.sbin/mrouted/route.c new file mode 100644 index 00000000000..d45de2361af --- /dev/null +++ b/usr.sbin/mrouted/route.c @@ -0,0 +1,1139 @@ +/* $NetBSD: route.c,v 1.4 1995/10/09 03:51:53 thorpej Exp $ */ + +/* + * 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. + */ + + +#include "defs.h" + + +/* + * This define statement saves a lot of space later + */ +#define RT_ADDR (struct rtentry *)&routing_table + +/* + * Exported variables. + */ +int routes_changed; /* 1=>some routes have changed */ +int delay_change_reports; /* 1=>postpone change reports */ + + +/* + * The routing table is shared with prune.c , so must not be static. + */ +struct rtentry *routing_table; /* pointer to list of route entries */ + +/* + * Private variables. + */ +static struct rtentry *rtp; /* pointer to a route entry */ +static struct rtentry *rt_end; /* pointer to last route entry */ +unsigned int nroutes; /* current number of route entries */ + +/* + * Initialize the routing table and associated variables. + */ +void +init_routes() +{ + routing_table = NULL; + nroutes = 0; + routes_changed = FALSE; + delay_change_reports = FALSE; +} + + +/* + * Initialize the children and leaf bits for route 'r', along with the + * associated dominant, subordinate, and leaf timing data structures. + * Return TRUE if this changes the value of either the children or + * leaf bitmaps for 'r'. + */ +static int +init_children_and_leaves(r, parent) + register struct rtentry *r; + register vifi_t parent; +{ + register vifi_t vifi; + register struct uvif *v; + vifbitmap_t old_children, old_leaves; + + VIFM_COPY(r->rt_children, old_children); + VIFM_COPY(r->rt_leaves, old_leaves ); + + VIFM_CLRALL(r->rt_children); + VIFM_CLRALL(r->rt_leaves); + r->rt_flags &= ~RTF_LEAF_TIMING; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + r->rt_dominants [vifi] = 0; + r->rt_subordinates[vifi] = 0; + + if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { + VIFM_SET(vifi, r->rt_children); + if (v->uv_neighbors == NULL) { + VIFM_SET(vifi, r->rt_leaves); + r->rt_leaf_timers[vifi] = 0; + } + else { + r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; + r->rt_flags |= RTF_LEAF_TIMING; + } + } + else { + r->rt_leaf_timers[vifi] = 0; + } + } + + return (!VIFM_SAME(r->rt_children, old_children) || + !VIFM_SAME(r->rt_leaves, old_leaves)); +} + + +/* + * A new vif has come up -- update the children and leaf bitmaps in all route + * entries to take that into account. + */ +void +add_vif_to_routes(vifi) + register vifi_t vifi; +{ + register struct rtentry *r; + register struct uvif *v; + + v = &uvifs[vifi]; + for (r = routing_table; r != NULL; r = r->rt_next) { + if (r->rt_metric != UNREACHABLE && + !VIFM_ISSET(vifi, r->rt_children)) { + VIFM_SET(vifi, r->rt_children); + r->rt_dominants [vifi] = 0; + r->rt_subordinates[vifi] = 0; + if (v->uv_neighbors == NULL) { + VIFM_SET(vifi, r->rt_leaves); + r->rt_leaf_timers[vifi] = 0; + } + else { + VIFM_CLR(vifi, r->rt_leaves); + r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; + r->rt_flags |= RTF_LEAF_TIMING; + } + update_table_entry(r); + } + } +} + + +/* + * A vif has gone down -- expire all routes that have that vif as parent, + * and update the children bitmaps in all other route entries to take into + * account the failed vif. + */ +void +delete_vif_from_routes(vifi) + register vifi_t vifi; +{ + register struct rtentry *r; + + for (r = routing_table; r != NULL; r = r->rt_next) { + if (r->rt_metric != UNREACHABLE) { + if (vifi == r->rt_parent) { + del_table_entry(r, 0, DEL_ALL_ROUTES); + r->rt_timer = ROUTE_EXPIRE_TIME; + r->rt_metric = UNREACHABLE; + r->rt_flags |= RTF_CHANGED; + routes_changed = TRUE; + } + else if (VIFM_ISSET(vifi, r->rt_children)) { + VIFM_CLR(vifi, r->rt_children); + VIFM_CLR(vifi, r->rt_leaves); + r->rt_subordinates[vifi] = 0; + r->rt_leaf_timers [vifi] = 0; + update_table_entry(r); + } + else { + r->rt_dominants[vifi] = 0; + } + } + } +} + + +/* + * A neighbor has failed or become unreachable. If that neighbor was + * considered a dominant or subordinate router in any route entries, + * take appropriate action. + */ +void +delete_neighbor_from_routes(addr, vifi) + register u_int32_t addr; + register vifi_t vifi; +{ + register struct rtentry *r; + register struct uvif *v; + + v = &uvifs[vifi]; + for (r = routing_table; r != NULL; r = r->rt_next) { + if (r->rt_metric != UNREACHABLE) { + if (r->rt_dominants[vifi] == addr) { + VIFM_SET(vifi, r->rt_children); + r->rt_dominants [vifi] = 0; + r->rt_subordinates[vifi] = 0; + if (v->uv_neighbors == NULL) { + VIFM_SET(vifi, r->rt_leaves); + r->rt_leaf_timers[vifi] = 0; + } + else { + VIFM_CLR(vifi, r->rt_leaves); + r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; + r->rt_flags |= RTF_LEAF_TIMING; + } + update_table_entry(r); + } + else if (r->rt_subordinates[vifi] == addr) { + r->rt_subordinates[vifi] = 0; + if (v->uv_neighbors == NULL) { + VIFM_SET(vifi, r->rt_leaves); + update_table_entry(r); + } + else { + r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; + r->rt_flags |= RTF_LEAF_TIMING; + } + } + else if (v->uv_neighbors == NULL && + r->rt_leaf_timers[vifi] != 0) { + VIFM_SET(vifi, r->rt_leaves); + r->rt_leaf_timers[vifi] = 0; + update_table_entry(r); + } + } + } +} + + +/* + * Prepare for a sequence of ordered route updates by initializing a pointer + * to the start of the routing table. The pointer is used to remember our + * position in the routing table in order to avoid searching from the + * beginning for each update; this relies on having the route reports in + * a single message be in the same order as the route entries in the routing + * table. + */ +void +start_route_updates() +{ + rtp = RT_ADDR; +} + + +/* + * Starting at the route entry following the one to which 'rtp' points, + * look for a route entry matching the specified origin and mask. If a + * match is found, return TRUE and leave 'rtp' pointing at the found entry. + * If no match is found, return FALSE and leave 'rtp' pointing to the route + * entry preceding the point at which the new origin should be inserted. + * This code is optimized for the normal case in which the first entry to + * be examined is the matching entry. + */ +static int +find_route(origin, mask) + register u_int32_t origin, mask; +{ + register struct rtentry *r; + + r = rtp->rt_next; + while (r != NULL) { + if (origin == r->rt_origin && mask == r->rt_originmask) { + rtp = r; + return (TRUE); + } + if (ntohl(mask) < ntohl(r->rt_originmask) || + (mask == r->rt_originmask && + ntohl(origin) < ntohl(r->rt_origin))) { + rtp = r; + r = r->rt_next; + } + else break; + } + return (FALSE); +} + +/* + * Create a new routing table entry for the specified origin and link it into + * the routing table. The shared variable 'rtp' is assumed to point to the + * routing entry after which the new one should be inserted. It is left + * pointing to the new entry. + * + * Only the origin, originmask, originwidth and flags fields are initialized + * in the new route entry; the caller is responsible for filling in the the + * rest. + */ +static void +create_route(origin, mask) + u_int32_t origin, mask; +{ + register struct rtentry *r; + + if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) + + (2 * numvifs * sizeof(u_int32_t)) + + (numvifs * sizeof(u_long)))) == NULL) { + log(LOG_ERR, 0, "ran out of memory"); /* fatal */ + } + r->rt_origin = origin; + r->rt_originmask = mask; + if (((char *)&mask)[3] != 0) r->rt_originwidth = 4; + else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3; + else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2; + else r->rt_originwidth = 1; + r->rt_flags = 0; + r->rt_dominants = (u_int32_t *)(r + 1); + r->rt_subordinates = (u_int32_t *)(r->rt_dominants + numvifs); + r->rt_leaf_timers = (u_long *)(r->rt_subordinates + numvifs); + r->rt_groups = NULL; + + r->rt_next = rtp->rt_next; + rtp->rt_next = r; + r->rt_prev = rtp; + if (r->rt_next != NULL) + (r->rt_next)->rt_prev = r; + else + rt_end = r; + rtp = r; + ++nroutes; +} + + +/* + * Discard the routing table entry following the one to which 'prev_r' points. + */ +static void +discard_route(prev_r) + register struct rtentry *prev_r; +{ + register struct rtentry *r; + + r = prev_r->rt_next; + prev_r->rt_next = r->rt_next; + if (prev_r->rt_next != NULL) + (prev_r->rt_next)->rt_prev = prev_r; + else + rt_end = prev_r; + free((char *)r); + --nroutes; +} + + +/* + * Process a route report for a single origin, creating or updating the + * corresponding routing table entry if necessary. 'src' is either the + * address of a neighboring router from which the report arrived, or zero + * to indicate a change of status of one of our own interfaces. + */ +void +update_route(origin, mask, metric, src, vifi) + u_int32_t origin, mask; + int metric; + u_int32_t src; + vifi_t vifi; +{ + register struct rtentry *r; + struct rtentry *prev_r; + int adj_metric; + + /* + * Compute an adjusted metric, taking into account the cost of the + * subnet or tunnel over which the report arrived, and normalizing + * all unreachable/poisoned metrics into a single value. + */ + if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) { + log(LOG_WARNING, 0, + "%s reports out-of-range metric %u for origin %s", + inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2)); + return; + } + adj_metric = metric + uvifs[vifi].uv_metric; + if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE; + + /* + * Look up the reported origin in the routing table. + */ + if (!find_route(origin, mask)) { + register struct rtentry *rp; + register struct gtable *gt; + register struct stable *st, **stnp; + + /* + * Not found. + * Don't create a new entry if the report says it's unreachable, + * or if the reported origin and mask are invalid. + */ + if (adj_metric == UNREACHABLE) { + return; + } + if (src != 0 && !inet_valid_subnet(origin, mask)) { + log(LOG_WARNING, 0, + "%s reports an invalid origin (%s) and/or mask (%08x)", + inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask)); + return; + } + + /* + * OK, create the new routing entry. 'rtp' will be left pointing + * to the new entry. + */ + create_route(origin, mask); + + /* + * Now "steal away" any sources that belong under this route + * by deleting any cache entries they might have created + * and allowing the kernel to re-request them. + */ + steal_sources(rtp); + + rtp->rt_metric = UNREACHABLE; /* temporary; updated below */ + } + + /* + * We now have a routing entry for the reported origin. Update it? + */ + r = rtp; + if (r->rt_metric == UNREACHABLE) { + /* + * The routing entry is for a formerly-unreachable or new origin. + * If the report claims reachability, update the entry to use + * the reported route. + */ + if (adj_metric == UNREACHABLE) + return; + + r->rt_parent = vifi; + init_children_and_leaves(r, vifi); + + r->rt_gateway = src; + r->rt_timer = 0; + r->rt_metric = adj_metric; + r->rt_flags |= RTF_CHANGED; + routes_changed = TRUE; + update_table_entry(r); + } + else if (src == r->rt_gateway) { + /* + * The report has come either from the interface directly-connected + * to the origin subnet (src and r->rt_gateway both equal zero) or + * from the gateway we have chosen as the best first-hop gateway back + * towards the origin (src and r->rt_gateway not equal zero). Reset + * the route timer and, if the reported metric has changed, update + * our entry accordingly. + */ + r->rt_timer = 0; + if (adj_metric == r->rt_metric) + return; + + if (adj_metric == UNREACHABLE) { + del_table_entry(r, 0, DEL_ALL_ROUTES); + r->rt_timer = ROUTE_EXPIRE_TIME; + } + else if (adj_metric < r->rt_metric) { + if (init_children_and_leaves(r, vifi)) { + update_table_entry(r); + } + } + r->rt_metric = adj_metric; + r->rt_flags |= RTF_CHANGED; + routes_changed = TRUE; + } + else if (src == 0 || + (r->rt_gateway != 0 && + (adj_metric < r->rt_metric || + (adj_metric == r->rt_metric && + r->rt_timer >= ROUTE_SWITCH_TIME)))) { + /* + * The report is for an origin we consider reachable; the report + * comes either from one of our own interfaces or from a gateway + * other than the one we have chosen as the best first-hop gateway + * back towards the origin. If the source of the update is one of + * our own interfaces, or if the origin is not a directly-connected + * subnet and the reported metric for that origin is better than + * what our routing entry says, update the entry to use the new + * gateway and metric. We also switch gateways if the reported + * metric is the same as the one in the route entry and the gateway + * associated with the route entry has not been heard from recently. + * Did you get all that? + */ + if (r->rt_parent != vifi || adj_metric < r->rt_metric) { + r->rt_parent = vifi; + if (init_children_and_leaves(r, vifi)) { + update_table_entry(r); + } + } + r->rt_gateway = src; + r->rt_timer = 0; + r->rt_metric = adj_metric; + r->rt_flags |= RTF_CHANGED; + routes_changed = TRUE; + } + else if (vifi != r->rt_parent) { + /* + * The report came from a vif other than the route's parent vif. + * Update the children and leaf info, if necessary. + */ + if (VIFM_ISSET(vifi, r->rt_children)) { + /* + * Vif is a child vif for this route. + */ + if (metric < r->rt_metric || + (metric == r->rt_metric && + ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) { + /* + * Neighbor has lower metric to origin (or has same metric + * and lower IP address) -- it becomes the dominant router, + * and vif is no longer a child for me. + */ + VIFM_CLR(vifi, r->rt_children); + VIFM_CLR(vifi, r->rt_leaves); + r->rt_dominants [vifi] = src; + r->rt_subordinates[vifi] = 0; + r->rt_leaf_timers [vifi] = 0; + update_table_entry(r); + } + else if (metric > UNREACHABLE) { /* "poisoned reverse" */ + /* + * Neighbor considers this vif to be on path to route's + * origin; if no subordinate recorded, record this neighbor + * as subordinate and clear the leaf flag. + */ + if (r->rt_subordinates[vifi] == 0) { + VIFM_CLR(vifi, r->rt_leaves); + r->rt_subordinates[vifi] = src; + r->rt_leaf_timers [vifi] = 0; + update_table_entry(r); + } + } + else if (src == r->rt_subordinates[vifi]) { + /* + * Current subordinate no longer considers this vif to be on + * path to route's origin; it is no longer a subordinate + * router, and we set the leaf confirmation timer to give + * us time to hear from other subordinates. + */ + r->rt_subordinates[vifi] = 0; + if (uvifs[vifi].uv_neighbors == NULL || + uvifs[vifi].uv_neighbors->al_next == NULL) { + VIFM_SET(vifi, r->rt_leaves); + update_table_entry(r); + } + else { + r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME; + r->rt_flags |= RTF_LEAF_TIMING; + } + } + + } + else if (src == r->rt_dominants[vifi] && + (metric > r->rt_metric || + (metric == r->rt_metric && + ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) { + /* + * Current dominant no longer has a lower metric to origin + * (or same metric and lower IP address); we adopt the vif + * as our own child. + */ + VIFM_SET(vifi, r->rt_children); + r->rt_dominants [vifi] = 0; + if (metric > UNREACHABLE) { + r->rt_subordinates[vifi] = src; + } + else if (uvifs[vifi].uv_neighbors == NULL || + uvifs[vifi].uv_neighbors->al_next == NULL) { + VIFM_SET(vifi, r->rt_leaves); + } + else { + r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; + r->rt_flags |= RTF_LEAF_TIMING; + } + update_table_entry(r); + } + } +} + + +/* + * On every timer interrupt, advance the timer in each routing entry. + */ +void +age_routes() +{ + register struct rtentry *r; + register struct rtentry *prev_r; + register vifi_t vifi; + + for (prev_r = RT_ADDR, r = routing_table; + r != NULL; + prev_r = r, r = r->rt_next) { + + if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) { + /* + * Route is still good; see if any leaf timers need to be + * advanced. + */ + if (r->rt_flags & RTF_LEAF_TIMING) { + r->rt_flags &= ~RTF_LEAF_TIMING; + for (vifi = 0; vifi < numvifs; ++vifi) { + if (r->rt_leaf_timers[vifi] != 0) { + /* + * Unlike other timers, leaf timers decrement. + */ + if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){ +#ifdef NOTYET + /* If the vif is a physical leaf but has neighbors, + * it is not a tree leaf. If I am a leaf, then no + * interface with neighbors is a tree leaf. */ + if (!(((uvifs[vifi].uv_flags & VIFF_LEAF) || + (vifs_with_neighbors == 1)) && + (uvifs[vifi].uv_neighbors != NULL))) { +#endif + VIFM_SET(vifi, r->rt_leaves); + update_table_entry(r); +#ifdef NOTYET + } +#endif + } + else { + r->rt_flags |= RTF_LEAF_TIMING; + } + } + } + } + } + else if (r->rt_timer >= ROUTE_DISCARD_TIME) { + /* + * Time to garbage-collect the route entry. + */ + del_table_entry(r, 0, DEL_ALL_ROUTES); + discard_route(prev_r); + r = prev_r; + } + else if (r->rt_metric != UNREACHABLE) { + /* + * Time to expire the route entry. If the gateway is zero, + * i.e., it is a route to a directly-connected subnet, just + * set the timer back to zero; such routes expire only when + * the interface to the subnet goes down. + */ + if (r->rt_gateway == 0) { + r->rt_timer = 0; + } + else { + del_table_entry(r, 0, DEL_ALL_ROUTES); + r->rt_metric = UNREACHABLE; + r->rt_flags |= RTF_CHANGED; + routes_changed = TRUE; + } + } + } +} + + +/* + * Mark all routes as unreachable. This function is called only from + * hup() in preparation for informing all neighbors that we are going + * off the air. For consistency, we ought also to delete all reachable + * route entries from the kernel, but since we are about to exit we rely + * on the kernel to do its own cleanup -- no point in making all those + * expensive kernel calls now. + */ +void +expire_all_routes() +{ + register struct rtentry *r; + + for (r = routing_table; r != NULL; r = r->rt_next) { + r->rt_metric = UNREACHABLE; + r->rt_flags |= RTF_CHANGED; + routes_changed = TRUE; + } +} + + +/* + * Delete all the routes in the routing table. + */ +void +free_all_routes() +{ + register struct rtentry *r; + + r = RT_ADDR; + + while (r->rt_next) + discard_route(r); +} + + +/* + * Process an incoming neighbor probe message. + */ +void +accept_probe(src, dst, p, datalen, level) + u_int32_t src; + u_int32_t dst; + char *p; + int datalen; + u_int32_t level; +{ + vifi_t vifi; + + if ((vifi = find_vif(src, dst)) == NO_VIF) { + log(LOG_INFO, 0, + "ignoring probe from non-neighbor %s", inet_fmt(src, s1)); + return; + } + + if (!update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level)) + return; + + report(ALL_ROUTES, vifi, src); +} + +struct newrt { + u_int32_t mask; + u_int32_t origin; + int metric; + int pad; +}; + +int +compare_rts(r1, r2) + register struct newrt *r1; + register struct newrt *r2; +{ + register u_int32_t m1 = ntohl(r1->mask); + register u_int32_t m2 = ntohl(r2->mask); + register u_int32_t o1, o2; + + if (m1 > m2) + return (-1); + if (m1 < m2) + return (1); + + /* masks are equal */ + o1 = ntohl(r1->origin); + o2 = ntohl(r2->origin); + if (o1 > o2) + return (-1); + if (o1 < o2) + return (1); + return (0); +} + +/* + * Process an incoming route report message. + */ +void +accept_report(src, dst, p, datalen, level) + u_int32_t src, dst, level; + register char *p; + register int datalen; +{ + vifi_t vifi; + register int width, i, nrt = 0; + int metric; + u_int32_t mask; + u_int32_t origin; + struct newrt rt[4096]; + + if ((vifi = find_vif(src, dst)) == NO_VIF) { + log(LOG_INFO, 0, + "ignoring route report from non-neighbor %s", inet_fmt(src, s1)); + return; + } + + if (!update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level)) + return; + + if (datalen > 2*4096) { + log(LOG_INFO, 0, + "ignoring oversize (%d bytes) route report from %s", + datalen, inet_fmt(src, s1)); + return; + } + + while (datalen > 0) { /* Loop through per-mask lists. */ + + if (datalen < 3) { + log(LOG_WARNING, 0, + "received truncated route report from %s", + inet_fmt(src, s1)); + return; + } + ((u_char *)&mask)[0] = 0xff; width = 1; + if ((((u_char *)&mask)[1] = *p++) != 0) width = 2; + if ((((u_char *)&mask)[2] = *p++) != 0) width = 3; + if ((((u_char *)&mask)[3] = *p++) != 0) width = 4; + datalen -= 3; + + do { /* Loop through (origin, metric) pairs */ + if (datalen < width + 1) { + log(LOG_WARNING, 0, + "received truncated route report from %s", + inet_fmt(src, s1)); + return; + } + origin = 0; + for (i = 0; i < width; ++i) + ((char *)&origin)[i] = *p++; + metric = *p++; + datalen -= width + 1; + rt[nrt].mask = mask; + rt[nrt].origin = origin; + rt[nrt].metric = (metric & 0x7f); + ++nrt; + } while (!(metric & 0x80)); + } + qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts); + start_route_updates(); + /* + * If the last entry is default, change mask from 0xff000000 to 0 + */ + if (rt[nrt-1].origin == 0) + rt[nrt-1].mask = 0; + + log(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt, + inet_fmt(src, s1), inet_fmt(dst, s2)); + for (i = 0; i < nrt; ++i) + update_route(rt[i].origin, rt[i].mask, rt[i].metric, + src, vifi); + + if (routes_changed && !delay_change_reports) + report_to_all_neighbors(CHANGED_ROUTES); +} + + +/* + * Send a route report message to destination 'dst', via virtual interface + * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. + */ +void +report(which_routes, vifi, dst) + int which_routes; + vifi_t vifi; + u_int32_t dst; +{ + register struct rtentry *r; + register char *p; + register int i; + int datalen; + int width; + u_int32_t mask; + u_int32_t src; + u_int32_t nflags; + + src = uvifs[vifi].uv_lcl_addr; + + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + mask = 0; + +#ifdef NOTYET + /* If I'm not a leaf, but the neighbor is a leaf, only advertise default */ + if ((vifs_with_neighbors != 1) && (uvifs[vifi].uv_flags & VIFF_LEAF)) { + *p++ = 0; /* 0xff000000 mask */ + *p++ = 0; + *p++ = 0; + *p++ = 0; /* class A net 0.0.0.0 == default */ + *p++ = 0x81; /*XXX metric 1, is this safe? */ + datalen += 5; + send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, + htonl(MROUTED_LEVEL), datalen); + return; + } +#endif + + nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS; + + for (r = rt_end; r != RT_ADDR; r = r->rt_prev) { + + if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) + continue; + + /* + * If there is no room for this route in the current message, + * send the message and start a new one. + */ + if (datalen + ((r->rt_originmask == mask) ? + (width + 1) : + (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) { + *(p-1) |= 0x80; + send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, + htonl(MROUTED_LEVEL | nflags), datalen); + + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + mask = 0; + } + + if(r->rt_originmask != mask) { + mask = r->rt_originmask; + width = r->rt_originwidth; + if (datalen != 0) *(p-1) |= 0x80; + *p++ = ((char *)&mask)[1]; + *p++ = ((char *)&mask)[2]; + *p++ = ((char *)&mask)[3]; + datalen += 3; + } + + for (i = 0; i < width; ++i) + *p++ = ((char *)&(r->rt_origin))[i]; + + *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ? + (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */ + (char)(r->rt_metric); + + datalen += width + 1; + } + + if (datalen != 0) { + *(p-1) |= 0x80; + send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, + htonl(MROUTED_LEVEL | nflags), datalen); + } +} + + +/* + * Send a route report message to all neighboring routers. + * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. + */ +void +report_to_all_neighbors(which_routes) + int which_routes; +{ + register vifi_t vifi; + register struct uvif *v; + register struct rtentry *r; + int routes_changed_before; + + /* + * Remember the state of the global routes_changed flag before + * generating the reports, and clear the flag. + */ + routes_changed_before = routes_changed; + routes_changed = FALSE; + + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_neighbors != NULL) { + report(which_routes, vifi, + (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr + : dvmrp_group); + } + } + + /* + * If there were changed routes before we sent the reports AND + * if no new changes occurred while sending the reports, clear + * the change flags in the individual route entries. If changes + * did occur while sending the reports, new reports will be + * generated at the next timer interrupt. + */ + if (routes_changed_before && !routes_changed) { + for (r = routing_table; r != NULL; r = r->rt_next) { + r->rt_flags &= ~RTF_CHANGED; + } + } + + /* + * Set a flag to inhibit further reports of changed routes until the + * next timer interrupt. This is to alleviate update storms. + */ + delay_change_reports = TRUE; +} + +/* + * Send a route report message to destination 'dst', via virtual interface + * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. + */ +int +report_chunk(start_rt, vifi, dst) + register struct rtentry *start_rt; + vifi_t vifi; + u_int32_t dst; +{ + register struct rtentry *r; + register char *p; + register int i; + register int nrt = 0; + int datalen; + int width; + u_int32_t mask; + u_int32_t src; + u_int32_t nflags; + + src = uvifs[vifi].uv_lcl_addr; + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + mask = 0; + + nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS; + + for (r = start_rt; r != RT_ADDR; r = r->rt_prev) { + +#ifdef NOTYET + /* Don't send poisoned routes back to parents if I am a leaf */ + if ((vifs_with_neighbors == 1) && (r->rt_parent == vifi) + && (r->rt_metric > 1)) { + ++nrt; + continue; + } +#endif + + /* + * If there is no room for this route in the current message, + * send it & return how many routes we sent. + */ + if (datalen + ((r->rt_originmask == mask) ? + (width + 1) : + (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) { + *(p-1) |= 0x80; + send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, + htonl(MROUTED_LEVEL | nflags), datalen); + return (nrt); + } + if(r->rt_originmask != mask) { + mask = r->rt_originmask; + width = r->rt_originwidth; + if (datalen != 0) *(p-1) |= 0x80; + *p++ = ((char *)&mask)[1]; + *p++ = ((char *)&mask)[2]; + *p++ = ((char *)&mask)[3]; + datalen += 3; + } + for (i = 0; i < width; ++i) + *p++ = ((char *)&(r->rt_origin))[i]; + + *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ? + (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */ + (char)(r->rt_metric); + ++nrt; + datalen += width + 1; + } + if (datalen != 0) { + *(p-1) |= 0x80; + send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, + htonl(MROUTED_LEVEL | nflags), datalen); + } + return (nrt); +} + +/* + * send the next chunk of our routing table to all neighbors. + * return the length of the smallest chunk we sent out. + */ +int +report_next_chunk() +{ + register vifi_t vifi; + register struct uvif *v; + register struct rtentry *sr; + register int i, n = 0, min = 20000; + static int start_rt; + + if (nroutes <= 0) + return (0); + + /* + * find this round's starting route. + */ + for (sr = rt_end, i = start_rt; --i >= 0; ) { + sr = sr->rt_prev; + if (sr == RT_ADDR) + sr = rt_end; + } + + /* + * send one chunk of routes starting at this round's start to + * all our neighbors. + */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if ((v->uv_neighbors != NULL) +#ifdef NOTYET + && !(v->uv_flags & VIFF_LEAF) +#endif + ) { + n = report_chunk(sr, vifi, + (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr + : dvmrp_group); + if (n < min) + min = n; + } + } + if (min == 20000) + min = 0; /* Neighborless router didn't send any routes */ + + n = min; + log(LOG_INFO, 0, "update %d starting at %d of %d", + n, (nroutes - start_rt), nroutes); + + start_rt = (start_rt + n) % nroutes; + return (n); +} + + +/* + * Print the contents of the routing table on file 'fp'. + */ +void +dump_routes(fp) + FILE *fp; +{ + register struct rtentry *r; + register int i; + register time_t thyme = time(0); + + + fprintf(fp, + "Multicast Routing Table (%u %s)\n%s\n", + nroutes, (nroutes == 1) ? "entry" : "entries", + " Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs"); + + for (r = routing_table; r != NULL; r = r->rt_next) { + + fprintf(fp, " %-18s %-15s ", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2)); + + fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ", + r->rt_metric); + + fprintf(fp, " %3u %3u ", r->rt_timer, r->rt_parent); + + for (i = 0; i < numvifs; ++i) { + if (VIFM_ISSET(i, r->rt_children)) { + fprintf(fp, " %u%c", + i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' '); + } + } + fprintf(fp, "\n"); + } + fprintf(fp, "\n"); +} + +struct rtentry * +determine_route(src) + u_int32_t src; +{ + struct rtentry *rt; + + for (rt = routing_table; rt != NULL; rt = rt->rt_next) { + if (rt->rt_origin == (src & rt->rt_originmask)) + break; + } + return rt; +} diff --git a/usr.sbin/mrouted/route.h b/usr.sbin/mrouted/route.h new file mode 100644 index 00000000000..5bc27b891af --- /dev/null +++ b/usr.sbin/mrouted/route.h @@ -0,0 +1,50 @@ +/* $NetBSD: route.h,v 1.3 1995/10/09 03:51:54 thorpej Exp $ */ + +/* + * 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. + */ + +/* + * Routing Table Entry, one per subnet from which a multicast could originate. + * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.) + * + * The Routing Table is stored as a doubly-linked list of these structures, + * ordered by decreasing value of rt_originmask and, secondarily, by + * decreasing value of rt_origin within each rt_originmask value. + * This data structure is efficient for generating route reports, whether + * full or partial, for processing received full reports, for clearing the + * CHANGED flags, and for periodically advancing the timers in all routes. + * It is not so efficient for updating a small number of routes in response + * to a partial report. In a stable topology, the latter are rare; if they + * turn out to be costing a lot, we can add an auxiliary hash table for + * faster access to arbitrary route entries. + */ +struct rtentry { + struct rtentry *rt_next; /* link to next entry MUST BE FIRST */ + u_int32_t rt_origin; /* subnet origin of multicasts */ + u_int32_t rt_originmask; /* subnet mask for origin */ + short rt_originwidth; /* # bytes of origin subnet number */ + u_char rt_metric; /* cost of route back to origin */ + u_char rt_flags; /* RTF_ flags defined below */ + u_int32_t rt_gateway; /* first-hop gateway back to origin */ + vifi_t rt_parent; /* incoming vif (ie towards origin) */ + vifbitmap_t rt_children; /* outgoing children vifs */ + vifbitmap_t rt_leaves; /* subset of outgoing children vifs */ + u_int32_t *rt_dominants; /* per vif dominant gateways */ + u_int32_t *rt_subordinates; /* per vif subordinate gateways */ + u_long *rt_leaf_timers; /* per vif leaf confirmation timers */ + u_long rt_timer; /* for timing out the route entry */ + struct rtentry *rt_prev; /* link to previous entry */ + struct gtable *rt_groups; /* link to active groups */ +}; + +#define RTF_CHANGED 0x01 /* route changed but not reported */ +#define RTF_LEAF_TIMING 0x02 /* some leaf timers are running */ + +#define ALL_ROUTES 0 /* possible arguments to report() */ +#define CHANGED_ROUTES 1 /* and report_to_all_neighbors() */ diff --git a/usr.sbin/mrouted/rsrr.c b/usr.sbin/mrouted/rsrr.c new file mode 100644 index 00000000000..f5b41955ea4 --- /dev/null +++ b/usr.sbin/mrouted/rsrr.c @@ -0,0 +1,366 @@ +/* $NetBSD: rsrr.c,v 1.2 1995/10/09 03:51:56 thorpej Exp $ */ + +/* + * Copyright (c) 1993 by the University of Southern California + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation in source and binary forms for non-commercial purposes + * and without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both the copyright notice and + * this permission notice appear in supporting documentation. and that + * any documentation, advertising materials, and other materials related + * to such distribution and use acknowledge that the software was + * developed by the University of Southern California, Information + * Sciences Institute. The name of the University may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about + * the suitability of this software for any purpose. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ + +/* RSRR code written by Daniel Zappala, USC Information Sciences Institute, + * April 1995. + */ + +#ifdef RSRR + +#include "defs.h" + +/* Taken from prune.c */ +/* + * checks for scoped multicast addresses + */ +#define GET_SCOPE(gt) { \ + register int _i; \ + if (((gt)->gt_mcastgrp & 0xff000000) == 0xef000000) \ + for (_i = 0; _i < numvifs; _i++) \ + if (scoped_addr(_i, (gt)->gt_mcastgrp)) \ + VIFM_SET(_i, (gt)->gt_scope); \ + } + +/* + * Exported variables. + */ +int rsrr_socket; /* interface to reservation protocol */ + +/* + * Global RSRR variables. + */ +char rsrr_recv_buf[RSRR_MAX_LEN]; /* RSRR receive buffer */ +char rsrr_send_buf[RSRR_MAX_LEN]; /* RSRR send buffer */ + +/* + * Procedure definitions needed internally. + */ +void rsrr_accept(); +void rsrr_send(); +void rsrr_accept_iq(); +void rsrr_accept_rq(); + +/* Initialize RSRR socket */ +void +rsrr_init() +{ + int servlen; + struct sockaddr_un serv_addr; + + if ((rsrr_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) + log(LOG_ERR, errno, "Can't create RSRR socket"); + + unlink(RSRR_SERV_PATH); + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sun_family = AF_UNIX; + strcpy(serv_addr.sun_path, RSRR_SERV_PATH); + servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path); + + if (bind(rsrr_socket, (struct sockaddr *) &serv_addr, servlen) < 0) + log(LOG_ERR, errno, "Can't bind RSRR socket"); + + if (register_input_handler(rsrr_socket,rsrr_read) < 0) + log(LOG_WARNING, 0, "Couldn't register RSRR as an input handler"); +} + +/* Read a message from the RSRR socket */ +void +rsrr_read() +{ + register int rsrr_recvlen; + struct sockaddr_un client_addr; + int client_length = sizeof(client_addr); + register int omask; + + bzero((char *) &client_addr, sizeof(client_addr)); + rsrr_recvlen = recvfrom(rsrr_socket, rsrr_recv_buf, sizeof(rsrr_recv_buf), + 0, &client_addr, &client_length); + if (rsrr_recvlen < 0) { + if (errno != EINTR) + log(LOG_ERR, errno, "RSRR recvfrom"); + return; + } + /* Use of omask taken from main() */ + omask = sigblock(sigmask(SIGALRM)); + rsrr_accept(rsrr_recvlen,&client_addr,client_length); + (void)sigsetmask(omask); +} + +/* Accept a message from the reservation protocol and take + * appropriate action. + */ +void +rsrr_accept(recvlen,client_addr,client_length) + int recvlen; + struct sockaddr_un *client_addr; + int client_length; +{ + struct rsrr_header *rsrr; + struct rsrr_rq *route_query; + + if (recvlen < RSRR_HEADER_LEN) { + log(LOG_WARNING, 0, + "Received RSRR packet of %d bytes, which is less than min size", + recvlen); + return; + } + + rsrr = (struct rsrr_header *) rsrr_recv_buf; + + if (rsrr->version > RSRR_MAX_VERSION) { + log(LOG_WARNING, 0, + "Received RSRR packet version %d, which I don't understand", + rsrr->version); + return; + } + + switch (rsrr->version) { + case 1: + switch (rsrr->type) { + case RSRR_INITIAL_QUERY: + /* Send Initial Reply to client */ + log(LOG_INFO, 0, "Received Initial Query\n"); + rsrr_accept_iq(client_addr,client_length); + break; + case RSRR_ROUTE_QUERY: + /* Check size */ + if (recvlen < RSRR_RQ_LEN) { + log(LOG_WARNING, 0, + "Received Route Query of %d bytes, which is too small", + recvlen); + break; + } + /* Get the query */ + route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN); + log(LOG_INFO, 0, + "Received Route Query for src %s grp %s notification %d", + inet_fmt(route_query->source_addr.s_addr, s1), + inet_fmt(route_query->dest_addr.s_addr,s2), + BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)); + /* Send Route Reply to client */ + rsrr_accept_rq(rsrr,route_query,client_addr,client_length); + break; + default: + log(LOG_WARNING, 0, + "Received RSRR packet type %d, which I don't handle", + rsrr->type); + break; + } + break; + + default: + log(LOG_WARNING, 0, + "Received RSRR packet version %d, which I don't understand", + rsrr->version); + break; + } +} + +/* Send an Initial Reply to the reservation protocol. */ +void +rsrr_accept_iq(client_addr,client_length) + struct sockaddr_un *client_addr; + int client_length; +{ + struct rsrr_header *rsrr; + struct rsrr_vif *vif_list; + struct uvif *v; + int vifi, sendlen; + + /* Check for space. There should be room for plenty of vifs, + * but we should check anyway. + */ + if (numvifs > RSRR_MAX_VIFS) { + log(LOG_WARNING, 0, + "Can't send RSRR Route Reply because %d is too many vifs %d", + numvifs); + return; + } + + /* Set up message */ + rsrr = (struct rsrr_header *) rsrr_send_buf; + rsrr->version = 1; + rsrr->type = RSRR_INITIAL_REPLY; + rsrr->flags = 0; + rsrr->num = numvifs; + + vif_list = (struct rsrr_vif *) (rsrr_send_buf + RSRR_HEADER_LEN); + + /* Include the vif list. */ + for (vifi=0, v = uvifs; vifi < numvifs; vifi++, v++) { + vif_list[vifi].id = vifi; + vif_list[vifi].status = 0; + if (v->uv_flags & VIFF_DISABLED) + BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT); + vif_list[vifi].threshold = v->uv_threshold; + vif_list[vifi].local_addr.s_addr = v->uv_lcl_addr; + } + + /* Get the size. */ + sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN; + + /* Send it. */ + log(LOG_INFO, 0, "Send RSRR Initial Reply"); + rsrr_send(sendlen,client_addr,client_length); +} + +/* Send a Route Reply to the reservation protocol. */ +void +rsrr_accept_rq(rsrr_in,route_query,client_addr,client_length) + struct rsrr_header *rsrr_in; + struct rsrr_rq *route_query; + struct sockaddr_un *client_addr; + int client_length; +{ + struct rsrr_header *rsrr; + struct rsrr_rr *route_reply; + struct gtable *gt,local_g; + struct rtentry *r; + int sendlen,i; + u_long mcastgrp; + + /* Set up message */ + rsrr = (struct rsrr_header *) rsrr_send_buf; + rsrr->version = 1; + rsrr->type = RSRR_ROUTE_REPLY; + rsrr->flags = rsrr_in->flags; + rsrr->num = 0; + + route_reply = (struct rsrr_rr *) (rsrr_send_buf + RSRR_HEADER_LEN); + route_reply->dest_addr.s_addr = route_query->dest_addr.s_addr; + route_reply->source_addr.s_addr = route_query->source_addr.s_addr; + route_reply->query_id = route_query->query_id; + + /* Blank routing entry for error. */ + route_reply->in_vif = 0; + route_reply->reserved = 0; + route_reply->out_vif_bm = 0; + + /* Clear error bit. */ + BIT_CLR(rsrr->flags,RSRR_ERROR_BIT); + /* Turn notification off. We don't do it yet. */ + BIT_CLR(rsrr->flags,RSRR_NOTIFICATION_BIT); + + /* First check kernel. Code taken from add_table_entry() */ + if (find_src_grp(route_query->source_addr.s_addr, 0, + route_query->dest_addr.s_addr)) { + gt = gtp ? gtp->gt_gnext : kernel_table; + + /* Include the routing entry. */ + route_reply->in_vif = gt->gt_route->rt_parent; + route_reply->out_vif_bm = gt->gt_grpmems; + + } else { + /* No kernel entry; use routing table. */ + r = determine_route(route_query->source_addr.s_addr); + + if (r != NULL) { + /* We need to mimic what will happen if a data packet + * is forwarded by multicast routing -- the kernel will + * make an upcall and mrouted will install a route in the kernel. + * Our outgoing vif bitmap should reflect what that table + * will look like. Grab code from add_table_entry(). + * This is gross, but it's probably better to be accurate. + */ + + gt = &local_g; + mcastgrp = route_query->dest_addr.s_addr; + + gt->gt_mcastgrp = mcastgrp; + gt->gt_grpmems = 0; + gt->gt_scope = 0; + gt->gt_route = r; + + /* obtain the multicast group membership list */ + for (i = 0; i < numvifs; i++) { + if (VIFM_ISSET(i, r->rt_children) && + !(VIFM_ISSET(i, r->rt_leaves))) + VIFM_SET(i, gt->gt_grpmems); + + if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp)) + VIFM_SET(i, gt->gt_grpmems); + } + + GET_SCOPE(gt); + gt->gt_grpmems &= ~gt->gt_scope; + + /* Include the routing entry. */ + route_reply->in_vif = gt->gt_route->rt_parent; + route_reply->out_vif_bm = gt->gt_grpmems; + + } else { + /* Set error bit. */ + BIT_SET(rsrr->flags,RSRR_ERROR_BIT); + } + } + + /* Get the size. */ + sendlen = RSRR_RR_LEN; + + log(LOG_INFO, 0, "Send RSRR Route Reply for src %s grp %s ", + inet_fmt(route_reply->source_addr.s_addr,s1), + inet_fmt(route_reply->dest_addr.s_addr,s2)); + log(LOG_INFO, 0, "in vif %d out vif %d\n", + route_reply->in_vif,route_reply->out_vif_bm); + + /* Send it. */ + rsrr_send(sendlen,client_addr,client_length); +} + +/* Send an RSRR message. */ +void +rsrr_send(sendlen,client_addr,client_length) + int sendlen; + struct sockaddr_un *client_addr; + int client_length; +{ + int error; + + /* Send it. */ + error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0, + *client_addr, client_length); + + /* Check for errors. */ + if (error < 0) { + log(LOG_WARNING, errno, "Failed send on RSRR socket"); + return; + } + if (error != sendlen) { + log(LOG_WARNING, 0, + "Sent only %d out of %d bytes on RSRR socket\n", error, sendlen); + return; + } +} + +void +rsrr_clean() +{ + unlink(RSRR_SERV_PATH); +} + +#endif RSRR diff --git a/usr.sbin/mrouted/rsrr.h b/usr.sbin/mrouted/rsrr.h new file mode 100644 index 00000000000..bdbf429714c --- /dev/null +++ b/usr.sbin/mrouted/rsrr.h @@ -0,0 +1,140 @@ +/* $NetBSD: rsrr.h,v 1.2 1995/10/09 03:51:57 thorpej Exp $ */ + +/* + * Copyright (c) 1993 by the University of Southern California + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation in source and binary forms for non-commercial purposes + * and without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both the copyright notice and + * this permission notice appear in supporting documentation. and that + * any documentation, advertising materials, and other materials related + * to such distribution and use acknowledge that the software was + * developed by the University of Southern California, Information + * Sciences Institute. The name of the University may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about + * the suitability of this software for any purpose. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ + +#define RSRR_SERV_PATH "/tmp/.rsrr_svr" +/* Note this needs to be 14 chars for 4.3 BSD compatibility */ +#define RSRR_CLI_PATH "/tmp/.rsrr_cli" + +#define RSRR_MAX_LEN 2048 +#define RSRR_HEADER_LEN (sizeof(struct rsrr_header)) +#define RSRR_RQ_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rq)) +#define RSRR_RR_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rr)) +#define RSRR_VIF_LEN (sizeof(struct rsrr_vif)) + +/* Current maximum number of vifs. */ +#define RSRR_MAX_VIFS 32 + +/* Maximum acceptable version */ +#define RSRR_MAX_VERSION 1 + +/* RSRR message types */ +#define RSRR_ALL_TYPES 0 +#define RSRR_INITIAL_QUERY 1 +#define RSRR_INITIAL_REPLY 2 +#define RSRR_ROUTE_QUERY 3 +#define RSRR_ROUTE_REPLY 4 + +/* RSRR Initial Reply (Vif) Status bits + * Each definition represents the position of the bit from right to left. + * + * Right-most bit is the disabled bit, set if the vif is administratively + * disabled. + */ +#define RSRR_DISABLED_BIT 0 +/* All other bits are zeroes */ + +/* RSRR Route Query/Reply flag bits + * Each definition represents the position of the bit from right to left. + * + * Right-most bit is the Route Change Notification bit, set if the + * reservation protocol wishes to receive notification of + * a route change for the source-destination pair listed in the query. + * Notification is in the form of an unsolicitied Route Reply. + */ +#define RSRR_NOTIFICATION_BIT 0 +/* Next bit indicates an error returning the Route Reply. */ +#define RSRR_ERROR_BIT 1 +/* All other bits are zeroes */ + +/* Definition of an RSRR message header. + * An Initial Query uses only the header, and an Initial Reply uses + * the header and a list of vifs. + */ +struct rsrr_header { + u_char version; /* RSRR Version, currently 1 */ + u_char type; /* type of message, as defined above */ + u_char flags; /* flags; defined by type */ + u_char num; /* number; defined by type */ +}; + +/* Definition of a vif as seen by the reservation protocol. + * + * Routing gives the reservation protocol a list of vifs in the + * Initial Reply. + * + * We explicitly list the ID because we can't assume that all routing + * protocols will use the same numbering scheme. + * + * The status is a bitmask of status flags, as defined above. It is the + * responsibility of the reservation protocol to perform any status checks + * if it uses the MULTICAST_VIF socket option. + * + * The threshold indicates the ttl an outgoing packet needs in order to + * be forwarded. The reservation protocol must perform this check itself if + * it uses the MULTICAST_VIF socket option. + * + * The local address is the address of the physical interface over which + * packets are sent. + */ +struct rsrr_vif { + u_char id; /* vif id */ + u_char threshold; /* vif threshold ttl */ + u_short status; /* vif status bitmask */ + struct in_addr local_addr; /* vif local address */ +}; + +/* Definition of an RSRR Route Query. + * + * The query asks routing for the forwarding entry for a particular + * source and destination. The query ID uniquely identifies the query + * for the reservation protocol. Thus, the combination of the client's + * address and the query ID forms a unique identifier for routing. + * Flags are defined above. + */ +struct rsrr_rq { + struct in_addr dest_addr; /* destination */ + struct in_addr source_addr; /* source */ + u_long query_id; /* query ID */ +}; + +/* Definition of an RSRR Route Reply. + * + * Routing uses the reply to give the reservation protocol the + * forwarding entry for a source-destination pair. Routing copies the + * query ID from the query and fills in the incoming vif and a bitmask + * of the outgoing vifs. + * Flags are defined above. + */ +struct rsrr_rr { + struct in_addr dest_addr; /* destination */ + struct in_addr source_addr; /* source */ + u_long query_id; /* query ID */ + u_short in_vif; /* incoming vif */ + u_short reserved; /* reserved */ + u_long out_vif_bm; /* outgoing vif bitmask */ +}; diff --git a/usr.sbin/mrouted/snmp.c b/usr.sbin/mrouted/snmp.c new file mode 100644 index 00000000000..5298da5dbc8 --- /dev/null +++ b/usr.sbin/mrouted/snmp.c @@ -0,0 +1,1307 @@ +/* $NetBSD: snmp.c,v 1.2 1995/10/09 03:51:58 thorpej Exp $ */ + +/* + * snmp.c + * + * Code written by David Thaler <thalerd@eecs.umich.edu> + * Moved to a seperate file by Bill Fenner <fenner@parc.xerox.com> + */ + + +#include "defs.h" +#include <string.h> +#include "snmp.h" + +#define NUMMIBS 2 +#define SNMPD_RETRY_INTERVAL 300 /* periodic snmpd probe interval */ + +char *mibs[]={ "ipMRouteMIB", "dvmrpMIB" }; + +extern int o_ipMRouteTable(); +extern int o_ipMRouteNextHopTable(); +extern int o_dvmrpRouteTable(); +extern int o_dvmrpRouteNextHopTable(); + + int smux_fd = NOTOK; + int rock_and_roll = 0; + int dont_bother_anymore = 0; + int quantum = 0; +static OID subtree[NUMMIBS] = NULLOID; +static struct smuxEntry *se = NULL; +extern int smux_errno; +extern char smux_info[BUFSIZ]; + +/* + * Place an IP address into an OID starting at element n + */ +void +put_address(oid, addr, n) + OID oid; + u_long addr; + int n; +{ + int i; + + for (i=n+3; i>=n+0; i--) { + oid->oid_elements[i] = addr & 0xFF; + addr >>= 8; + } +} + +/* Get an IP address from an OID starting at element n */ +int +get_address(oid, addr, n) + OID oid; + u_long *addr; + int n; +{ + int i; + int ok = 1; + + (*addr) = 0; + + if (oid -> oid_nelem < n+4) + return 0; + + for (i=n; i<n+4; i++) { + (*addr) <<= 8; + if (i >= oid->oid_nelem) + ok = 0; + else + (*addr) |= oid->oid_elements[i]; + } + + return ok; +} + +/* + * Attempt to start up SMUX protocol + */ +void +try_smux_init() +{ + if (smux_fd != NOTOK || dont_bother_anymore) + return; + if ((smux_fd = smux_init(debug)) == NOTOK) { + log(LOG_WARNING, 0,"smux_init: %s [%s]", smux_error(smux_errno), + smux_info); + } else + rock_and_roll = 0; +} + +/* + * Implements scalar objects from both MIBs + */ +static int +o_scalar(oi, v, offset) + OI oi; + register struct type_SNMP_VarBind *v; + int offset; +{ + int ifvar; + register OID oid = oi -> oi_name; + register OT ot = oi -> oi_type; + + ifvar = (int) ot -> ot_info; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (oid -> oid_nelem != + ot -> ot_name -> oid_nelem + 1 + || oid -> oid_elements[oid -> oid_nelem - 1] + != 0) + return int_SNMP_error__status_noSuchName; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + if (oid -> oid_nelem + == ot -> ot_name -> oid_nelem) { + OID new; + + if ((new = oid_extend (oid, 1)) == NULLOID) + return int_SNMP_error__status_genErr; + new -> oid_elements[new -> oid_nelem - 1] = 0; + + if (v -> name) + free_SNMP_ObjectName (v -> name); + v -> name = new; + } + else + return NOTOK; + break; + + default: + return int_SNMP_error__status_genErr; + } + + switch (ifvar) { + case ipMRouteEnable: + return o_integer (oi, v, 1); + + case dvmrpVersion: { + static char buff[15]; + + sprintf(buff, "mrouted%d.%d", PROTOCOL_VERSION, MROUTED_VERSION); + return o_string (oi, v, buff, strlen (buff)); + } + + case dvmrpGenerationId: + return o_integer (oi, v, dvmrp_genid); + + default: + return int_SNMP_error__status_noSuchName; + } +} + +/* + * Find if a specific scoped boundary exists on a Vif + */ +struct vif_acl * +find_boundary(vifi, addr, mask) + int vifi; + int addr; + int mask; +{ + struct vif_acl *n; + + for (n = uvifs[vifi].uv_acl; n != NULL; n = n->acl_next) { + if (addr == n->acl_addr && mask==n->acl_mask) + return n; + } + return NULL; +} + +/* + * Find the next scoped boundary in order after a given spec + */ +struct vif_acl * +next_boundary(vifi, addr, mask) + int *vifi; + int addr; + int mask; +{ + struct vif_acl *bestn, *n; + int i; + + for (i = *vifi; i < numvifs; i++) { + bestn = NULL; + for (n = uvifs[i].uv_acl; n; n=n->acl_next) { + if ((i > *vifi || n->acl_addr > addr + || (n->acl_addr==addr && n->acl_mask>mask)) + && (!bestn || n->acl_addr < bestn->acl_addr + || (n->acl_addr==bestn->acl_addr && n->acl_mask<bestn->acl_mask))) + bestn = n; + } + if (bestn) { + *vifi = i; + return bestn; + } + } + return NULL; +} + +/* + * Implements the Boundary Table portion of the DVMRP MIB + */ +static int +o_dvmrpBoundaryTable (oi, v, offset) +OI oi; +register struct type_SNMP_VarBind *v; +{ + int ifvar, vifi, + addr, mask; + register OID oid = oi -> oi_name; + register OT ot = oi -> oi_type; + struct vif_acl *bound; + + ifvar = (int) ot -> ot_info; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (oid->oid_nelem != ot->ot_name->oid_nelem + 9) + return int_SNMP_error__status_noSuchName; + + if ((vifi = oid -> oid_elements[ot-> ot_name->oid_nelem]) >= numvifs) + return int_SNMP_error__status_noSuchName; + + if (!get_address(oid, &addr, ot->ot_name->oid_nelem+1) + || !get_address(oid, &mask, ot->ot_name->oid_nelem+5)) + return int_SNMP_error__status_noSuchName; + + if (!(bound = find_boundary(vifi, addr, mask))) + return int_SNMP_error__status_noSuchName; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + if (oid->oid_nelem < ot->ot_name->oid_nelem + 9) { + OID new; + + if (oid->oid_nelem == ot->ot_name->oid_nelem) { + vifi = addr = mask = 0; + } else { + vifi = oid->oid_elements[ot->ot_name->oid_nelem]; + get_address(oid, &addr, ot->ot_name->oid_nelem+1); + get_address(oid, &mask, ot->ot_name->oid_nelem+5); + } + + bound = next_boundary(&vifi,addr,mask); + if (!bound) + return NOTOK; + + new = oid_extend (oid, ot->ot_name->oid_nelem+9-oid->oid_nelem); + if (new == NULLOID) + return NOTOK; + new -> oid_elements[ot->ot_name->oid_nelem] = vifi; + put_address(new, bound->acl_addr, ot->ot_name->oid_nelem+1); + put_address(new, bound->acl_mask, ot->ot_name->oid_nelem+5); + + if (v -> name) + free_SNMP_ObjectName (v -> name); + v -> name = new; + } else { /* get next entry given previous */ + int i = ot -> ot_name -> oid_nelem; + + vifi = oid->oid_elements[i]; + get_address(oid, &addr, ot->ot_name->oid_nelem+1); + get_address(oid, &mask, ot->ot_name->oid_nelem+5); + if (!(bound = next_boundary(&vifi,addr,mask+1))) + return NOTOK; + + put_address(oid, bound->acl_addr, ot->ot_name->oid_nelem+1); + put_address(oid, bound->acl_mask, ot->ot_name->oid_nelem+5); + oid->oid_elements[i] = vifi; + oid->oid_nelem = i + 9; + } + break; + + default: + return int_SNMP_error__status_genErr; + } + + switch (ifvar) { + + case dvmrpBoundaryVifIndex: + return o_integer (oi, v, vifi); + + default: + return int_SNMP_error__status_noSuchName; + } +} + +/* + * Given a vif index and address, return the next greater neighbor entry + */ +struct listaddr * +next_neighbor(vifi, addr) + int *vifi; + int addr; +{ + struct listaddr *bestn, *n; + int i; + + for (i = *vifi; i < numvifs; i++) { + bestn = NULL; + for (n = uvifs[i].uv_neighbors; n; n=n->al_next) { + if ((i > *vifi || n->al_addr > addr) + && (!bestn || n->al_addr < bestn->al_addr)) + bestn = n; + } + if (bestn) { + *vifi = i; + return bestn; + } + } + return NULL; +} + +/* + * Find a neighbor, if it exists off a given Vif + */ +struct listaddr * +find_neighbor(vifi, addr) + int vifi; + int addr; +{ + struct listaddr *n; + + for (n = uvifs[vifi].uv_neighbors; n != NULL; n = n->al_next) { + if (addr == n->al_addr) + return n; + } + return NULL; +} + +/* + * Implements the Neighbor Table portion of the DVMRP MIB + */ +static int +o_dvmrpNeighborTable (oi, v, offset) +OI oi; +register struct type_SNMP_VarBind *v; +{ + int ifvar, vifi, + addr; + register OID oid = oi -> oi_name; + register OT ot = oi -> oi_type; + struct listaddr *neighbor; + + ifvar = (int) ot -> ot_info; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (oid->oid_nelem != ot->ot_name->oid_nelem + 5) + return int_SNMP_error__status_noSuchName; + + if ((vifi = oid -> oid_elements[ot-> ot_name->oid_nelem]) >= numvifs) + return int_SNMP_error__status_noSuchName; + + if (!get_address(oid, &addr, ot->ot_name->oid_nelem+1)) + return int_SNMP_error__status_noSuchName; + + if (!(neighbor = find_neighbor(vifi, addr))) + return int_SNMP_error__status_noSuchName; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + if (oid->oid_nelem < ot->ot_name->oid_nelem + 5) { + OID new; + + if (oid->oid_nelem == ot->ot_name->oid_nelem) { + vifi = addr = 0; + } else { + vifi = oid->oid_elements[ot->ot_name->oid_nelem]; + get_address(oid, &addr, ot->ot_name->oid_nelem+1); + } + + neighbor = next_neighbor(&vifi,addr); /* Get first entry */ + if (!neighbor) + return NOTOK; + + new = oid_extend (oid, ot->ot_name->oid_nelem+5-oid->oid_nelem); + if (new == NULLOID) + return NOTOK; + new -> oid_elements[ot->ot_name->oid_nelem] = vifi; + put_address(new, neighbor->al_addr, ot->ot_name->oid_nelem+1); + + if (v -> name) + free_SNMP_ObjectName (v -> name); + v -> name = new; + + } else { /* get next entry given previous */ + int i = ot -> ot_name -> oid_nelem; + + vifi = oid->oid_elements[i]; + get_address(oid, &addr, ot->ot_name->oid_nelem+1); + if (!(neighbor = next_neighbor(&vifi,addr+1))) + return NOTOK; + + put_address(oid, neighbor->al_addr, ot->ot_name->oid_nelem+1); + oid->oid_elements[i] = vifi; + oid->oid_nelem = i + 5; + } + break; + + default: + return int_SNMP_error__status_genErr; + } + + switch (ifvar) { + + case dvmrpNeighborUpTime: { + time_t currtime; + time(&currtime); + return o_integer (oi, v, (currtime - neighbor->al_ctime)*100); + } + + case dvmrpNeighborExpiryTime: + return o_integer (oi, v, (NEIGHBOR_EXPIRE_TIME-neighbor->al_timer) * 100); + + case dvmrpNeighborVersion: { + static char buff[15]; + + sprintf(buff, "%d.%d", neighbor->al_pv, neighbor->al_mv); + return o_string (oi, v, buff, strlen (buff)); + } + + case dvmrpNeighborGenerationId: + return o_integer (oi, v, neighbor->al_genid); + + default: + return int_SNMP_error__status_noSuchName; + } +} + +/* + * Given a virtual interface number, make sure we have the current + * kernel information for that Vif. + */ +refresh_vif(v_req, ifnum) + struct sioc_vif_req *v_req; + int ifnum; +{ + static int lastq = -1; + + if (quantum!=lastq || v_req->vifi != ifnum) { + lastq = quantum; + v_req->vifi = ifnum; + if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)v_req) < 0) + v_req->icount = v_req->ocount = v_req->ibytes = v_req->obytes = 0; + } +} + +/* + * Implements the Multicast Routing Interface Table portion of the Multicast MIB + */ +static int +o_ipMRouteInterfaceTable (oi, v, offset) +OI oi; +register struct type_SNMP_VarBind *v; +int offset; +{ + int ifnum, + ifvar; + register OID oid = oi -> oi_name; + register OT ot = oi -> oi_type; +static struct sioc_vif_req v_req; + + ifvar = (int) ot -> ot_info; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (oid -> oid_nelem != ot -> ot_name -> oid_nelem + 1) + return int_SNMP_error__status_noSuchName; + if ((ifnum = oid -> oid_elements[oid -> oid_nelem - 1]) >= numvifs) + return int_SNMP_error__status_noSuchName; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) { + OID new; + + ifnum = 0; + + if ((new = oid_extend (oid, 1)) == NULLOID) + return NOTOK; + new -> oid_elements[new -> oid_nelem - 1] = ifnum; + + if (v -> name) + free_SNMP_ObjectName (v -> name); + v -> name = new; + + } else { + int i = ot -> ot_name -> oid_nelem; + + if ((ifnum = oid -> oid_elements[i] + 1) >= numvifs) + return NOTOK; + + oid -> oid_elements[i] = ifnum; + oid -> oid_nelem = i + 1; + } + break; + + default: + return int_SNMP_error__status_genErr; + } + + switch (ifvar) { + case ipMRouteInterfaceTtl: + return o_integer (oi, v, uvifs[ifnum].uv_threshold); + + case dvmrpVInterfaceType: + if (uvifs[ifnum].uv_flags & VIFF_SRCRT) + return o_integer (oi, v, 2); + else if (uvifs[ifnum].uv_flags & VIFF_TUNNEL) + return o_integer (oi, v, 1); + else if (uvifs[ifnum].uv_flags & VIFF_QUERIER) + return o_integer (oi, v, 3); + else /* SUBNET */ + return o_integer (oi, v, 4); + + case dvmrpVInterfaceState: + if (uvifs[ifnum].uv_flags & VIFF_DISABLED) + return o_integer (oi, v, 3); + else if (uvifs[ifnum].uv_flags & VIFF_DOWN) + return o_integer (oi, v, 2); + else /* UP */ + return o_integer (oi, v, 1); + + case dvmrpVInterfaceLocalAddress: { + struct sockaddr_in tmp; + tmp.sin_addr.s_addr = uvifs[ifnum].uv_lcl_addr; + return o_ipaddr (oi, v, &tmp); + } + + case dvmrpVInterfaceRemoteAddress: { + struct sockaddr_in tmp; + tmp.sin_addr.s_addr = (uvifs[ifnum].uv_flags & VIFF_TUNNEL) ? + uvifs[ifnum].uv_rmt_addr : + uvifs[ifnum].uv_subnet; + return o_ipaddr (oi, v, &tmp); + } + + case dvmrpVInterfaceRemoteSubnetMask: { + struct sockaddr_in tmp; + tmp.sin_addr.s_addr = uvifs[ifnum].uv_subnetmask; + return o_ipaddr (oi, v, &tmp); + } + + case dvmrpVInterfaceMetric: + return o_integer (oi, v, uvifs[ifnum].uv_metric); + + case dvmrpVInterfaceRateLimit: + return o_integer (oi, v, uvifs[ifnum].uv_rate_limit); + + case dvmrpVInterfaceInPkts: + refresh_vif(&v_req, ifnum); + return o_integer(oi, v, v_req.icount); + + case dvmrpVInterfaceOutPkts: + refresh_vif(&v_req, ifnum); + return o_integer(oi, v, v_req.ocount); + + case dvmrpVInterfaceInOctets: + refresh_vif(&v_req, ifnum); + return o_integer(oi, v, v_req.ibytes); + + case dvmrpVInterfaceOutOctets: + refresh_vif(&v_req, ifnum); + return o_integer(oi, v, v_req.obytes); + + default: + return int_SNMP_error__status_noSuchName; + } +} + +struct mib_variable { + char *name; /* MIB variable name */ + int (*function)(); /* Function to call */ + int info; /* Which variable */ +} mib_vars[] = { + "ipMRouteEnable", o_scalar, ipMRouteEnable, + "ipMRouteUpstreamNeighbor", o_ipMRouteTable, ipMRouteUpstreamNeighbor, + "ipMRouteInIfIndex", o_ipMRouteTable, ipMRouteInIfIndex, + "ipMRouteUpTime", o_ipMRouteTable, ipMRouteUpTime, + "ipMRouteExpiryTime", o_ipMRouteTable, ipMRouteExpiryTime, + "ipMRoutePkts", o_ipMRouteTable, ipMRoutePkts, + "ipMRouteDifferentInIfIndexes", o_ipMRouteTable, ipMRouteDifferentInIfIndexes, + "ipMRouteOctets", o_ipMRouteTable, ipMRouteOctets, + "ipMRouteProtocol", o_ipMRouteTable, ipMRouteProtocol, + "ipMRouteNextHopState", o_ipMRouteNextHopTable, ipMRouteNextHopState, + "ipMRouteNextHopUpTime", o_ipMRouteNextHopTable, ipMRouteNextHopUpTime, + "ipMRouteNextHopExpiryTime", o_ipMRouteNextHopTable, ipMRouteNextHopExpiryTime, + "ipMRouteNextHopClosestMemberHops", o_ipMRouteNextHopTable, ipMRouteNextHopClosestMemberHops, + "ipMRouteNextHopProtocol", o_ipMRouteNextHopTable, ipMRouteNextHopProtocol, + "ipMRouteInterfaceTtl", o_ipMRouteInterfaceTable, ipMRouteInterfaceTtl, + "dvmrpVersion", o_scalar, dvmrpVersion, + "dvmrpGenerationId", o_scalar, dvmrpGenerationId, + "dvmrpVInterfaceType", o_ipMRouteInterfaceTable, dvmrpVInterfaceType, + "dvmrpVInterfaceState", o_ipMRouteInterfaceTable, dvmrpVInterfaceState, + "dvmrpVInterfaceLocalAddress", o_ipMRouteInterfaceTable, dvmrpVInterfaceLocalAddress, + "dvmrpVInterfaceRemoteAddress", o_ipMRouteInterfaceTable, dvmrpVInterfaceRemoteAddress, + "dvmrpVInterfaceRemoteSubnetMask", o_ipMRouteInterfaceTable, dvmrpVInterfaceRemoteSubnetMask, + "dvmrpVInterfaceMetric", o_ipMRouteInterfaceTable, dvmrpVInterfaceMetric, + "dvmrpVInterfaceRateLimit", o_ipMRouteInterfaceTable, dvmrpVInterfaceRateLimit, + "dvmrpVInterfaceInPkts", o_ipMRouteInterfaceTable, dvmrpVInterfaceInPkts, + "dvmrpVInterfaceOutPkts", o_ipMRouteInterfaceTable, dvmrpVInterfaceOutPkts, + "dvmrpVInterfaceInOctets", o_ipMRouteInterfaceTable, dvmrpVInterfaceInOctets, + "dvmrpVInterfaceOutOctets", o_ipMRouteInterfaceTable, dvmrpVInterfaceOutOctets, + "dvmrpNeighborUpTime", o_dvmrpNeighborTable, dvmrpNeighborUpTime, + "dvmrpNeighborExpiryTime", o_dvmrpNeighborTable, dvmrpNeighborExpiryTime, + "dvmrpNeighborVersion", o_dvmrpNeighborTable, dvmrpNeighborVersion, + "dvmrpNeighborGenerationId",o_dvmrpNeighborTable, dvmrpNeighborGenerationId, + "dvmrpRouteUpstreamNeighbor", o_dvmrpRouteTable, dvmrpRouteUpstreamNeighbor, + "dvmrpRouteInVifIndex", o_dvmrpRouteTable, dvmrpRouteInVifIndex, + "dvmrpRouteMetric", o_dvmrpRouteTable, dvmrpRouteMetric, + "dvmrpRouteExpiryTime", o_dvmrpRouteTable, dvmrpRouteExpiryTime, + "dvmrpRouteNextHopType", o_dvmrpRouteNextHopTable, dvmrpRouteNextHopType, + "dvmrpBoundaryVifIndex", o_dvmrpBoundaryTable, dvmrpBoundaryVifIndex, + 0, 0, 0 +}; + +/* + * Register variables as part of the MIBs + */ +void +init_mib() +{ + register OT ot; + int i; + + for (i=0; mib_vars[i].name; i++) + if (ot=text2obj(mib_vars[i].name)) { + ot->ot_getfnx = mib_vars[i].function; + ot->ot_info = (caddr_t)mib_vars[i].info; + } +} + +/* + * Initialize the SNMP part of mrouted + */ +void +snmp_init() +{ + OT ot; + int i; + + if (readobjects("mrouted.defs") == NOTOK) + log(LOG_ERR, 0, "readobjects: %s", PY_pepy); + for (i=0; i < NUMMIBS; i++) { + if ((ot = text2obj(mibs[i])) == NULL) + log(LOG_ERR, 0, "object \"%s\" not in \"%s\"", + mibs[i], "mrouted.defs"); + subtree[i] = ot -> ot_name; + } + init_mib(); + try_smux_init(); +} + +/* + * Process an SNMP "get" or "get-next" request + */ +static +get_smux (pdu, offset) + register struct type_SNMP_GetRequest__PDU *pdu; + int offset; +{ + int idx, + status; + object_instance ois; + register struct type_SNMP_VarBindList *vp; + IFP method; + + quantum = pdu -> request__id; + idx = 0; + for (vp = pdu -> variable__bindings; vp; vp = vp -> next) { + register OI oi; + register OT ot; + register struct type_SNMP_VarBind *v = vp -> VarBind; + + idx++; + + if (offset == type_SNMP_SMUX__PDUs_get__next__request) { + if ((oi = name2inst (v -> name)) == NULLOI + && (oi = next2inst (v -> name)) == NULLOI) + goto no_name; + + if ((ot = oi -> oi_type) -> ot_getfnx == NULLIFP) + goto get_next; + } + else + if ((oi = name2inst (v -> name)) == NULLOI + || (ot = oi -> oi_type) -> ot_getfnx + == NULLIFP) { +no_name: ; + pdu -> error__status = + int_SNMP_error__status_noSuchName; + goto out; + } + +try_again: ; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (!(method = ot -> ot_getfnx)) + goto no_name; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + if (!(method = ot -> ot_getfnx)) + goto get_next; + break; + + case type_SNMP_SMUX__PDUs_set__request: + if (!(method = ot -> ot_setfnx)) + goto no_name; + break; + + default: + goto no_name; + } + + switch (status = (*ot -> ot_getfnx) (oi, v, offset)) { + case NOTOK: /* get-next wants a bump */ +get_next: ; + oi = &ois; + for (;;) { + if ((ot = ot -> ot_next) == NULLOT) { + pdu -> error__status = + int_SNMP_error__status_noSuchName; + goto out; + } + oi -> oi_name = + (oi -> oi_type = ot) -> ot_name; + if (ot -> ot_getfnx) + goto try_again; + } + + case int_SNMP_error__status_noError: + break; + + default: + pdu -> error__status = status; + goto out; + } + } + idx = 0; + +out: ; + pdu -> error__index = idx; + + if (smux_response (pdu) == NOTOK) { + log(LOG_WARNING,0,"smux_response: %s [%s]", + smux_error (smux_errno), smux_info); + smux_fd = NOTOK; + } +} + +/* + * Handle SNMP "set" request by replying that it is illegal + */ +static +set_smux(event) + struct type_SNMP_SMUX__PDUs *event; +{ + switch (event -> offset) { + case type_SNMP_SMUX__PDUs_set__request: + { + register struct type_SNMP_GetResponse__PDU *pdu = + event -> un.get__response; + + pdu -> error__status = int_SNMP_error__status_noSuchName; + pdu -> error__index = pdu -> variable__bindings ? 1 : 0; + + if (smux_response (pdu) == NOTOK) { + log(LOG_WARNING, 0, + "smux_response: %s [%s]", + smux_error (smux_errno), + smux_info); + smux_fd = NOTOK; + } + } + break; + + case type_SNMP_SMUX__PDUs_commitOrRollback: + { + struct type_SNMP_SOutPDU *cor = + event -> un.commitOrRollback; + + if (cor -> parm == int_SNMP_SOutPDU_commit) { + /* "should not happen" */ + (void) smux_close (protocolError); + smux_fd = NOTOK; + } + } + break; + } +} + +/* + * Handle an incoming SNMP message + */ +void +doit_smux() +{ + struct type_SNMP_SMUX__PDUs *event; + + if (smux_wait(&event, NOTOK)==NOTOK) { + if (smux_errno==inProgress) + return; + log(LOG_WARNING, 0, "smux_wait: %s [%s]", smux_error(smux_errno), + smux_info); + smux_fd = NOTOK; + return; + } + + switch (event -> offset) { + case type_SNMP_SMUX__PDUs_registerResponse: + { + struct type_SNMP_RRspPDU *rsp = + event -> un.registerResponse; + + if (rsp -> parm == int_SNMP_RRspPDU_failure) { + log(LOG_WARNING,0,"SMUX registration of subtree failed"); + dont_bother_anymore = 1; + (void) smux_close (goingDown); + break; + } + } + if (smux_trap(NULLOID, int_SNMP_generic__trap_coldStart, 0, + (struct type_SNMP_VarBindList *)0) == NOTOK) { + log(LOG_WARNING,0,"smux_trap: %s [%s]", smux_error (smux_errno), + smux_info); + break; + } + return; + + case type_SNMP_SMUX__PDUs_get__request: + case type_SNMP_SMUX__PDUs_get__next__request: + get_smux (event -> un.get__request, event -> offset); + return; + + case type_SNMP_SMUX__PDUs_close: + log(LOG_WARNING, 0, "SMUX close: %s", + smux_error (event -> un.close -> parm)); + break; + + case type_SNMP_SMUX__PDUs_set__request: + case type_SNMP_SMUX__PDUs_commitOrRollback: + set_smux (event); + return; + + default: + log(LOG_WARNING,0,"bad SMUX operation: %d", event -> offset); + (void) smux_close (protocolError); + break; + } + smux_fd = NOTOK; +} + +/* + * Inform snmpd that we are here and handling our MIBs + */ +void +start_smux() +{ + int i; + + for (i=0; i<NUMMIBS; i++) { + if ((se = getsmuxEntrybyname (mibs[i])) == NULL) { + log(LOG_WARNING,0,"no SMUX entry for \"%s\"", mibs[i]); + return; + } + + /* Only open a new connection the first time through */ + if (!i) { + if (smux_simple_open(&se->se_identity, mibs[i], + se->se_password, strlen(se->se_password))==NOTOK) { + if (smux_errno == inProgress) + return; + + log(LOG_WARNING, 0,"smux_simple_open: %s [%s]", + smux_error(smux_errno), smux_info); + smux_fd = NOTOK; + return; + } + log(LOG_NOTICE,0, "SMUX open: %s \"%s\"", + oid2ode (&se->se_identity), se->se_name); + rock_and_roll = 1; + } + + if (smux_register(subtree[i], -1, readWrite)==NOTOK) { + log(LOG_WARNING, 0,"smux_register: %s [%s]", smux_error(smux_errno), + smux_info); + smux_fd = NOTOK; + return; + } + } + log(LOG_NOTICE, 0, "SMUX registered"); +} + +/* + * Implements the DVMRP Route Table portion of the DVMRP MIB + */ +int +o_dvmrpRouteTable (oi, v, offset) +OI oi; +register struct type_SNMP_VarBind *v; +int offset; +{ + u_long src, mask; + int ifvar; + register OID oid = oi -> oi_name; + register OT ot = oi -> oi_type; + struct rtentry *rt = NULL; + + ifvar = (int) ot -> ot_info; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (!get_address(oid, &src, ot->ot_name->oid_nelem) + || !get_address(oid, &mask, ot->ot_name->oid_nelem+4) + || !(rt = snmp_find_route(src,mask))) + return int_SNMP_error__status_noSuchName; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + + /* Check if we're requesting the first row */ + if (oid->oid_nelem < ot->ot_name->oid_nelem+8) { + OID new; + + /* Get partial specification (if any) */ + get_address(oid, &src, ot->ot_name->oid_nelem); + get_address(oid, &mask, ot->ot_name->oid_nelem+4); + + if (!next_route(&rt,src,mask)) /* Get first entry */ + return NOTOK; + + /* Extend by 8 more ints to hold index columns */ + new = oid_extend (oid, ot->ot_name->oid_nelem+8-oid->oid_nelem); + if (new == NULLOID) + return NOTOK; + + put_address(new, rt->rt_origin, ot->ot_name->oid_nelem); + put_address(new, rt->rt_originmask, ot->ot_name->oid_nelem+4); + + if (v -> name) + free_SNMP_ObjectName (v -> name); + v -> name = new; + + /* Else we start from a previous row */ + } else { + int i = ot -> ot_name -> oid_nelem; + + /* Get the lowest entry in the table > the given grp/src/mask */ + get_address(oid, &src, ot->ot_name->oid_nelem); + get_address(oid, &mask, ot->ot_name->oid_nelem+4); + if (!next_route(&rt, src,mask)) + return NOTOK; + + put_address(oid, rt->rt_origin, ot->ot_name->oid_nelem); + put_address(oid, rt->rt_originmask, ot->ot_name->oid_nelem+4); + } + break; + + default: + return int_SNMP_error__status_genErr; + } + + switch (ifvar) { + case dvmrpRouteUpstreamNeighbor: { + struct sockaddr_in tmp; + tmp.sin_addr.s_addr = rt->rt_gateway; + return o_ipaddr (oi, v, &tmp); + } + + case dvmrpRouteInVifIndex: + return o_integer (oi, v, rt->rt_parent); + + case dvmrpRouteMetric: + return o_integer (oi, v, rt->rt_metric); + + case dvmrpRouteExpiryTime: + return o_integer (oi, v, rt->rt_timer*100); + + default: + return int_SNMP_error__status_noSuchName; + } +} + +/* + * Implements the DVMRP Routing Next Hop Table portion of the DVMRP MIB + */ +int +o_dvmrpRouteNextHopTable (oi, v, offset) +OI oi; +register struct type_SNMP_VarBind *v; +int offset; +{ + u_long src, mask; + vifi_t vifi; + int ifvar; + register OID oid = oi -> oi_name; + register OT ot = oi -> oi_type; + struct rtentry *rt = NULL; + + ifvar = (int) ot -> ot_info; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (oid->oid_nelem != ot->ot_name->oid_nelem+9) + return int_SNMP_error__status_noSuchName; + + if (!get_address(oid, &src, ot->ot_name->oid_nelem) + || !get_address(oid, &mask, ot->ot_name->oid_nelem+4) + || (!(rt=snmp_find_route(src,mask)))) + return int_SNMP_error__status_noSuchName; + + vifi = oid->oid_elements[ot->ot_name->oid_nelem+8]; + if (!(VIFM_ISSET(vifi, rt->rt_children))) + return int_SNMP_error__status_noSuchName; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + + /* Check if we're requesting the first row */ + if (oid->oid_nelem < ot->ot_name->oid_nelem+9) { + OID new; + + get_address(oid, &src, ot->ot_name->oid_nelem); + get_address(oid, &mask, ot->ot_name->oid_nelem+4); + + /* Find first child vif */ + vifi=0; + if (!next_route_child(&rt, src, mask, &vifi)) + return NOTOK; + + /* Extend by 9 more ints to hold index columns */ + new = oid_extend (oid, ot->ot_name->oid_nelem+9-oid->oid_nelem); + if (new == NULLOID) + return NOTOK; + + put_address(new, rt->rt_origin, ot->ot_name->oid_nelem); + put_address(new, rt->rt_originmask, ot->ot_name->oid_nelem+4); + new->oid_elements[ot->ot_name->oid_nelem+8] = vifi; + + if (v -> name) + free_SNMP_ObjectName (v -> name); + v -> name = new; + + /* Else we start from a previous row */ + } else { + int i = ot -> ot_name -> oid_nelem; + + /* Get the lowest entry in the table > the given grp/src/mask */ + vifi = oid->oid_elements[oid->oid_nelem-1] + 1; + if (!get_address(oid, &src, ot->ot_name->oid_nelem) + || !get_address(oid, &mask, ot->ot_name->oid_nelem+4) + || !next_route_child(&rt, src, mask, &vifi)) + return NOTOK; + + put_address(oid, rt->rt_origin, ot->ot_name->oid_nelem); + put_address(oid, rt->rt_originmask, ot->ot_name->oid_nelem+4); + oid->oid_elements[ot->ot_name->oid_nelem+8] = vifi; + } + break; + + default: + return int_SNMP_error__status_genErr; + } + + switch (ifvar) { + + case dvmrpRouteNextHopType: + return o_integer (oi, v, (VIFM_ISSET(vifi, rt->rt_leaves))? 1 : 2); + + default: + return int_SNMP_error__status_noSuchName; + } +} + +/* + * Implements the IP Multicast Route Table portion of the Multicast MIB + */ +int +o_ipMRouteTable (oi, v, offset) +OI oi; +register struct type_SNMP_VarBind *v; +int offset; +{ + u_long src, grp, mask; + int ifvar; + register OID oid = oi -> oi_name; + register OT ot = oi -> oi_type; + struct gtable *gt = NULL; + struct stable *st = NULL; +static struct sioc_sg_req sg_req; + + ifvar = (int) ot -> ot_info; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (!get_address(oid, &grp, ot->ot_name->oid_nelem) + || !get_address(oid, &src, ot->ot_name->oid_nelem+4) + || !get_address(oid, &mask, ot->ot_name->oid_nelem+8) + || (mask != 0xFFFFFFFF) /* we keep sources now, not subnets */ + || !(gt = find_grp(grp)) + || !(st = find_grp_src(gt,src))) + return int_SNMP_error__status_noSuchName; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + + /* Check if we're requesting the first row */ + if (oid->oid_nelem < ot->ot_name->oid_nelem+12) { + OID new; + + /* Get partial specification (if any) */ + get_address(oid, &grp, ot->ot_name->oid_nelem); + get_address(oid, &src, ot->ot_name->oid_nelem+4); + get_address(oid, &mask, ot->ot_name->oid_nelem+8); + + if (!next_grp_src_mask(>,&st,grp,src,mask)) /* Get first entry */ + return NOTOK; + + /* Extend by 12 more ints to hold index columns */ + new = oid_extend (oid, ot->ot_name->oid_nelem+12-oid->oid_nelem); + if (new == NULLOID) + return NOTOK; + + put_address(new, gt->gt_mcastgrp, ot->ot_name->oid_nelem); + put_address(new, st->st_origin, ot->ot_name->oid_nelem+4); + put_address(new, 0xFFFFFFFF, ot->ot_name->oid_nelem+8); + + if (v -> name) + free_SNMP_ObjectName (v -> name); + v -> name = new; + + /* Else we start from a previous row */ + } else { + int i = ot -> ot_name -> oid_nelem; + + /* Get the lowest entry in the table > the given grp/src/mask */ + get_address(oid, &grp, ot->ot_name->oid_nelem); + get_address(oid, &src, ot->ot_name->oid_nelem+4); + get_address(oid, &mask, ot->ot_name->oid_nelem+8); + if (!next_grp_src_mask(>, &st, grp,src,mask)) + return NOTOK; + + put_address(oid, gt->gt_mcastgrp, ot->ot_name->oid_nelem); + put_address(oid, st->st_origin, ot->ot_name->oid_nelem+4); + put_address(oid, 0xFFFFFFFF, ot->ot_name->oid_nelem+8); + } + break; + + default: + return int_SNMP_error__status_genErr; + } + + switch (ifvar) { + case ipMRouteUpstreamNeighbor: { + struct sockaddr_in tmp; + tmp.sin_addr.s_addr = gt->gt_route->rt_gateway; + return o_ipaddr (oi, v, &tmp); + } + + case ipMRouteInIfIndex: + return o_integer (oi, v, gt->gt_route->rt_parent); + + case ipMRouteUpTime: { + time_t currtime; + time(&currtime); + return o_integer (oi, v, (currtime - gt->gt_ctime)*100); + } + + case ipMRouteExpiryTime: + return o_integer (oi, v, gt->gt_timer*100); + + case ipMRoutePkts: + refresh_sg(&sg_req, gt, st); + return o_integer (oi, v, sg_req.pktcnt); + + case ipMRouteOctets: + refresh_sg(&sg_req, gt, st); + return o_integer (oi, v, sg_req.bytecnt); + + case ipMRouteDifferentInIfIndexes: + refresh_sg(&sg_req, gt, st); + return o_integer (oi, v, sg_req.wrong_if); + + case ipMRouteProtocol: + return o_integer (oi, v, 4); + + default: + return int_SNMP_error__status_noSuchName; + } +} + +/* + * Implements the IP Multicast Routing Next Hop Table portion of the Multicast + * MIB + */ +int +o_ipMRouteNextHopTable (oi, v, offset) +OI oi; +register struct type_SNMP_VarBind *v; +int offset; +{ + u_long src, grp, mask, addr; + vifi_t vifi; + int ifvar; + register OID oid = oi -> oi_name; + register OT ot = oi -> oi_type; + struct gtable *gt; + struct stable *st; + + ifvar = (int) ot -> ot_info; + switch (offset) { + case type_SNMP_SMUX__PDUs_get__request: + if (oid->oid_nelem != ot->ot_name->oid_nelem+17) + return int_SNMP_error__status_noSuchName; + + if (!get_address(oid, &grp, ot->ot_name->oid_nelem) + || !get_address(oid, &src, ot->ot_name->oid_nelem+4) + || !get_address(oid, &mask, ot->ot_name->oid_nelem+8) + || !get_address(oid, &addr, ot->ot_name->oid_nelem+13) + || grp!=addr + || mask!=0xFFFFFFFF + || (!(gt=find_grp(grp))) + || (!(st=find_grp_src(gt,src)))) + return int_SNMP_error__status_noSuchName; + + vifi = oid->oid_elements[ot->ot_name->oid_nelem+12]; + if (!(VIFM_ISSET(vifi, gt->gt_route->rt_children))) + return int_SNMP_error__status_noSuchName; + break; + + case type_SNMP_SMUX__PDUs_get__next__request: + + /* Check if we're requesting the first row */ + if (oid->oid_nelem < ot->ot_name->oid_nelem+17) { + OID new; + + get_address(oid, &grp, ot->ot_name->oid_nelem); + get_address(oid, &src, ot->ot_name->oid_nelem+4); + get_address(oid, &mask, ot->ot_name->oid_nelem+8); + + /* Find first child vif */ + vifi=0; + if (!next_child(>, &st, grp, src, mask, &vifi)) + return NOTOK; + + /* Extend by 17 more ints to hold index columns */ + new = oid_extend (oid, ot->ot_name->oid_nelem+17-oid->oid_nelem); + if (new == NULLOID) + return NOTOK; + + put_address(new, gt->gt_mcastgrp, ot->ot_name->oid_nelem); + put_address(new, st->st_origin, ot->ot_name->oid_nelem+4); + put_address(new, 0xFFFFFFFF, ot->ot_name->oid_nelem+8); + new->oid_elements[ot->ot_name->oid_nelem+12] = vifi; + put_address(new, gt->gt_mcastgrp, ot->ot_name->oid_nelem+13); + + if (v -> name) + free_SNMP_ObjectName (v -> name); + v -> name = new; + + /* Else we start from a previous row */ + } else { + int i = ot -> ot_name -> oid_nelem; + + /* Get the lowest entry in the table > the given grp/src/mask */ + vifi = oid->oid_elements[oid->oid_nelem-1] + 1; + if (!get_address(oid, &grp, ot->ot_name->oid_nelem) + || !get_address(oid, &src, ot->ot_name->oid_nelem+4) + || !get_address(oid, &mask, ot->ot_name->oid_nelem+8) + || !next_child(>, &st, grp, src, mask, &vifi)) + return NOTOK; + + put_address(oid, gt->gt_mcastgrp, ot->ot_name->oid_nelem); + put_address(oid, st->st_origin, ot->ot_name->oid_nelem+4); + put_address(oid, 0xFFFFFFFF, ot->ot_name->oid_nelem+8); + oid->oid_elements[ot->ot_name->oid_nelem+12] = vifi; + put_address(oid, gt->gt_mcastgrp, ot->ot_name->oid_nelem+13); + } + break; + + default: + return int_SNMP_error__status_genErr; + } + + switch (ifvar) { + + case ipMRouteNextHopState: + return o_integer (oi, v, (VIFM_ISSET(vifi, gt->gt_grpmems))? 2 : 1); + + /* Currently equal to ipMRouteUpTime */ + case ipMRouteNextHopUpTime: { + time_t currtime; + time(&currtime); + return o_integer (oi, v, (currtime - gt->gt_ctime)*100); + } + + case ipMRouteNextHopExpiryTime: + return o_integer (oi, v, gt->gt_prsent_timer); + + case ipMRouteNextHopClosestMemberHops: + return o_integer (oi, v, 0); + + case ipMRouteNextHopProtocol: + return o_integer (oi, v, 4); + + default: + return int_SNMP_error__status_noSuchName; + } +} diff --git a/usr.sbin/mrouted/snmp.h b/usr.sbin/mrouted/snmp.h new file mode 100644 index 00000000000..c78c050d6b3 --- /dev/null +++ b/usr.sbin/mrouted/snmp.h @@ -0,0 +1,505 @@ +/* $NetBSD: snmp.h,v 1.2 1995/10/09 03:52:00 thorpej Exp $ */ + +/* + * This file contains excepts from ISODE include files, and is + * subject to the following notice: + * + * The ISODE is not proprietary, but it is not in the public domain. This was + * necessary to include a "hold harmless" clause in the release. The upshot + * of all this is that anyone can get a copy of the release and do anything + * they want with it, but no one takes any responsibility whatsoever for any + * (mis)use. + */ + +typedef u_char PElementClass; +typedef u_char PElementForm; +typedef u_short PElementID; /* 0..16383 are meaningful (14 bits) */ +typedef int PElementLen; +typedef u_char *PElementData; +typedef int (*IFP) (); +#define INTDEF long +typedef INTDEF integer; +#undef IP +typedef int *IP; +#define NULLIP ((IP) 0) +#define NULLIFP ((IFP) 0) +#define NULLFD ((fd_set *) 0) +#define NULLCP ((char *) 0) +#define NULLVP ((char **) 0) + +#ifndef SFD +#if !defined(SVR3) && !defined(SUNOS4) && !defined(BSD44) && !defined(ultrix) +#define SFD int +#define SFP IFP +#else +#define SFD void +#define SFP VFP +#endif +#endif + +typedef struct { + int pe_type; /* Type of entry */ + integer pe_ucode; /* index to user's code if any */ + int pe_tag; /* Tag of this entry if any */ + int pe_flags; /* Flags */ +} tpe; + +typedef struct { + int pe_type; /* Type of entry */ + integer pe_ucode; /* index to user's code if any */ + int pe_tag; /* Tag of this entry if any */ + int pe_flags; /* Flags */ + char **pe_typename; /* User defined name of variable */ +} ptpe; + +typedef struct { + char *md_name; /* Name of this module */ + int md_nentries; /* Number of entries */ + tpe **md_etab; /* Pointer to encoding tables */ + tpe **md_dtab; /* Pointer to decoding tables */ + ptpe **md_ptab; /* Pointer to printing tables */ + int (*md_eucode)(); /* User code for encoding */ + int (*md_ducode)(); /* User code for decoding */ + int (*md_pucode)(); /* User code for printing */ + caddr_t *md_ptrtab; /* pointer table */ +} modtyp; + +#define type_SNMP_ObjectSyntax PElement +typedef struct PElement { + int pe_errno; /* Error codes */ + int pe_context; /* indirect reference */ + PElementClass pe_class; +#define PE_CLASS_UNIV 0x0 /* Universal */ + PElementForm pe_form; +#define PE_FORM_PRIM 0x0 /* PRIMitive */ + PElementID pe_id; /* should be extensible, 14 bits for now */ +#define PE_PRIM_NULL 0x005 /* Null */ + PElementLen pe_len; + PElementLen pe_ilen; + union { + PElementData un_pe_prim; /* PRIMitive value */ + struct PElement *un_pe_cons; /* CONStructor head */ + } pe_un1; + union { + int un_pe_cardinal; /* cardinality of list */ + int un_pe_nbits; /* number of bits in string */ + } pe_un2; + int pe_inline; /* for "ultra-efficient" PElements */ + char *pe_realbase; /* .. */ + int pe_offset; /* offset of element in sequence */ + struct PElement *pe_next; + int pe_refcnt; /* hack for ANYs in pepy */ +} *PE; +#define NULLPE ((PE) 0) + +typedef struct OIDentifier { + int oid_nelem; /* number of sub-identifiers */ + + unsigned int *oid_elements; /* the (ordered) list of sub-identifiers */ +} OIDentifier, *OID; +#define NULLOID ((OID) 0) +#define type_SNMP_ObjectName OIDentifier + +typedef struct object_syntax { + char *os_name; /* syntax name */ + IFP os_encode; /* data -> PE */ + IFP os_decode; /* PE -> data */ + IFP os_free; /* free data */ + IFP os_parse; /* str -> data */ + IFP os_print; /* data -> tty */ + char **os_data1; /* for moresyntax() in snmpi... */ + int os_data2; /* .. */ +} *OS; + +typedef struct object_type { + char *ot_text; /* OBJECT DESCRIPTOR */ + char *ot_id; /* OBJECT IDENTIFIER */ + OID ot_name; /* .. */ + OS ot_syntax; /* SYNTAX */ + int ot_access; /* ACCESS */ + u_long ot_views; /* for views */ + int ot_status; /* STATUS */ + caddr_t ot_info; /* object information */ + IFP ot_getfnx; /* get/get-next method */ + IFP ot_setfnx; /* set method */ + caddr_t ot_save; /* for set method */ + caddr_t ot_smux; /* for SMUX */ + struct object_type *ot_chain; /* hash-bucket for text2obj */ + struct object_type *ot_sibling; /* linked-list for name2obj */ + struct object_type *ot_children; /* .. */ + struct object_type *ot_next; /* linked-list for get-next */ +} *OT; +#define NULLOT ((OT) 0) + +typedef struct object_instance { + OID oi_name; /* instance OID */ + OT oi_type; /* prototype */ +} object_instance, *OI; +#define NULLOI ((OI) 0) + +struct type_SNMP_VarBind { + struct type_SNMP_ObjectName *name; + struct type_SNMP_ObjectSyntax *value; +}; + +struct type_SNMP_VarBindList { + struct type_SNMP_VarBind *VarBind; + struct type_SNMP_VarBindList *next; +}; + +#define type_SNMP_GetRequest__PDU type_SNMP_PDU +#define type_SNMP_GetResponse__PDU type_SNMP_PDU +struct type_SNMP_PDU { + integer request__id; + integer error__status; +#define int_SNMP_error__status_noError 0 +#define int_SNMP_error__status_noSuchName 2 +#define int_SNMP_error__status_genErr 5 + integer error__index; + struct type_SNMP_VarBindList *variable__bindings; +}; + +struct type_SNMP_PDUs { + int offset; +#define type_SNMP_PDUs_get__request 1 +#define type_SNMP_PDUs_get__next__request 2 +#define type_SNMP_PDUs_get__response 3 +#define type_SNMP_PDUs_set__request 4 + union { + struct type_SNMP_GetRequest__PDU *get__request; + struct type_SNMP_GetNextRequest__PDU *get__next__request; + struct type_SNMP_GetResponse__PDU *get__response; + struct type_SNMP_SetRequest__PDU *set__request; + struct type_SNMP_Trap__PDU *trap; + } un; +}; + +struct type_SNMP_Message { + integer version; +#define int_SNMP_version_version__1 0 + struct qbuf *community; + struct type_SNMP_PDUs *data; +}; + +struct type_SNMP_SMUX__PDUs { + int offset; +#define type_SNMP_SMUX__PDUs_close 2 +#define type_SNMP_SMUX__PDUs_registerResponse 4 +#define type_SNMP_SMUX__PDUs_get__request 5 +#define type_SNMP_SMUX__PDUs_get__next__request 6 +#define type_SNMP_SMUX__PDUs_set__request 8 +#define type_SNMP_SMUX__PDUs_commitOrRollback 10 + union { + struct type_SNMP_SimpleOpen *simple; + struct type_SNMP_ClosePDU *close; + struct type_SNMP_RReqPDU *registerRequest; + struct type_SNMP_RRspPDU *registerResponse; + struct type_SNMP_GetRequest__PDU *get__request; + struct type_SNMP_GetNextRequest__PDU *get__next__request; + struct type_SNMP_GetResponse__PDU *get__response; + struct type_SNMP_SetRequest__PDU *set__request; + struct type_SNMP_Trap__PDU *trap; + struct type_SNMP_SOutPDU *commitOrRollback; + } un; +}; + +struct type_SNMP_RReqPDU { + struct type_SNMP_ObjectName *subtree; + integer priority; + integer operation; +#define int_SNMP_operation_readWrite 2 +}; + +struct type_SNMP_ClosePDU { + integer parm; +#define int_SNMP_ClosePDU_goingDown 0 +#define int_SNMP_ClosePDU_protocolError 3 +}; + +struct type_SNMP_RRspPDU { + integer parm; +#define int_SNMP_RRspPDU_failure -1 +}; + +struct type_SNMP_SOutPDU { + integer parm; +#define int_SNMP_SOutPDU_commit 0 +}; + +struct type_SNMP_Trap__PDU { + OID enterprise; + struct type_SNMP_NetworkAddress *agent__addr; + integer generic__trap; +#define int_SNMP_generic__trap_coldStart 0 + integer specific__trap; + struct type_SNMP_TimeTicks *time__stamp; + struct type_SNMP_VarBindList *variable__bindings; +}; + +struct smuxEntry { + char *se_name; + OIDentifier se_identity; + char *se_password; + int se_priority; +}; + +typedef struct { + int ps_errno; /* Error codes */ +#define PS_ERR_NONE 0 /* No error */ +#define PS_ERR_OVERID 1 /* Overflow in ID */ +#define PS_ERR_OVERLEN 2 /* Overflow in length */ +#define PS_ERR_NMEM 3 /* Out of memory */ +#define PS_ERR_EOF 4 /* End of file */ +#define PS_ERR_EOFID 5 /* End of file reading extended ID */ +#define PS_ERR_EOFLEN 6 /* End of file reading extended length */ +#define PS_ERR_LEN 7 /* Length mismatch */ +#define PS_ERR_TRNC 8 /* Truncated */ +#define PS_ERR_INDF 9 /* Indefinite length in primitive form */ +#define PS_ERR_IO 10 /* I/O error */ +#define PS_ERR_EXTRA 11 /* Extraneous octets */ +#define PS_ERR_XXX 12 /* XXX */ + union { + caddr_t un_ps_addr; + struct { + char *st_ps_base; + int st_ps_cnt; + char *st_ps_ptr; + int st_ps_bufsiz; + } un_ps_st; + struct { + struct udvec *uv_ps_head; + struct udvec *uv_ps_cur; + struct udvec *uv_ps_end; + int uv_ps_elems; + int uv_ps_slop; + int uv_ps_cc; + } un_ps_uv; + } ps_un; +#define ps_addr ps_un.un_ps_addr +#define ps_base ps_un.un_ps_st.st_ps_base +#define ps_cnt ps_un.un_ps_st.st_ps_cnt +#define ps_ptr ps_un.un_ps_st.st_ps_ptr +#define ps_bufsiz ps_un.un_ps_st.st_ps_bufsiz +#define ps_head ps_un.un_ps_uv.uv_ps_head +#define ps_cur ps_un.un_ps_uv.uv_ps_cur +#define ps_end ps_un.un_ps_uv.uv_ps_end +#define ps_elems ps_un.un_ps_uv.uv_ps_elems +#define ps_slop ps_un.un_ps_uv.uv_ps_slop +#define ps_cc ps_un.un_ps_uv.uv_ps_cc + caddr_t ps_extra; /* for George's recursive PStreams */ + int ps_inline; /* for "ultra-efficient" PStreams */ + int ps_scratch; /* XXX */ + int ps_byteno; /* byte position */ + IFP ps_primeP; + IFP ps_readP; + IFP ps_writeP; + IFP ps_flushP; + IFP ps_closeP; +} PStream, *PS; +#define NULLPS ((PS) 0) + +struct NSAPaddr { /* this structure shouldn't have holes in it */ + long na_stack; /* TS-stack */ +#define NA_TCP 1 /* RFC1006/TCP */ + long na_community; /* internal community # */ + union { + struct na_nsap { /* real network service */ +#define NASIZE 64 /* 20 ought to do it */ + char na_nsap_address[NASIZE]; + char na_nsap_addrlen; + } un_na_nsap; + struct na_tcp { /* emulation via RFC1006 */ +#define NSAP_DOMAINLEN 63 + char na_tcp_domain[NSAP_DOMAINLEN + 1]; + u_short na_tcp_port; /* non-standard TCP port */ + u_short na_tcp_tset; /* transport set */ +#define NA_TSET_TCP 0x0001 /* .. TCP */ +#define NA_TSET_UDP 0x0002 /* .. UDP */ + } un_na_tcp; + struct na_x25 { /* X.25 (assume single subnet) */ +#define NSAP_DTELEN 36 + char na_x25_dte[NSAP_DTELEN + 1]; /* Numeric DTE + Link */ + char na_x25_dtelen; /* number of digits used */ + +/* Conventionally, the PID sits at the first head bytes of user data and so + * should probably not be mentioned specially. A macro might do it, if + * necessary. + */ +#define NPSIZE 4 + char na_x25_pid[NPSIZE]; /* X.25 protocol id */ + char na_x25_pidlen; /* .. */ +#define CUDFSIZE 16 + char na_x25_cudf[CUDFSIZE];/* call user data field */ + char na_x25_cudflen; /* .. */ +/* + * X25 Facilities field. + */ +#define FACSIZE 6 + char na_x25_fac[FACSIZE]; /* X.25 facilities */ + char na_x25_faclen; /* .. */ + } un_na_x25; + } na_un; +#define na_address na_un.un_na_nsap.na_nsap_address +#define na_addrlen na_un.un_na_nsap.na_nsap_addrlen +#define na_domain na_un.un_na_tcp.na_tcp_domain +#define na_port na_un.un_na_tcp.na_tcp_port +#define na_tset na_un.un_na_tcp.na_tcp_tset +#define na_dte na_un.un_na_x25.na_x25_dte +#define na_dtelen na_un.un_na_x25.na_x25_dtelen +#define na_pid na_un.un_na_x25.na_x25_pid +#define na_pidlen na_un.un_na_x25.na_x25_pidlen +#define na_cudf na_un.un_na_x25.na_x25_cudf +#define na_cudflen na_un.un_na_x25.na_x25_cudflen +#define na_fac na_un.un_na_x25.na_x25_fac +#define na_faclen na_un.un_na_x25.na_x25_faclen +/* for backwards compatibility... these two will be removed after ISODE 7.0 */ +#define na_type na_stack +#define na_subnet na_community +}; + +struct TSAPaddr { +#define NTADDR 8 /* according to NIST OIW */ + struct NSAPaddr ta_addrs[NTADDR]; /* choice of network addresses */ + int ta_naddr; +#define TSSIZE 64 + int ta_selectlen; + union un_ta_type { /* TSAP selector */ + char ta_un_selector[TSSIZE]; + u_short ta_un_port; + } un_ta; +#define ta_selector un_ta.ta_un_selector +#define ta_port un_ta.ta_un_port +}; + +struct qbuf { + struct qbuf *qb_forw; /* doubly-linked list */ + struct qbuf *qb_back; /* .. */ + int qb_len; /* length of data */ + char *qb_data; /* current pointer into data */ + char qb_base[1]; /* extensible... */ +}; + +#define start_udp_client start_udp_server +#define read_udp_socket read_dgram_socket +#define write_udp_socket write_dgram_socket +#define close_udp_socket close_dgram_socket +#define check_udp_socket check_dgram_socket +#define free_SNMP_ObjectName oid_free +#define o_ipaddr(oi,v,value) o_specific ((oi), (v), (caddr_t) (value)) +#define o_integer(oi,v,value) o_longword ((oi), (v), (integer) (value)) +#define oid2ode(i) oid2ode_aux ((i), 1) +#define ps2pe(ps) ps2pe_aux ((ps), 1, 1) +#define pe2ps(ps, pe) pe2ps_aux ((ps), (pe), 1) +#define str2vec(s,v) str2vecX ((s), (v), 0, NULLIP, NULL, 1) +#define free_SNMP_Message(parm)\ + (void) fre_obj((char *) parm, _ZSNMP_mod.md_dtab[_ZMessageSNMP], &_ZSNMP_mod, 1) +#define encode_SNMP_Message(pe, top, len, buffer, parm) \ + enc_f(_ZMessageSNMP, &_ZSNMP_mod, pe, top, len, buffer, (char *) parm) +#define print_SNMP_Message(pe, top, len, buffer, parm) \ + prnt_f(_ZMessageSNMP, &_ZSNMP_mod, pe, top, len, buffer) +#define decode_SNMP_Message(pe, top, len, buffer, parm) \ + dec_f(_ZMessageSNMP, &_ZSNMP_mod, pe, top, len, buffer, (char **) parm) +#define inaddr_copy(hp,sin) \ + bcopy ((hp) -> h_addr, (char *) &((sin) -> sin_addr), (hp) -> h_length) +#define join_udp_server(fd,sock) \ + join_dgram_aux ((fd), (struct sockaddr *) (sock), 0) + +#define MAXDGRAM 8192 +#define NOTOK (-1) +#define OK 0 +#define NVEC 100 +#define invalidOperation (-1) +#define parameterMissing (-2) +#define systemError (-3) +#define youLoseBig (-4) +#define congestion (-5) +#define inProgress (-6) +#define protocolError int_SNMP_ClosePDU_protocolError +#define goingDown int_SNMP_ClosePDU_goingDown +#define readWrite int_SNMP_operation_readWrite + +OID oid_extend(), text2oid (), oid_cpy (); +OT text2obj (); +OI name2inst (), next2inst (), text2inst (); +OS text2syn (); +PS ps_alloc (); +PE pe_alloc (), ps2pe_aux (); +struct smuxEntry *getsmuxEntrybyname (); +struct hostent *gethostbystring (); +char *getlocalhost (), *oid2ode_aux (); +struct TSAPaddr *str2taddr (); /* string encoding to TSAPaddr */ +int dg_open (), read_dgram_socket (), write_dgram_socket (); +int check_dgram_socket (), pe2ps_aux (); +struct qbuf *str2qb (); + +integer request__id; +extern char PY_pepy[]; +extern int quantum; +extern int ts_comm_tcp_default, ps_len_strategy; +extern modtyp _ZSNMP_mod; +#define _ZMessageSNMP 0 + +#define PS_LEN_LONG 2 + +/* Scalars */ +#define ipMRouteEnable 0 + +/* IP Multicast Route Table */ +#define ipMRouteUpstreamNeighbor 0 +#define ipMRouteInIfIndex 1 +#define ipMRouteUpTime 2 +#define ipMRouteExpiryTime 3 +#define ipMRoutePkts 4 +#define ipMRouteDifferentInIfIndexes 5 +#define ipMRouteOctets 6 +#define ipMRouteProtocol 7 + +/* IP Multicast Routing Next Hop Table */ +#define ipMRouteNextHopState 0 +#define ipMRouteNextHopUpTime 1 +#define ipMRouteNextHopExpiryTime 2 +#define ipMRouteNextHopClosestMemberHops 3 +#define ipMRouteNextHopProtocol 4 + +/* Multicast Routing Interface Table */ +#define ipMRouteInterfaceTtl 0 + +/* Scalars (cont.) */ +#define dvmrpVersion 1 +#define dvmrpGenerationId 2 + +/* DVMRP Virtual Interface Table */ +#define dvmrpVInterfaceType 1 +#define dvmrpVInterfaceState 2 +#define dvmrpVInterfaceLocalAddress 3 +#define dvmrpVInterfaceRemoteAddress 4 +#define dvmrpVInterfaceRemoteSubnetMask 5 +#define dvmrpVInterfaceMetric 6 +#define dvmrpVInterfaceRateLimit 7 +#define dvmrpVInterfaceInPkts 8 +#define dvmrpVInterfaceOutPkts 9 +#define dvmrpVInterfaceInOctets 10 +#define dvmrpVInterfaceOutOctets 11 + +/* DVMRP Neighbor Table */ +#define dvmrpNeighborUpTime 0 +#define dvmrpNeighborExpiryTime 1 +#define dvmrpNeighborVersion 2 +#define dvmrpNeighborGenerationId 3 + +/* DVMRP Route Table */ +#define dvmrpRouteUpstreamNeighbor 0 +#define dvmrpRouteInVifIndex 1 +#define dvmrpRouteMetric 2 +#define dvmrpRouteExpiryTime 3 + +/* DVMRP Routing Next Hop Table */ +#define dvmrpRouteNextHopType 0 + +/* Boundary Table */ +#define dvmrpBoundaryVifIndex 0 + +#define SNMPD_RETRY_INTERVAL 300 /* periodic snmpd probe interval */ +extern int smux_fd; +extern int rock_and_roll; +extern int dont_bother_anymore; diff --git a/usr.sbin/mrouted/vif.c b/usr.sbin/mrouted/vif.c new file mode 100644 index 00000000000..406e8157858 --- /dev/null +++ b/usr.sbin/mrouted/vif.c @@ -0,0 +1,1373 @@ +/* $NetBSD: vif.c,v 1.5 1995/10/09 03:52:01 thorpej Exp $ */ + +/* + * 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. + */ + + +#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. */ +int vifs_with_neighbors; /* == 1 if I am a leaf */ + +/* + * 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; + extern char *configfilename; + + 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"); + log(LOG_INFO,0,"Getting vifs from kernel interfaces"); + config_vifs_from_kernel(); + log(LOG_INFO,0,"Getting vifs from %s",configfilename); + 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. + */ + log(LOG_INFO,0,"Installing vifs in kernel..."); + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (!(v->uv_flags & VIFF_DISABLED)) { + if (!(v->uv_flags & VIFF_DOWN)) { + if (v->uv_flags & VIFF_TUNNEL) + log(LOG_INFO,0,"vif #%d, tunnel %s -> %s", vifi, + inet_fmt(v->uv_lcl_addr,s1), + inet_fmt(v->uv_rmt_addr,s2)); + else + log(LOG_INFO,0,"vif #%d, phyint %s", vifi, + inet_fmt(v->uv_lcl_addr,s1)); + 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; + } + } + } +} + +/* + * Send a probe message on vif v + */ +void +send_probe_on_vif(v) + register struct uvif *v; +{ + register char *p; + register int datalen = 0; + struct listaddr *nbr; + int i; + + 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 | + ((v->uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS)), + datalen); +} + +/* + * Start routing on the specified virtual interface. + */ +static void +start_vif(vifi) + vifi_t vifi; +{ + struct uvif *v; + u_int32_t src; + struct phaddr *p; + + v = &uvifs[vifi]; + src = v->uv_lcl_addr; + + /* + * Install the interface in the kernel's vif structure. + */ + 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); + + /* + * Join the ALL-ROUTERS multicast group on the interface. + * This allows mtrace requests to loop back if they are run + * on the multicast router. + */ + k_join(allrtrs_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); + for (p = v->uv_addrs; p; p = p->pa_next) { + start_route_updates(); + update_route(p->pa_addr, p->pa_mask, 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, + IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0); + age_old_hosts(); + } + + v->uv_leaf_timer = LEAF_CONFIRMATION_TIME; + + /* + * Send a probe via the new vif to look for neighbors. + */ + send_probe_on_vif(v); +} + +/* + * Stop routing on the specified virtual interface. + */ +static void +stop_vif(vifi) + vifi_t vifi; +{ + struct uvif *v; + struct listaddr *a; + struct phaddr *p; + + 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); + + /* + * Depart from the ALL-ROUTERS multicast group on the interface. + */ + k_leave(allrtrs_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); + for (p = v->uv_addrs; p; p = p->pa_next) { + start_route_updates(); + update_route(p->pa_addr, p->pa_mask, 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. + */ + if (v->uv_neighbors) + vifs_with_neighbors--; + + 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_int32_t src; + register u_int32_t dst; +{ + register vifi_t vifi; + register struct uvif *v; + register struct phaddr *p; + + 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); + for (p=v->uv_addrs; p; p=p->pa_next) { + if ((src & p->pa_mask) == p->pa_addr && + src != p->pa_addr) + return(vifi); + } + } + } + } + return (NO_VIF); +} + +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, + IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0); + } + } + age_old_hosts(); +} + +/* + * Process an incoming host membership query + */ +void +accept_membership_query(src, dst, group, tmo) + u_int32_t src, dst, group; + int tmo; +{ + register vifi_t vifi; + register struct uvif *v; + + if ((vifi = find_vif(src, dst)) == NO_VIF || + (uvifs[vifi].uv_flags & VIFF_TUNNEL)) { + log(LOG_INFO, 0, + "ignoring group membership query from non-adjacent host %s", + inet_fmt(src, s1)); + return; + } + + v = &uvifs[vifi]; + + /* If we consider ourselves the querier for this vif, but hear a + * query from a router with a lower IP address, yield to them. + * + * This is done here as well as in the neighbor discovery in case + * there is a querier that doesn't speak DVMRP. + */ + if ((v->uv_flags & VIFF_QUERIER) && + (ntohl(src) < ntohl(v->uv_lcl_addr))) { + + v->uv_flags &= ~VIFF_QUERIER; + + } +} + +/* + * Process an incoming group membership report. + */ +void +accept_group_report(src, dst, group, r_type) + u_int32_t 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_v2_HOST_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_v2_HOST_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; + time(&g->al_ctime); + 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 +accept_leave_message( src, dst, group) + u_int32_t 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 leave report from non-adjacent host %s", + inet_fmt(src, s1)); + return; + } + + v = &uvifs[vifi]; + + if (!(v->uv_flags & VIFF_QUERIER)) + return; + + /* + * Look for the group in our group list in order to set up a short-timeout + * query. + */ + for (g = v->uv_groups; g != NULL; g = g->al_next) { + if (group == g->al_addr) { + log(LOG_DEBUG, 0, + "[vif.c, _accept_leave_message] %d %d \n", + g->al_old, g->al_query); + + /* Ignore the leave message if there are old hosts present */ + if (g->al_old) + return; + + /* still waiting for a reply to a query, ignore the leave */ + if (g->al_query) + return; + + /** delete old timer set a timer for expiration **/ + if (g->al_timerid) + g->al_timerid = DeleteTimer(g->al_timerid); + + /** send a group specific querry **/ + g->al_timer = LEAVE_EXPIRE_TIME; + send_igmp(v->uv_lcl_addr, g->al_addr, + IGMP_HOST_MEMBERSHIP_QUERY, + LEAVE_EXPIRE_TIME / 3 * IGMP_TIMER_SCALE, + g->al_addr, 0); + g->al_query = SetQueryTimer(g, vifi, g->al_timer / 3, + LEAVE_EXPIRE_TIME / 3 * 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; + + for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { + if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { + send_probe_on_vif(v); + } + } +} + + +/* + * Send a list of all of our neighbors to the requestor, `src'. + */ +void +accept_neighbor_request(src, dst) + u_int32_t src, dst; +{ + vifi_t vifi; + struct uvif *v; + u_char *p, *ncount; + struct listaddr *la; + int datalen; + u_int32_t 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; +#if (defined(BSD) && (BSD >= 199103)) + addr.sin_len = sizeof addr; +#endif + 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_int32_t src, dst; +{ + vifi_t vifi; + struct uvif *v; + u_char *p, *ncount; + struct listaddr *la; + int datalen; + u_int32_t 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; +#if (defined(BSD) && (BSD >= 199103)) + addr.sin_len = sizeof addr; +#endif + 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; + if (vflags & VIFF_LEAF) + rflags |= DVMRP_NF_LEAF; + 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_int32_t 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_int32_t 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_int32_t addr; + int msgtype; + char *p; + int datalen; + u_int32_t level; +{ + register struct uvif *v; + register struct listaddr *n; + u_int32_t genid = 0; + u_int32_t router; + int he_hears_me = TRUE; + int nflags; + + v = &uvifs[vifi]; + nflags = (level >> 16) & 0xff; + + /* + * 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 (only if vers > 3.2) + * Need to reset the prune state of the router if not. + */ + if (msgtype == DVMRP_PROBE) { + + /* Check genid neighbor flag. Also check version number; 3.3 and + * 3.4 didn't set this flag. */ + if ((((level >> 16) & 0xff) & NF_GENID) || + (((level & 0xff) == 3) && (((level >> 8) & 0xff) > 2))) { + + int i; + + if (datalen < 4) { + log(LOG_WARNING, 0, + "received truncated probe message from %s (len %d)", + inet_fmt(addr, s1), datalen); + return (FALSE); + } + + for (i = 0; i < 4; i++) + ((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 (len %d)", + inet_fmt(addr, s1), datalen); + 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; + else if (n->al_genid != genid) { + log(LOG_DEBUG, 0, + "reset neighbor %s on vif %d [old genid:%x, new:%x]", + inet_fmt(addr, s1), vifi, n->al_genid, genid); + + n->al_genid = genid; + n->al_pv = level & 0xff; + n->al_mv = (level >> 8) & 0xff; + n->al_flags = 0; /*XXX*/ + reset_neighbor_state(vifi, addr); + + /* + * need to do a full route report here + * it gets done by accept_probe() + */ + return (TRUE); + } + + /*XXX nflags shouldn't be dealt with in 2 places in the same + *XXX routine...*/ + if (n->al_flags != nflags) { + n->al_flags = nflags; + if (nflags & NF_LEAF) { + if (!v->uv_leaf_timer) + v->uv_leaf_timer = LEAF_CONFIRMATION_TIME; + } else { + v->uv_flags &= ~VIFF_LEAF; + v->uv_leaf_timer = 0; + } + /* Neighbor flags changed, do a full report */ + return TRUE; + } + } + + /* + * 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); + } + + /* 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) { + log(LOG_DEBUG, 0, "New neighbor %s on vif %d v%d.%d nf 0x%02x", + inet_fmt(addr, s1), vifi, level & 0xff, (level >> 8) & 0xff, + (level >> 16) & 0xff); + + 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; + + time(&n->al_ctime); + n->al_timer = 0; + n->al_next = v->uv_neighbors; + + if (v->uv_neighbors == NULL) + vifs_with_neighbors++; + + v->uv_neighbors = n; + + if (!(v->uv_flags & VIFF_TUNNEL) && + ntohl(addr) < ntohl(v->uv_lcl_addr)) + v->uv_flags &= ~VIFF_QUERIER; + } + + n->al_flags = nflags; + if (!(n->al_flags & NF_LEAF)) { + v->uv_flags &= ~VIFF_LEAF; + v->uv_leaf_timer = 0; + } else { + /*XXX If we have non-leaf neighbors then we know we shouldn't + * mark this vif as a leaf. For now we just count on other + * probes and/or reports resetting the timer. */ + if (!v->uv_leaf_timer) + v->uv_leaf_timer = LEAF_CONFIRMATION_TIME; + } + + 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_int32_t addr; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) { + if (v->uv_leaf_timer && (v->uv_leaf_timer -= TIMER_INTERVAL == 0)) { + v->uv_flags |= VIFF_LEAF; + } + + 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_neighbors == NULL) + vifs_with_neighbors--; + + v->uv_leaf_timer = LEAF_CONFIRMATION_TIME; + + 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; + } + if (!(n->al_flags & NF_LEAF)) { + v->uv_leaf_timer = 0; + } + } + } + } + } +} + +/* + * Returns the neighbor info struct for a given neighbor + */ +struct listaddr * +neighbor_info(vifi, addr) + vifi_t vifi; + u_int32_t addr; +{ + struct listaddr *u; + + for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next) + if (u->al_addr == addr) + return u; + + return NULL; +} + +/* + * Return the neighbor's version number + * returns (protocol_version << 8 + mrouted_version) of neighbor + */ +int +nbr_vers(vifi, addr) + vifi_t vifi; + u_int32_t addr; +{ + struct listaddr *u = neighbor_info(vifi, addr); + + return u ? NBR_VERS(u) : 0; +} + +/* + * 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; + register struct phaddr *p; + struct sioc_vif_req v_req; + + fprintf(fp, "vifs_with_neighbors = %d\n", vifs_with_neighbors); + + if (vifs_with_neighbors == 1) + fprintf(fp,"[This host is a leaf]\n\n"); + + 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: %-18s %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"); + if (v->uv_flags & VIFF_LEAF) fprintf(fp, " leaf"); + fprintf(fp, "\n"); + + if (v->uv_addrs != NULL) { + fprintf(fp, " alternate subnets: %s\n", + inet_fmts(v->uv_addrs->pa_addr, v->uv_addrs->pa_mask, s1)); + for (p = v->uv_addrs->pa_next; p; p = p->pa_next) { + fprintf(fp, " %s\n", + inet_fmts(p->pa_addr, p->pa_mask, s1)); + } + } + + if (v->uv_neighbors != NULL) { + fprintf(fp, " peers: %s (%d.%d) (0x%x)\n", + inet_fmt(v->uv_neighbors->al_addr, s1), + v->uv_neighbors->al_pv, v->uv_neighbors->al_mv, + v->uv_neighbors->al_flags); + for (a = v->uv_neighbors->al_next; a != NULL; a = a->al_next) { + fprintf(fp, " %s (%d.%d) (0x%x)\n", + inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv, + a->al_flags); + } + } + + 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: %-18s\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, " : %-18s\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; + +void +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); +} + + +int +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); +} + +int +DeleteTimer( id) +int id; +{ + timer_clearTimer(id); + return 0; +} + +void +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, 0, 0); + cbk->g->al_query = 0; + free(cbk); +} + +int +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); +} diff --git a/usr.sbin/mrouted/vif.h b/usr.sbin/mrouted/vif.h new file mode 100644 index 00000000000..4ff8194946d --- /dev/null +++ b/usr.sbin/mrouted/vif.h @@ -0,0 +1,76 @@ +/* $NetBSD: vif.h,v 1.5 1995/10/09 03:52:03 thorpej Exp $ */ + +/* + * 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. + */ + +/* + * User level Virtual Interface structure + * + * A "virtual interface" is either a physical, multicast-capable interface + * (called a "phyint") or a virtual point-to-point link (called a "tunnel"). + * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.) + */ +struct uvif { + u_short uv_flags; /* VIFF_ flags defined below */ + u_char uv_metric; /* cost of this vif */ + u_int uv_rate_limit; /* rate limit on this vif */ + u_char uv_threshold; /* min ttl required to forward on vif */ + u_int32_t uv_lcl_addr; /* local address of this vif */ + u_int32_t uv_rmt_addr; /* remote end-point addr (tunnels only) */ + u_int32_t uv_subnet; /* subnet number (phyints only) */ + u_int32_t uv_subnetmask; /* subnet mask (phyints only) */ + u_int32_t uv_subnetbcast;/* subnet broadcast addr (phyints only) */ + char uv_name[IFNAMSIZ]; /* interface name */ + struct listaddr *uv_groups; /* list of local groups (phyints only) */ + struct listaddr *uv_neighbors; /* list of neighboring routers */ + struct vif_acl *uv_acl; /* access control list of groups */ + int uv_leaf_timer; /* time until this vif is considrd leaf */ + struct phaddr *uv_addrs; /* Additional subnets on this vif */ +}; + +#define VIFF_KERNEL_FLAGS (VIFF_TUNNEL|VIFF_SRCRT) +#define VIFF_DOWN 0x0100 /* kernel state of interface */ +#define VIFF_DISABLED 0x0200 /* administratively disabled */ +#define VIFF_QUERIER 0x0400 /* I am the subnet's querier */ +#define VIFF_ONEWAY 0x0800 /* Maybe one way interface */ +#define VIFF_LEAF 0x1000 /* all neighbors are leaves */ + +struct phaddr { + struct phaddr *pa_next; + u_long pa_addr; + u_long pa_mask; +}; + +struct vif_acl { + struct vif_acl *acl_next; /* next acl member */ + u_int32_t acl_addr; /* Group address */ + u_int32_t acl_mask; /* Group addr. mask */ +}; + +struct listaddr { + struct listaddr *al_next; /* link to next addr, MUST BE FIRST */ + u_int32_t al_addr; /* local group or neighbor address */ + u_long al_timer; /* for timing out group or neighbor */ + time_t al_ctime; /* neighbor creation time */ + u_int32_t al_genid; /* generation id for neighbor */ + u_char al_pv; /* router protocol version */ + u_char al_mv; /* router mrouted version */ + u_long al_timerid; /* returned by set timer */ + u_long al_query; /* second query in case of leave */ + u_short al_old; /* if old memberships are present */ + u_short al_last; /* # of query's since last old rep */ + u_char al_flags; /* flags related to this neighbor */ +}; + +#define NF_LEAF 0x01 /* This neighbor is a leaf */ +#define NF_PRUNE 0x02 /* This neighbor understands prunes */ +#define NF_GENID 0x04 /* I supply genid & rtrlist in probe*/ +#define NF_MTRACE 0x08 /* I can understand mtrace requests */ + +#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */ |