summaryrefslogtreecommitdiff
path: root/usr.sbin/mrouted
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /usr.sbin/mrouted
initial import of NetBSD tree
Diffstat (limited to 'usr.sbin/mrouted')
-rw-r--r--usr.sbin/mrouted/LICENSE48
-rw-r--r--usr.sbin/mrouted/Makefile11
-rw-r--r--usr.sbin/mrouted/callout.c201
-rw-r--r--usr.sbin/mrouted/cfparse.y512
-rw-r--r--usr.sbin/mrouted/config.c145
-rw-r--r--usr.sbin/mrouted/defs.h230
-rw-r--r--usr.sbin/mrouted/dvmrp.h160
-rw-r--r--usr.sbin/mrouted/igmp.c305
-rw-r--r--usr.sbin/mrouted/inet.c202
-rw-r--r--usr.sbin/mrouted/kern.c222
-rw-r--r--usr.sbin/mrouted/main.c611
-rw-r--r--usr.sbin/mrouted/mrouted.8399
-rw-r--r--usr.sbin/mrouted/pathnames.h17
-rw-r--r--usr.sbin/mrouted/prune.c2259
-rw-r--r--usr.sbin/mrouted/prune.h139
-rw-r--r--usr.sbin/mrouted/route.c1139
-rw-r--r--usr.sbin/mrouted/route.h50
-rw-r--r--usr.sbin/mrouted/rsrr.c366
-rw-r--r--usr.sbin/mrouted/rsrr.h140
-rw-r--r--usr.sbin/mrouted/snmp.c1307
-rw-r--r--usr.sbin/mrouted/snmp.h505
-rw-r--r--usr.sbin/mrouted/vif.c1373
-rw-r--r--usr.sbin/mrouted/vif.h76
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->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->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->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->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->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->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->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->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->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->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->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->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(&gt,&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(&gt, &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(&gt, &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(&gt, &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 */