The multicast router. Stick it next to routed and XNSrouted since that
authorJordan K. Hubbard <jkh@FreeBSD.org>
Tue, 17 May 1994 20:59:34 +0000 (20:59 +0000)
committerJordan K. Hubbard <jkh@FreeBSD.org>
Tue, 17 May 1994 20:59:34 +0000 (20:59 +0000)
seems the logical place (though /usr/sbin is more my taste).

36 files changed:
sbin/mrouted/.depend [new file with mode: 0644]
sbin/mrouted/LICENSE [new file with mode: 0644]
sbin/mrouted/Makefile [new file with mode: 0644]
sbin/mrouted/config.c [new file with mode: 0644]
sbin/mrouted/defs.h [new file with mode: 0644]
sbin/mrouted/dvmrp.h [new file with mode: 0644]
sbin/mrouted/igmp.c [new file with mode: 0644]
sbin/mrouted/inet.c [new file with mode: 0644]
sbin/mrouted/kern.c [new file with mode: 0644]
sbin/mrouted/main.c [new file with mode: 0644]
sbin/mrouted/mapper.c [new file with mode: 0644]
sbin/mrouted/mrinfo.c [new file with mode: 0644]
sbin/mrouted/mrouted.8 [new file with mode: 0644]
sbin/mrouted/mrouted.conf [new file with mode: 0644]
sbin/mrouted/route.c [new file with mode: 0644]
sbin/mrouted/route.h [new file with mode: 0644]
sbin/mrouted/vif.c [new file with mode: 0644]
sbin/mrouted/vif.h [new file with mode: 0644]
usr.sbin/mrouted/.depend [new file with mode: 0644]
usr.sbin/mrouted/LICENSE [new file with mode: 0644]
usr.sbin/mrouted/Makefile [new file with mode: 0644]
usr.sbin/mrouted/config.c [new file with mode: 0644]
usr.sbin/mrouted/defs.h [new file with mode: 0644]
usr.sbin/mrouted/dvmrp.h [new file with mode: 0644]
usr.sbin/mrouted/igmp.c [new file with mode: 0644]
usr.sbin/mrouted/inet.c [new file with mode: 0644]
usr.sbin/mrouted/kern.c [new file with mode: 0644]
usr.sbin/mrouted/main.c [new file with mode: 0644]
usr.sbin/mrouted/mapper.c [new file with mode: 0644]
usr.sbin/mrouted/mrinfo.c [new file with mode: 0644]
usr.sbin/mrouted/mrouted.8 [new file with mode: 0644]
usr.sbin/mrouted/mrouted.conf [new file with mode: 0644]
usr.sbin/mrouted/route.c [new file with mode: 0644]
usr.sbin/mrouted/route.h [new file with mode: 0644]
usr.sbin/mrouted/vif.c [new file with mode: 0644]
usr.sbin/mrouted/vif.h [new file with mode: 0644]

diff --git a/sbin/mrouted/.depend b/sbin/mrouted/.depend
new file mode 100644 (file)
index 0000000..50bbd6c
--- /dev/null
@@ -0,0 +1,90 @@
+igmp.o : igmp.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+inet.o : inet.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+kern.o : kern.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+config.o : config.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+main.o : main.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+route.o : route.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+vif.o : vif.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+mapper.o : mapper.c /usr/include/netdb.h /usr/include/sys/cdefs.h /usr/include/sys/time.h \
+  /usr/include/time.h /usr/include/machine/ansi.h defs.h /usr/include/stdio.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/net/if_arp.h /usr/include/netinet/in.h \
+  /usr/include/netinet/in_systm.h /usr/include/netinet/ip.h /usr/include/netinet/igmp.h \
+  /usr/include/netinet/ip_mroute.h dvmrp.h vif.h route.h 
+mrinfo.o : mrinfo.c /usr/include/netdb.h /usr/include/sys/cdefs.h /usr/include/sys/time.h \
+  /usr/include/time.h /usr/include/machine/ansi.h defs.h /usr/include/stdio.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/net/if_arp.h /usr/include/netinet/in.h \
+  /usr/include/netinet/in_systm.h /usr/include/netinet/ip.h /usr/include/netinet/igmp.h \
+  /usr/include/netinet/ip_mroute.h dvmrp.h vif.h route.h 
diff --git a/sbin/mrouted/LICENSE b/sbin/mrouted/LICENSE
new file mode 100644 (file)
index 0000000..ef7da47
--- /dev/null
@@ -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/sbin/mrouted/Makefile b/sbin/mrouted/Makefile
new file mode 100644 (file)
index 0000000..b3c1777
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# Makefile for mrouted, a multicast router, and its auxiliary programs,
+# map-mbone and mrinfo.
+#
+# $Id: Makefile,v 1.5 1993/06/24 05:11:16 deering Exp $
+#
+# Modified by: Jim Lowe for FreeBSD 5/17/94
+#
+
+IGMP_SRCS=     igmp.c inet.c kern.c
+IGMP_OBJS=     igmp.o inet.o kern.o
+ROUTER_SRCS=   config.c main.c route.c vif.c
+ROUTER_OBJS=   config.o main.o route.o vif.o
+MAPPER_SRCS=   mapper.c
+MAPPER_OBJS=   mapper.o
+MRINFO_SRCS=   mrinfo.c
+MRINFO_OBJS=   mrinfo.o
+HDRS=          defs.h dvmrp.h route.h vif.h
+SRCS= ${IGMP_SRCS} ${ROUTER_SRCS} ${MAPPER_SRCS} ${MRINFO_SRCS}
+OBJS= ${IGMP_OBJS} ${ROUTER_OBJS} ${MAPPER_OBJS} ${MRINFO_OBJS}
+
+MAN8= mrouted.8
+
+all: mrouted map-mbone mrinfo
+
+mrouted: ${IGMP_OBJS} ${ROUTER_OBJS}
+       rm -f $@
+       ${CC} ${LDFLAGS} -o $@ ${CFLAGS} ${IGMP_OBJS} ${ROUTER_OBJS}
+
+map-mbone: ${IGMP_OBJS} ${MAPPER_OBJS}
+       rm -f $@
+       ${CC} ${LDFLAGS} -o $@ ${CFLAGS} ${IGMP_OBJS} ${MAPPER_OBJS}
+
+mrinfo: ${IGMP_OBJS} ${MRINFO_OBJS}
+       rm -f $@
+       ${CC} ${LDFLAGS} -o $@ ${CFLAGS} ${IGMP_OBJS} ${MRINFO_OBJS}
+
+install: mrouted mrinfo map-mbone
+       install -c mrouted ${BINDIR}
+       install -c -o root -m 500 mrinfo ${BINDIR}
+       install -c -o root -m 500 map-mbone ${BINDIR}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mrouted/config.c b/sbin/mrouted/config.c
new file mode 100644 (file)
index 0000000..9285ccb
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: config.c,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+
+#include "defs.h"
+
+
+char *configfilename = "/etc/mrouted.conf";
+
+
+/*
+ * Forward declarations.
+ */
+static char *next_word();
+
+
+/*
+ * 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, *mp;
+    struct ifconf ifc;
+    register struct uvif *v;
+    register vifi_t vifi;
+    int i, n;
+    u_long addr, mask, subnet;
+    u_int 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.
+        */
+       addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr;
+       if (ifrp->ifr_addr.sa_family != AF_INET)
+           continue;
+
+       /*
+        * 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 (%08x)",
+               ifr.ifr_name, inet_fmt(addr, s1), ntohl(mask));
+           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_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;
+
+       log(LOG_INFO, 0, "installing %s (%s on subnet %s) as vif #%u",
+           v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2),
+           numvifs);
+
+       ++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;
+       }
+    }
+}
+
+static struct ifreq *
+ifconfaddr(ifcp, a)
+    struct ifconf *ifcp;
+    u_long 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 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);
+}
+/*
+ * Read the config file to learn about tunnel vifs and
+ * non-default phyint parameters.
+ */
+void config_vifs_from_file()
+{
+    FILE *f;
+    char linebuf[100];
+    char *w, *s, c;
+    u_long lcl_addr, rmt_addr;
+    struct ifconf ifc;
+    struct ifreq *ifr;
+    struct ifreq ffr;
+    int i;
+    u_int n;
+    struct ifreq ifbuf[32];
+    vifi_t vifi;
+    struct uvif *v;
+
+    f = fopen(configfilename, "r");
+    if (f == NULL) {
+       if (errno != ENOENT)
+           log(LOG_WARNING, 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");
+
+    while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
+
+       s = linebuf;
+       if (EQUAL((w = next_word(&s)), "")) {
+           /*
+            * blank or comment line; ignore
+            */
+       }
+
+       else if (EQUAL(w, "phyint")) {
+           /*
+            * phyint <local-addr> [disable] [metric <m>] [threshold <t>]
+            */
+
+           /*
+            * Parse the local address.
+            */
+           if (EQUAL((w = next_word(&s)), "")) {
+               log(LOG_WARNING, 0,
+                   "missing phyint address in %s",
+                   configfilename);
+               continue;
+           }
+           if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
+               !inet_valid_host(lcl_addr)) {
+               log(LOG_WARNING, 0,
+                   "invalid phyint address '%s' in %s",
+                   w, configfilename);
+               continue;
+           }
+
+           /*
+            * Look up the vif with the specified local address.
+            */
+           for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+               if (!(v->uv_flags & VIFF_TUNNEL) &&
+                   lcl_addr == v->uv_lcl_addr) {
+                   break;
+               }
+           }
+           if (vifi == numvifs) {
+               log(LOG_WARNING, 0,
+                   "phyint %s in %s is not a configured interface",
+                   inet_fmt(lcl_addr, s1), configfilename);
+               continue;
+           }
+
+           /*
+            * Look for "disable", "metric" and "threshold" options.
+            */
+           while (!EQUAL((w = next_word(&s)), "")) {
+               if (EQUAL(w, "disable")) {
+                   v->uv_flags |= VIFF_DISABLED;
+               }
+               else if (EQUAL(w, "metric")) {
+                   if(EQUAL((w = next_word(&s)), "")) {
+                       log(LOG_WARNING, 0,
+                           "missing metric for phyint %s in %s",
+                           inet_fmt(lcl_addr, s1), configfilename);
+                       w = "garbage";
+                       break;
+                   }
+                   if(sscanf(w, "%u%c", &n, &c) != 1 ||
+                             n < 1 || n >= UNREACHABLE ) {
+                       log(LOG_WARNING, 0,
+                           "invalid metric '%s' for phyint %s in %s",
+                           w, inet_fmt(lcl_addr, s1), configfilename);
+                       break;
+                   }
+                   v->uv_metric = n;
+               }
+               else if (EQUAL(w, "threshold")) {
+                   if(EQUAL((w = next_word(&s)), "")) {
+                       log(LOG_WARNING, 0,
+                           "missing threshold for phyint %s in %s",
+                           inet_fmt(lcl_addr, s1), configfilename);
+                       w = "garbage";
+                       break;
+                   }
+                   if(sscanf(w, "%u%c", &n, &c) != 1 ||
+                             n < 1 || n > 255 ) {
+                       log(LOG_WARNING, 0,
+                           "invalid threshold '%s' for phyint %s in %s",
+                           w, inet_fmt(lcl_addr, s1), configfilename);
+                       break;
+                   }
+                   v->uv_threshold = n;
+               }
+               else break;
+           }
+           if (!EQUAL(w, "")) continue;
+       }
+
+       else if (EQUAL(w, "tunnel")) {
+           /*
+            * tunnel <local-addr> <remote-addr> [srcrt] [metric <m>] [threshold <t>]
+            */
+
+           /*
+            * Parse the local address.
+            */
+           if (EQUAL((w = next_word(&s)), "")) {
+               log(LOG_WARNING, 0,
+                   "missing tunnel local address in %s",
+                   configfilename);
+               continue;
+           }
+           if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
+               !inet_valid_host(lcl_addr)) {
+               log(LOG_WARNING, 0,
+                   "invalid tunnel local address '%s' in %s",
+                   w, configfilename);
+               continue;
+           }
+
+           /*
+            * Make sure the local address is one of ours.
+            */
+           ifr = ifconfaddr(&ifc, lcl_addr);
+           if (ifr == 0) {
+               log(LOG_WARNING, 0,
+                   "tunnel local address %s in %s is not one of ours",
+                   inet_fmt(lcl_addr, s1), configfilename);
+               continue;
+           }
+
+           /*
+            * Make sure the local address doesn't name a loopback interface..
+            */
+           strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
+           if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr) < 0) {
+               log(LOG_ERR, errno,
+                   "ioctl SIOCGIFFLAGS for %s", ffr.ifr_name);
+           }
+           if (ffr.ifr_flags & IFF_LOOPBACK) {
+               log(LOG_WARNING, 0,
+                   "tunnel local address %s in %s is a loopback interface",
+                   inet_fmt(lcl_addr, s1), configfilename);
+               continue;
+           }
+
+           /*
+            * Parse the remote address.
+            */
+           if (EQUAL((w = next_word(&s)), "")) {
+               log(LOG_WARNING, 0,
+                   "missing tunnel remote address in %s",
+                   configfilename);
+               continue;
+           }
+           if ((rmt_addr = inet_parse(w)) == 0xffffffff ||
+               !inet_valid_host(rmt_addr)) {
+               log(LOG_WARNING, 0,
+                   "invalid tunnel remote address %s in %s",
+                   w, configfilename);
+               continue;
+           }
+
+           /*
+            * Make sure the remote address is not one of ours.
+            */
+           if (ifconfaddr(&ifc, rmt_addr) != 0) {
+               log(LOG_WARNING, 0,
+                   "tunnel remote address %s in %s is one of ours",
+                   inet_fmt(rmt_addr, s1), configfilename);
+               continue;
+           }
+
+           /*
+            * Make sure the remote address has not been used for another
+            * tunnel and does not belong to a subnet to which we have direct
+            * access on an enabled phyint.
+            */
+           for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+               if (v->uv_flags & VIFF_TUNNEL) {
+                   if (rmt_addr == v->uv_rmt_addr) {
+                       log(LOG_WARNING, 0,
+                           "duplicate tunnel remote address %s in %s",
+                           inet_fmt(rmt_addr, s1), configfilename);
+                       break;
+                   }
+               }
+               else if (!(v->uv_flags & VIFF_DISABLED)) {
+                   if ((rmt_addr & v->uv_subnetmask) == v->uv_subnet) {
+                       log(LOG_WARNING, 0,
+                           "unnecessary tunnel remote address %s in %s",
+                           inet_fmt(rmt_addr, s1), configfilename);
+                       break;
+                   }
+               }
+           }
+           if (vifi != numvifs) continue;
+
+           /*
+            * OK, let's initialize a uvif structure for the tunnel.
+            */
+           if (numvifs == MAXVIFS) {
+               log(LOG_WARNING, 0, "too many vifs, ignoring tunnel to %s",
+                   inet_fmt(rmt_addr, s1));
+               continue;
+           }
+           v  = &uvifs[numvifs];
+           v->uv_flags       = VIFF_TUNNEL;
+           v->uv_metric      = DEFAULT_METRIC;
+           v->uv_threshold   = DEFAULT_THRESHOLD;
+           v->uv_lcl_addr    = lcl_addr;
+           v->uv_rmt_addr    = rmt_addr;
+           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;
+
+           /*
+            * Look for "metric" and "threshold" options.
+            */
+           while (!EQUAL((w = next_word(&s)), "")) {
+               if (EQUAL(w, "metric")) {
+                   if(EQUAL((w = next_word(&s)), "")) {
+                       log(LOG_WARNING, 0,
+                           "missing metric for tunnel to %s in %s",
+                           inet_fmt(rmt_addr, s1), configfilename);
+                       w = "garbage";
+                       break;
+                   }
+                   if(sscanf(w, "%u%c", &n, &c) != 1 ||
+                             n < 1 || n >= UNREACHABLE ) {
+                       log(LOG_WARNING, 0,
+                           "invalid metric '%s' for tunnel to %s in %s",
+                           w, inet_fmt(rmt_addr, s1), configfilename);
+                       break;
+                   }
+                   v->uv_metric = n;
+               }
+               else if (EQUAL(w, "threshold")) {
+                   if(EQUAL((w = next_word(&s)), "")) {
+                       log(LOG_WARNING, 0,
+                           "missing threshold for tunnel to %s in %s",
+                           inet_fmt(rmt_addr, s1), configfilename);
+                       w = "garbage";
+                       break;
+                   }
+                   if(sscanf(w, "%u%c", &n, &c) != 1 ||
+                             n < 1 || n > 255 ) {
+                       log(LOG_WARNING, 0,
+                           "invalid threshold '%s' for tunnel to %s in %s",
+                           w, inet_fmt(rmt_addr, s1), configfilename);
+                       break;
+                   }
+                   v->uv_threshold = n;
+               }
+               else if (EQUAL(w, "srcrt") || EQUAL(w, "sourceroute")) {
+                   v->uv_flags |= VIFF_SRCRT;
+               }
+               else break;
+           }
+           if (!EQUAL(w, "")) continue;
+
+           log(LOG_INFO, 0,
+               "installing %stunnel from %s to %s as vif #%u",
+               v->uv_flags & VIFF_SRCRT? "srcrt " : "",
+               inet_fmt(lcl_addr, s1), inet_fmt(rmt_addr, s2), numvifs);
+
+           ++numvifs;
+
+           if (!(ffr.ifr_flags & IFF_UP)) {
+               v->uv_flags |= VIFF_DOWN;
+               vifs_down = TRUE;
+           }
+       }
+
+       else {
+           log(LOG_WARNING, 0,
+               "unknown command '%s' in %s", w, configfilename);
+       }
+    }
+
+    close(f);
+}
+
+
+/*
+ * Return a pointer to the next "word" in the string to which '*s' points,
+ * lower-cased and null terminated, and advance '*s' to point beyond the word.
+ * Words are separated by blanks and/or tabs, and the input string is
+ * considered to terminate at a newline, '#' (comment), or null character.
+ * If no words remain, a pointer to a null string ("") is returned.  
+ * Warning: This function clobbers the input string.
+ */
+static char *next_word(s)
+    char **s;
+{
+    char *w;
+
+    w = *s;
+    while (*w == ' ' || *w == '\t')
+       ++w;
+
+    *s = w;
+    for(;;) {
+       switch (**s) {
+
+           case ' '  :
+           case '\t' : **s = '\0';
+                       ++*s;
+                       return (w);
+
+           case '\n' :
+           case '#'  : **s = '\0';
+                       return (w);
+
+           case '\0' : return (w);
+
+           default   : if (isascii(**s) && isupper(**s))
+                           **s = tolower(**s);
+                       ++*s;
+       }
+    }
+}
diff --git a/sbin/mrouted/defs.h b/sbin/mrouted/defs.h
new file mode 100644 (file)
index 0000000..f01179f
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: defs.h,v 1.4 1993/06/24 05:11:16 deering Exp $
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.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 "dvmrp.h"
+#include "vif.h"
+#include "route.h"
+
+
+/*
+ * Miscellaneous constants and macros.
+ */
+#define FALSE          0
+#define TRUE           1
+
+#define EQUAL(s1, s2)  (strcmp((s1), (s2)) == 0)
+
+#define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY
+
+#define PROTOCOL_VERSION 2  /* increment when packet format/content changes */
+
+#define MROUTED_VERSION  0  /* increment on local changes or bug fixes, */
+                           /* reset to 0 whever PROTOCOL_VERSION increments */
+
+#define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION )
+                           /* for IGMP 'group' field of DVMRP messages */
+
+/*
+ * External declarations for global variables and functions.
+ */
+extern char            recv_buf[MAX_IP_PACKET_LEN];
+extern char            send_buf[MAX_IP_PACKET_LEN];
+extern int             igmp_socket;
+extern u_long          allhosts_group;
+extern u_long          dvmrp_group;
+
+#define DEFAULT_DEBUG  2       /* default if "-d" given without value */
+
+extern int             debug;
+
+extern int             routes_changed;
+extern int             delay_change_reports;
+
+extern struct uvif     uvifs[MAXVIFS];
+extern vifi_t          numvifs;
+extern int             vifs_down;
+extern int             udp_socket;
+
+extern char            s1[];
+extern char            s2[];
+extern char            s3[];
+
+extern int             errno;
+extern int             sys_nerr;
+extern char *          sys_errlist[];
+
+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            accept_probe();
+extern void            accept_report();
+extern void            report();
+extern void            report_to_all_neighbors();
+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            accept_group_report();
+extern void            query_groups();
+extern void            probe_for_neighbors();
+extern int             update_neighbor();
+extern void            accept_neighbor_request();
+
+extern void            config_vifs_from_kernel();
+extern void            config_vifs_from_file();
+
+extern int             inet_valid_host();
+extern int             inet_valid_subnet();
+extern char *          inet_fmt();
+extern char *          inet_fmts();
+extern u_long          inet_parse();
+extern int             inet_cksum();
+
+extern char *          malloc();
+extern char *          fgets();
+extern FILE *          fopen();
+
+#ifndef htonl
+extern u_long          htonl();
+extern u_long          ntohl();
+#endif
diff --git a/sbin/mrouted/dvmrp.h b/sbin/mrouted/dvmrp.h
new file mode 100644 (file)
index 0000000..662f1cb
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: dvmrp.h,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+/*
+ * 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
+
+/*
+ * '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_DOWN          0x10    /* kernel state of interface */
+#define DVMRP_NF_DISABLED      0x20    /* administratively disabled */
+#define DVMRP_NF_QUERIER       0x40    /* I am the subnet's querier */
+
+/*
+ * 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_long)0xe0000004    /* 224.0.0.4 */
+
+#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        190     /* 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 UNREACHABLE            32      /* "infinity" metric, must be <= 64 */
+#define DEFAULT_METRIC         1       /* default subnet/tunnel metric     */
+#define DEFAULT_THRESHOLD      1       /* default subnet/tunnel threshold  */
diff --git a/sbin/mrouted/igmp.c b/sbin/mrouted/igmp.c
new file mode 100644 (file)
index 0000000..24c8099
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: igmp.c,v 1.5 1993/06/23 18:47:17 pavel Exp $
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * Exported variables.
+ */
+char           recv_buf[MAX_IP_PACKET_LEN]; /* input packet buffer         */
+char           send_buf[MAX_IP_PACKET_LEN]; /* output packet buffer        */
+int            igmp_socket;                 /* socket for all network I/O  */
+u_long         allhosts_group;              /* allhosts  addr in net order */
+u_long         dvmrp_group;                 /* DVMRP grp addr in net order */
+
+
+/*
+ * Open and initialize the igmp socket, and fill in the non-changing
+ * IP header fields in the output packet buffer.
+ */
+void init_igmp()
+{
+    struct ip *ip;
+
+    if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) 
+       log(LOG_ERR, errno, "IGMP socket");
+
+    k_hdr_include(TRUE);       /* include IP header when sending */
+    k_set_rcvbuf(48*1024);     /* lots of input buffering        */
+    k_set_ttl(1);              /* restrict multicasts to one hop */
+    k_set_loop(FALSE);         /* disable multicast loopback     */
+
+    ip         = (struct ip *)send_buf;
+    ip->ip_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);
+}
+
+static char *packet_kind(type, code)
+     u_char type, code;
+{
+    switch (type) {
+       case IGMP_HOST_MEMBERSHIP_QUERY:        return "membership query  ";
+       case IGMP_HOST_MEMBERSHIP_REPORT:       return "membership report ";
+       case IGMP_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   ";
+           default:                            return "unknown DVMRP msg ";
+         }
+       default:                                return "unknown IGMP msg  ";
+    }
+}
+
+/*
+ * Process a newly received IGMP packet that is sitting in the input
+ * packet buffer.
+ */
+void accept_igmp(recvlen)
+    int recvlen;
+{
+    register vifi_t vifi;
+    register u_long src, dst, group;
+    struct ip *ip;
+    struct igmp *igmp;
+    int ipdatalen, iphdrlen, igmpdatalen;
+
+    if (recvlen < sizeof(struct ip)) {
+       log(LOG_WARNING, 0,
+           "received packet too short (%u bytes) for IP header", recvlen);
+       return;
+    }
+
+    ip        = (struct ip *)recv_buf;
+    src       = ip->ip_src.s_addr;
+    dst       = ip->ip_dst.s_addr;
+    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:
+           return;     /* Answered automatically by the kernel. */
+
+       case IGMP_HOST_MEMBERSHIP_REPORT:
+           accept_group_report(src, dst, group);
+           return;
+
+       case IGMP_DVMRP:
+           switch (igmp->igmp_code) {
+
+               case DVMRP_PROBE:
+                   accept_probe(src, dst);
+                   return;
+
+               case DVMRP_REPORT:
+                   accept_report(src, dst,
+                                 (char *)(igmp+1), igmpdatalen);
+                   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;
+
+               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;
+           }
+
+       default:
+           log(LOG_INFO, 0,
+               "ignoring unknown IGMP message type %u from %s to %s",
+               igmp->igmp_type, inet_fmt(src, s1),
+               inet_fmt(dst, s2));
+           return;
+    }
+}
+
+
+/*
+ * Construct an IGMP message in the output packet buffer.  The caller may
+ * have already placed data in that buffer, of length 'datalen'.  Then send
+ * the message from the interface with IP address 'src' to destination 'dst'.
+ */
+void send_igmp(src, dst, type, code, group, datalen)
+    u_long src, dst;
+    int type, code;
+    u_long group;
+    int datalen;
+{
+    static struct sockaddr_in sdst = {AF_INET};
+    struct ip *ip;
+    struct igmp *igmp;
+
+    ip                      = (struct ip *)send_buf;
+    ip->ip_src.s_addr       = src;
+    ip->ip_dst.s_addr       = dst;
+    ip->ip_len              = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+
+    igmp                    = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
+    igmp->igmp_type         = type;
+    igmp->igmp_code         = code;
+    igmp->igmp_group.s_addr = group;
+    igmp->igmp_cksum        = 0;
+    igmp->igmp_cksum        = inet_cksum((u_short *)igmp,
+                                        IGMP_MINLEN + datalen);
+
+    if (IN_MULTICAST(ntohl(dst))) k_set_if(src);
+    if (dst == allhosts_group) k_set_loop(TRUE);
+
+    sdst.sin_addr.s_addr = dst;
+    if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
+                       (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
+       if (errno == ENETDOWN) check_vif_state();
+       else log(LOG_WARNING, errno, "sendto on %s", inet_fmt(src, s1));
+    }
+
+    if (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/sbin/mrouted/inet.c b/sbin/mrouted/inet.c
new file mode 100644 (file)
index 0000000..5d7442b
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: inet.c,v 1.4 1993/05/30 01:36:38 deering Exp $
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * Exported variables.
+ */
+char s1[16];           /* buffers to hold the string representations  */
+char s2[16];           /* of IP addresses, to be passed to inet_fmt() */
+char s3[16];           /* or inet_fmts().                             */
+
+
+/*
+ * 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_long naddr;
+{
+    register u_long 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.
+ */
+int inet_valid_subnet(nsubnet, nmask)
+    u_long nsubnet, nmask;
+{
+    register u_long subnet, mask;
+
+    subnet = ntohl(nsubnet);
+    mask   = ntohl(nmask);
+
+    if ((subnet & mask) != subnet) return (FALSE);
+
+    if (IN_CLASSA(subnet)) {
+       if (mask < 0xff000000 ||
+          (subnet & 0xff000000) == 0 ||
+          (subnet & 0xff000000) == 0x7f000000) return (FALSE);
+    }
+    else if (IN_CLASSB(subnet)) {
+       if (mask < 0xffff0000) return (FALSE);
+    }
+    else if (IN_CLASSC(subnet)) {
+       if (mask < 0xffffff00) return (FALSE);
+    }
+    else return (FALSE);
+
+    return (TRUE);
+}
+
+
+/*
+ * Convert an IP address in u_long (network) format into a printable string.
+ */
+char *inet_fmt(addr, s)
+    u_long 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.
+ */
+char *inet_fmts(addr, mask, s)
+    u_long addr, mask;
+    char *s;
+{
+    register u_char *a, *m;
+
+    a = (u_char *)&addr;
+    m = (u_char *)&mask;
+
+    if      (m[3] != 0) sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
+    else if (m[2] != 0) sprintf(s, "%u.%u.%u",    a[0], a[1], a[2]);
+    else if (m[1] != 0) sprintf(s, "%u.%u",       a[0], a[1]);
+    else                sprintf(s, "%u",          a[0]);
+
+    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_long inet_parse(s)
+    char *s;
+{
+    u_long a;
+    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/sbin/mrouted/kern.c b/sbin/mrouted/kern.c
new file mode 100644 (file)
index 0000000..548247b
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: kern.c,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+
+#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_long 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_long grp;
+    u_long 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_long grp;
+    u_long 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()
+{
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_INIT,
+                       (char *)NULL, 0) < 0)
+       log(LOG_ERR, errno, "can't enable DVMRP routing in kernel");
+}
+
+
+void k_stop_dvmrp()
+{
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DONE,
+                       (char *)NULL, 0) < 0)
+       log(LOG_WARNING, errno, "can't disable DVMRP 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_lcl_addr.s_addr = v->uv_lcl_addr;
+    vc.vifc_rmt_addr.s_addr = v->uv_rmt_addr;
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_VIF,
+                       (char *)&vc, sizeof(vc)) < 0)
+       log(LOG_ERR, errno, "setsockopt DVMRP_ADD_VIF");
+}
+
+
+void k_del_vif(vifi)
+    vifi_t vifi;
+{
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_VIF,
+                       (char *)&vifi, sizeof(vifi)) < 0)
+       log(LOG_ERR, errno, "setsockopt DVMRP_DEL_VIF");
+}
+
+
+void k_add_group(vifi, group)
+    vifi_t vifi;
+    u_long group;
+{
+    struct lgrplctl lc;
+
+    lc.lgc_vifi         = vifi;
+    lc.lgc_gaddr.s_addr = group;
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_LGRP,
+                       (char *)&lc, sizeof(lc)) < 0)
+       log(LOG_WARNING, errno, "setsockopt DVMRP_ADD_LGRP");
+}
+
+
+void k_del_group(vifi, group)
+    vifi_t vifi;
+    u_long group;
+{
+    struct lgrplctl lc;
+
+    lc.lgc_vifi         = vifi;
+    lc.lgc_gaddr.s_addr = group;
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_LGRP,
+                       (char *)&lc, sizeof(lc)) < 0)
+       log(LOG_WARNING, errno, "setsockopt DVMRP_DEL_LGRP");
+}
+
+
+void k_add_route(r)
+    struct rtentry *r;
+{
+    struct mrtctl mc;
+
+    mc.mrtc_origin.s_addr     = r->rt_origin;
+    mc.mrtc_originmask.s_addr = r->rt_originmask;
+    mc.mrtc_parent            = r->rt_parent;
+    VIFM_COPY(r->rt_children, mc.mrtc_children);
+    VIFM_COPY(r->rt_leaves,   mc.mrtc_leaves);
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_MRT,
+                       (char *)&mc, sizeof(mc)) < 0)
+       log(LOG_WARNING, errno, "setsockopt DVMRP_ADD_MRT");
+}
+
+
+void k_update_route(r)
+    struct rtentry *r;
+{
+    k_add_route(r);
+}
+
+
+void k_del_route(r)
+    struct rtentry *r;
+{
+    struct in_addr orig;
+
+    orig.s_addr = r->rt_origin;
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_MRT,
+                       (char *)&orig, sizeof(orig)) < 0)
+       log(LOG_WARNING, errno, "setsockopt DVMRP_DEL_MRT");
+}
diff --git a/sbin/mrouted/main.c b/sbin/mrouted/main.c
new file mode 100644 (file)
index 0000000..6352a97
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: main.c,v 1.5 1993/06/24 05:11:16 deering Exp $
+ */
+
+/*
+ * 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"
+
+extern char *configfilename;
+
+static char pidfilename[]  = "/etc/mrouted.pid";
+static char dumpfilename[] = "/usr/tmp/mrouted.dump";
+
+static int debug = 0;
+
+
+/*
+ * Forward declarations.
+ */
+static void timer();
+static void hup();
+static void dump();
+static void fdump();
+
+
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    register int recvlen;
+    register int omask;
+    int dummy;
+    FILE *fp;
+    extern uid_t geteuid();
+
+    setlinebuf(stderr);
+
+    if (geteuid() != 0) {
+       fprintf(stderr, "mrouted: 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
+           goto usage;
+       argv++, argc--;
+    }
+
+    if (argc > 0) {
+usage: fprintf(stderr, "usage: mrouted [-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);
+       t = open("/dev/tty", 2);
+       if (t >= 0) {
+           (void)ioctl(t, TIOCNOTTY, (char *)0);
+           (void)close(t);
+       }
+    }
+    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);
+
+    fp = fopen(pidfilename, "w");
+    if (fp != NULL) {
+       fprintf(fp, "%d\n", getpid());
+       (void) fclose(fp);
+    }
+
+    srandom(gethostid());
+
+    init_igmp();
+    k_init_dvmrp();            /* enable DVMRP routing in kernel */
+    init_routes();
+    init_vifs();
+
+    if (debug >= 2) dump();
+
+    (void)signal(SIGALRM, timer);
+    (void)signal(SIGHUP,  hup);
+    (void)signal(SIGTERM, hup);
+    (void)signal(SIGINT,  hup);
+    (void)signal(SIGUSR1, fdump);
+    if (debug != 0)
+       (void)signal(SIGQUIT, dump);
+
+    (void)alarm(TIMER_INTERVAL);        /* schedule first timer interrupt */
+
+    /*
+     * Main receive loop.
+     */
+    dummy = 0;
+    for(;;) {
+       recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
+                          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);
+    }
+}
+
+
+/*
+ * 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()
+{
+    int next_interval;
+
+    age_routes();      /* Advance the timers in the route entries     */
+    age_vifs();                /* Advance the timers for neighbors and groups */
+
+    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;
+    next_interval = TIMER_INTERVAL;
+
+    if (virtual_time % ROUTE_REPORT_INTERVAL == 0) {
+       /*
+        * Time for the periodic report of all routes to all neighbors.
+        */
+       report_to_all_neighbors(ALL_ROUTES);
+
+       /*
+        * Schedule the next timer interrupt for a random time between
+        * 1 and TIMER_INTERVAL seconds from now.  This randomization is
+        * intended to counteract the undesirable synchronizing tendency
+        * of periodic transmissions from multiple sources.
+        */
+       next_interval = (random() % TIMER_INTERVAL) + 1;
+    }
+    else 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);
+    }
+
+    /*
+     * Advance virtual time and schedule the next timer interrupt.
+     */
+    virtual_time += TIMER_INTERVAL;
+    (void)alarm(next_interval);
+}
+
+
+/*
+ * On hangup signal, let everyone know we're going away.
+ */
+static void hup()
+{
+    log(LOG_INFO, 0, "hup");
+    expire_all_routes();
+    report_to_all_neighbors(ALL_ROUTES);
+    exit(1);
+}
+
+
+/*
+ * 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);
+    }
+}
+
+
+/*
+ * 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.
+ */
+void log(severity, syserr, format, a, b, c, d, e)
+    int severity, syserr;
+    char *format;
+    int a, b, c, d, e;
+{
+    char fmt[100];
+
+    switch (debug) {
+       case 0: break;
+       case 1: if (severity > LOG_NOTICE) break;
+       case 2: if (severity > LOG_INFO  ) break;
+       default:
+           fmt[0] = '\0';
+           if (severity == LOG_WARNING) strcat(fmt, "warning - ");
+           strncat(fmt, format, 80);
+           fprintf(stderr, fmt, a, b, c, d, e);
+           if (syserr == 0)
+               fprintf(stderr, "\n");
+           else if(syserr < sys_nerr)
+               fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+           else
+               fprintf(stderr, ": errno %d\n", syserr);
+    }
+
+    if (severity <= LOG_NOTICE) {
+       fmt[0] = '\0';
+       if (severity == LOG_WARNING) strcat(fmt, "warning - ");
+       strncat(fmt, format, 80);
+       if (syserr != 0) {
+               strcat(fmt, ": %m");
+               errno = syserr;
+       }
+       syslog(severity, fmt, a, b, c, d, e);
+
+       if (severity <= LOG_ERR) exit(-1);
+    }
+}
diff --git a/sbin/mrouted/mapper.c b/sbin/mrouted/mapper.c
new file mode 100644 (file)
index 0000000..287df84
--- /dev/null
@@ -0,0 +1,932 @@
+/* Mapper for connections between MRouteD multicast routers.
+ * Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
+ *
+ * $Id: mapper.c,v 1.4 1993/06/24 05:11:16 deering Exp $
+ */
+
+/*
+ * Copyright (c) Xerox Corporation 1992. All rights reserved.
+ *  
+ * License is granted to copy, to use, and to make and to use derivative
+ * works for research and evaluation purposes, provided that Xerox is
+ * acknowledged in all documentation pertaining to any such copy or derivative
+ * work. Xerox grants no other licenses expressed or implied. The Xerox trade
+ * name should not be used in any advertising without its written permission.
+ *  
+ * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
+ * FOR ANY PARTICULAR PURPOSE.  The software is provided "as is" without
+ * express or implied warranty of any kind.
+ *  
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#include <netdb.h>
+#include <sys/time.h>
+#include "defs.h"
+
+#define DEFAULT_TIMEOUT        2       /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 1      /* How many times to ask each router */
+
+
+/* All IP addresses are stored in the data structure in NET order. */
+
+typedef struct neighbor {
+    struct neighbor    *next;
+    u_long             addr;           /* IP address in NET order */
+    u_char             metric;         /* TTL cost of forwarding */
+    u_char             threshold;      /* TTL threshold to forward */
+    u_short            flags;          /* flags on connection */
+#define NF_PRESENT 0x8000      /* True if flags are meaningful */
+} Neighbor;
+
+typedef struct interface {
+    struct interface *next;
+    u_long     addr;           /* IP address of the interface in NET order */
+    Neighbor   *neighbors;     /* List of neighbors' IP addresses */
+} Interface;
+
+typedef struct node {
+    u_long     addr;           /* IP address of this entry in NET order */
+    u_long     version;        /* which mrouted version is running */
+    int                tries;          /* How many requests sent?  -1 for aliases */
+    union {
+       struct node *alias;             /* If alias, to what? */
+       struct interface *interfaces;   /* Else, neighbor data */
+    } u;
+    struct node *left, *right;
+} Node;
+
+
+Node   *routers = 0;
+u_long our_addr, target_addr = 0;              /* in NET order */
+int    debug = 0;
+int    retries = DEFAULT_RETRIES;
+int    timeout = DEFAULT_TIMEOUT;
+int    show_names = TRUE;
+
+
+Node *find_node(addr, ptr)
+    u_long addr;
+    Node **ptr;
+{
+    Node *n = *ptr;
+
+    if (!n) {
+       *ptr = n = (Node *) malloc(sizeof(Node));
+       n->addr = addr;
+       n->version = 0;
+       n->tries = 0;
+       n->u.interfaces = 0;
+       n->left = n->right = 0;
+       return n;
+    } else if (addr == n->addr)
+       return n;
+    else if (addr < n->addr)
+       return find_node(addr, &(n->left));
+    else
+       return find_node(addr, &(n->right));
+}
+
+
+Interface *find_interface(addr, node)
+    u_long addr;
+    Node *node;
+{
+    Interface *ifc;
+
+    for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+       if (ifc->addr == addr)
+           return ifc;
+
+    ifc = (Interface *) malloc(sizeof(Interface));
+    ifc->addr = addr;
+    ifc->next = node->u.interfaces;
+    node->u.interfaces = ifc;
+    ifc->neighbors = 0;
+
+    return ifc;
+}
+
+
+Neighbor *find_neighbor(addr, node)
+    u_long addr;
+    Node *node;
+{
+    Interface *ifc;
+
+    for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+       Neighbor *nb;
+
+       for (nb = ifc->neighbors; nb; nb = nb->next)
+           if (nb->addr == addr)
+               return nb;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level.  For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+void log(severity, syserr, format, a, b, c, d, e)
+    int severity, syserr;
+    char *format;
+    int a, b, c, d, e;
+{
+    char fmt[100];
+
+    switch (debug) {
+       case 0: if (severity > LOG_WARNING) return;
+       case 1: if (severity > LOG_NOTICE ) return;
+       case 2: if (severity > LOG_INFO   ) return;
+       default:
+           fmt[0] = '\0';
+           if (severity == LOG_WARNING)
+               strcat(fmt, "warning - ");
+           strncat(fmt, format, 80);
+           fprintf(stderr, fmt, a, b, c, d, e);
+           if (syserr == 0)
+               fprintf(stderr, "\n");
+           else if (syserr < sys_nerr)
+               fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+           else
+               fprintf(stderr, ": errno %d\n", syserr);
+    }
+
+    if (severity <= LOG_ERR)
+       exit(-1);
+}
+
+
+/*
+ * Send a neighbors-list request.
+ */
+void ask(dst)
+    u_long dst;
+{
+    send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+               htonl(MROUTED_LEVEL), 0);
+}
+
+void ask2(dst)
+    u_long dst;
+{
+    send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+               htonl(MROUTED_LEVEL), 0);
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void accept_group_report(src, dst, group)
+    u_long src, dst, group;
+{
+    log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void accept_probe(src, dst)
+    u_long src, dst;
+{
+    log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming route report message.
+ */
+void accept_report(src, dst, p, datalen)
+    u_long src, dst;
+    char *p;
+    int datalen;
+{
+    log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list request message.
+ */
+void accept_neighbor_request(src, dst)
+    u_long src, dst;
+{
+    if (src != our_addr)
+       log(LOG_INFO, 0,
+           "ignoring spurious DVMRP neighbor request from %s to %s",
+           inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+void accept_neighbor_request2(src, dst)
+    u_long src, dst;
+{
+    if (src != our_addr)
+       log(LOG_INFO, 0,
+           "ignoring spurious DVMRP neighbor request2 from %s to %s",
+           inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors(src, dst, p, datalen, level)
+    u_long src, dst, level;
+    u_char *p;
+    int datalen;
+{
+    Node       *node = find_node(src, &routers);
+
+    if (node->tries == 0)      /* Never heard of 'em; must have hit them at */
+       node->tries = 1;        /* least once, though...*/
+    else if (node->tries == -1)        /* follow alias link */
+       node = node->u.alias;
+
+#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
+                    a += ((u_long)*p++ << 8), a += *p++)
+
+    /* if node is running a recent mrouted, ask for additional info */
+    if (level != 0) {
+       node->version = ntohl(level);
+       node->tries = 0;
+       ask2(src);
+       return;
+    }
+
+    if (debug > 3) {
+       int i;
+
+       fprintf(stderr, "    datalen = %d\n", datalen);
+       for (i = 0; i < datalen; i++) {
+           if ((i & 0xF) == 0)
+               fprintf(stderr, "   ");
+           fprintf(stderr, " %02x", p[i]);
+           if ((i & 0xF) == 0xF)
+               fprintf(stderr, "\n");
+       }
+       if ((datalen & 0xF) != 0xF)
+           fprintf(stderr, "\n");
+    }
+
+    while (datalen > 0) {      /* loop through interfaces */
+       u_long          ifc_addr;
+       u_char          metric, threshold, ncount;
+       Node           *ifc_node;
+       Interface      *ifc;
+       Neighbor       *old_neighbors;
+
+       if (datalen < 4 + 3) {
+           log(LOG_WARNING, 0, "received truncated interface record from %s",
+               inet_fmt(src, s1));
+           return;
+       }
+
+       GET_ADDR(ifc_addr);
+       ifc_addr = htonl(ifc_addr);
+       metric = *p++;
+       threshold = *p++;
+       ncount = *p++;
+       datalen -= 4 + 3;
+
+       /* Fix up any alias information */
+       ifc_node = find_node(ifc_addr, &routers);
+       if (ifc_node->tries == 0) { /* new node */
+           ifc_node->tries = -1;
+           ifc_node->u.alias = node;
+       } else if (ifc_node != node
+                  && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
+           /* must merge two hosts' nodes */
+           Interface  *ifc_i, *next_ifc_i;
+
+           if (ifc_node->tries == -1) {
+               Node *tmp = ifc_node->u.alias;
+
+               ifc_node->u.alias = node;
+               ifc_node = tmp;
+           }
+
+           /* Merge ifc_node (foo_i) into node (foo_n) */
+
+           if (ifc_node->tries > node->tries)
+               node->tries = ifc_node->tries;
+
+           for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+               Neighbor *nb_i, *next_nb_i, *nb_n;
+               Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+               old_neighbors = ifc_n->neighbors;
+               for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+                   next_nb_i = nb_i->next;
+                   for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+                       if (nb_i->addr == nb_n->addr) {
+                           if (nb_i->metric != nb_n->metric
+                               || nb_i->threshold != nb_i->threshold)
+                               log(LOG_WARNING, 0,
+                                   "inconsistent %s for neighbor %s of %s",
+                                   "metric/threshold",
+                                   inet_fmt(nb_i->addr, s1),
+                                   inet_fmt(node->addr, s2));
+                           free(nb_i);
+                           break;
+                       }
+                   if (!nb_n) { /* no match for this neighbor yet */
+                       nb_i->next = ifc_n->neighbors;
+                       ifc_n->neighbors = nb_i;
+                   }
+               }
+
+               next_ifc_i = ifc_i->next;
+               free(ifc_i);
+           }
+
+           ifc_node->tries = -1;
+           ifc_node->u.alias = node;
+       }
+       
+       ifc = find_interface(ifc_addr, node);
+       old_neighbors = ifc->neighbors;
+       
+       /* Add the neighbors for this interface */
+       while (ncount--) {
+           u_long      neighbor;
+           Neighbor   *nb;
+           Node       *n_node;
+
+           if (datalen < 4) {
+               log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+                   inet_fmt(src, s1));
+               return;
+           }
+
+           GET_ADDR(neighbor);
+           neighbor = htonl(neighbor);
+           datalen -= 4;
+
+           for (nb = old_neighbors; nb; nb = nb->next)
+               if (nb->addr == neighbor) {
+                   if (metric != nb->metric || threshold != nb->threshold)
+                       log(LOG_WARNING, 0,
+                           "inconsistent %s for neighbor %s of %s",
+                           "metric/threshold",
+                           inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+                   goto next_neighbor;
+               }
+
+           nb = (Neighbor *) malloc(sizeof(Neighbor));
+           nb->next = ifc->neighbors;
+           ifc->neighbors = nb;
+           nb->addr = neighbor;
+           nb->metric = metric;
+           nb->threshold = threshold;
+           nb->flags = 0;
+
+           n_node = find_node(neighbor, &routers);
+           if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
+               ask(neighbor);
+               n_node->tries = 1;
+           }
+
+         next_neighbor: ;
+       }
+    }
+}
+
+void accept_neighbors2(src, dst, p, datalen)
+    u_long src, dst;
+    u_char *p;
+    int datalen;
+{
+    Node       *node = find_node(src, &routers);
+
+    if (node->tries == 0)      /* Never heard of 'em; must have hit them at */
+       node->tries = 1;        /* least once, though...*/
+    else if (node->tries == -1)        /* follow alias link */
+       node = node->u.alias;
+
+    while (datalen > 0) {      /* loop through interfaces */
+       u_long          ifc_addr;
+       u_char          metric, threshold, ncount, flags;
+       Node           *ifc_node;
+       Interface      *ifc;
+       Neighbor       *old_neighbors;
+
+       if (datalen < 4 + 4) {
+           log(LOG_WARNING, 0, "received truncated interface record from %s",
+               inet_fmt(src, s1));
+           return;
+       }
+
+       ifc_addr = *(u_long*)p;
+       p += 4;
+       metric = *p++;
+       threshold = *p++;
+       flags = *p++;
+       ncount = *p++;
+       datalen -= 4 + 4;
+
+       /* Fix up any alias information */
+       ifc_node = find_node(ifc_addr, &routers);
+       if (ifc_node->tries == 0) { /* new node */
+           ifc_node->tries = -1;
+           ifc_node->u.alias = node;
+       } else if (ifc_node != node
+                  && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
+           /* must merge two hosts' nodes */
+           Interface  *ifc_i, *next_ifc_i;
+
+           if (ifc_node->tries == -1) {
+               Node *tmp = ifc_node->u.alias;
+
+               ifc_node->u.alias = node;
+               ifc_node = tmp;
+           }
+
+           /* Merge ifc_node (foo_i) into node (foo_n) */
+
+           if (ifc_node->tries > node->tries)
+               node->tries = ifc_node->tries;
+
+           for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+               Neighbor *nb_i, *next_nb_i, *nb_n;
+               Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+               old_neighbors = ifc_n->neighbors;
+               for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+                   next_nb_i = nb_i->next;
+                   for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+                       if (nb_i->addr == nb_n->addr) {
+                           if (nb_i->metric != nb_n->metric
+                               || nb_i->threshold != nb_i->threshold)
+                               log(LOG_WARNING, 0,
+                                   "inconsistent %s for neighbor %s of %s",
+                                   "metric/threshold",
+                                   inet_fmt(nb_i->addr, s1),
+                                   inet_fmt(node->addr, s2));
+                           free(nb_i);
+                           break;
+                       }
+                   if (!nb_n) { /* no match for this neighbor yet */
+                       nb_i->next = ifc_n->neighbors;
+                       ifc_n->neighbors = nb_i;
+                   }
+               }
+
+               next_ifc_i = ifc_i->next;
+               free(ifc_i);
+           }
+
+           ifc_node->tries = -1;
+           ifc_node->u.alias = node;
+       }
+       
+       ifc = find_interface(ifc_addr, node);
+       old_neighbors = ifc->neighbors;
+       
+       /* Add the neighbors for this interface */
+       while (ncount--) {
+           u_long      neighbor;
+           Neighbor   *nb;
+           Node       *n_node;
+
+           if (datalen < 4) {
+               log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+                   inet_fmt(src, s1));
+               return;
+           }
+
+           neighbor = *(u_long*)p;
+           p += 4;
+           datalen -= 4;
+           if (neighbor == 0)
+               /* make leaf nets point to themselves */
+               neighbor = ifc_addr;
+
+           for (nb = old_neighbors; nb; nb = nb->next)
+               if (nb->addr == neighbor) {
+                   if (metric != nb->metric || threshold != nb->threshold)
+                       log(LOG_WARNING, 0,
+                           "inconsistent %s for neighbor %s of %s",
+                           "metric/threshold",
+                           inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+                   goto next_neighbor;
+               }
+
+           nb = (Neighbor *) malloc(sizeof(Neighbor));
+           nb->next = ifc->neighbors;
+           ifc->neighbors = nb;
+           nb->addr = neighbor;
+           nb->metric = metric;
+           nb->threshold = threshold;
+           nb->flags = flags | NF_PRESENT;
+
+           n_node = find_node(neighbor, &routers);
+           if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
+               ask(neighbor);
+               n_node->tries = 1;
+           }
+
+         next_neighbor: ;
+       }
+    }
+}
+
+
+void check_vif_state()
+{
+    log(LOG_NOTICE, 0, "network marked down...");
+}
+
+
+int retry_requests(node)
+    Node *node;
+{
+    int        result;
+
+    if (node) {
+       result = retry_requests(node->left);
+       if (node->tries > 0  &&  node->tries < retries) {
+           if (node->version)
+               ask2(node->addr);
+           else
+               ask(node->addr);
+           node->tries++;
+           result = 1;
+       }
+       return retry_requests(node->right) || result;
+    } else
+       return 0;
+}
+
+
+char *inet_name(addr)
+    u_long addr;
+{
+    struct hostent *e;
+
+    e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+    return e ? e->h_name : 0;
+}
+
+
+void print_map(node)
+    Node *node;
+{
+    if (node) {
+       char *name, *addr;
+       
+       print_map(node->left);
+
+       addr = inet_fmt(node->addr, s1);
+       if (!target_addr
+           || (node->tries >= 0 && node->u.interfaces)
+           || (node->tries == -1
+               && node->u.alias->tries >= 0
+               && node->u.alias->u.interfaces)) {
+           if (show_names && (name = inet_name(node->addr)))
+               printf("%s (%s):", addr, name);
+           else
+               printf("%s:", addr);
+           if (node->tries < 0)
+               printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1));
+           else if (!node->u.interfaces)
+               printf(" no response to query\n\n");
+           else {
+               Interface *ifc;
+
+               if (node->version)
+                   printf(" <v%d.%d>", node->version & 0xff,
+                                       (node->version >> 8) & 0xff);
+               printf("\n");
+               for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+                   Neighbor *nb;
+                   char *ifc_name = inet_fmt(ifc->addr, s1);
+                   int ifc_len = strlen(ifc_name);
+                   int count = 0;
+
+                   printf("    %s:", ifc_name);
+                   for (nb = ifc->neighbors; nb; nb = nb->next) {
+                       if (count > 0)
+                           printf("%*s", ifc_len + 5, "");
+                       printf("  %s", inet_fmt(nb->addr, s1));
+                       if (show_names  &&  (name = inet_name(nb->addr)))
+                           printf(" (%s)", name);
+                       printf(" [%d/%d", nb->metric, nb->threshold);
+                       if (nb->flags) {
+                           u_short flags = nb->flags;
+                           if (flags & DVMRP_NF_TUNNEL)
+                                   printf("/tunnel");
+                           if (flags & DVMRP_NF_SRCRT)
+                                   printf("/srcrt");
+                           if (flags & DVMRP_NF_QUERIER)
+                                   printf("/querier");
+                           if (flags & DVMRP_NF_DISABLED)
+                                   printf("/disabled");
+                           if (flags & DVMRP_NF_DOWN)
+                                   printf("/down");
+                       }
+                        printf("]\n");
+                       count++;
+                   }
+               }
+               printf("\n");
+           }
+       }
+       print_map(node->right);
+    }
+}
+
+
+char *graph_name(addr, buf)
+    u_long addr;
+    char *buf;
+{
+    char *name;
+
+    if (show_names  &&  (name = inet_name(addr)))
+       strcpy(buf, name);
+    else
+       inet_fmt(addr, buf);
+
+    return buf;
+}
+
+
+void graph_edges(node)
+    Node *node;
+{
+    Interface *ifc;
+    Neighbor *nb;
+    char name[100];
+
+    if (node) {
+       graph_edges(node->left);
+       if (node->tries >= 0) {
+           printf("  %d {$ NP %d0 %d0 $} \"%s%s\" \n",
+                  (int) node->addr,
+                  node->addr & 0xFF, (node->addr >> 8) & 0xFF,
+                  graph_name(node->addr, name),
+                  node->u.interfaces ? "" : "*");
+           for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+               for (nb = ifc->neighbors; nb; nb = nb->next) {
+                   Node *nb_node = find_node(nb->addr, &routers);
+                   Neighbor *nb2;
+
+                   if (nb_node->tries < 0)
+                       nb_node = nb_node->u.alias;
+
+                   if (node != nb_node &&
+                       (!(nb2 = find_neighbor(node->addr, nb_node))
+                        || node->addr < nb_node->addr)) {
+                       printf("    %d \"%d/%d",
+                              nb_node->addr, nb->metric, nb->threshold);
+                       if (nb2 && (nb2->metric != nb->metric
+                                   || nb2->threshold != nb->threshold))
+                           printf(",%d/%d", nb2->metric, nb2->threshold);
+                       if (nb->flags & NF_PRESENT)
+                           printf("%s%s",
+                                  nb->flags & DVMRP_NF_SRCRT ? "" :
+                                  nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
+                                  nb->flags & DVMRP_NF_DOWN ? "D" : "");
+                       printf("\"\n");
+                   }
+               }
+           printf("    ;\n");
+       }
+       graph_edges(node->right);
+    }
+}
+
+void elide_aliases(node)
+    Node *node;
+{
+    if (node) {
+       elide_aliases(node->left);
+       if (node->tries >= 0) {
+           Interface *ifc;
+
+           for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+               Neighbor *nb;
+
+               for (nb = ifc->neighbors; nb; nb = nb->next) {
+                   Node *nb_node = find_node(nb->addr, &routers);
+
+                   if (nb_node->tries < 0)
+                       nb->addr = nb_node->u.alias->addr;
+               }
+           }
+       }
+       elide_aliases(node->right);
+    }
+}
+
+void graph_map()
+{
+    time_t now = time(0);
+    char *nowstr = ctime(&now);
+
+    nowstr[24] = '\0';         /* Kill the newline at the end */
+    elide_aliases(routers);
+    printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
+          nowstr);
+    graph_edges(routers);
+    printf("END\n");
+}
+
+
+int get_number(var, deflt, pargv, pargc)
+    int *var, *pargc, deflt;
+    char ***pargv;
+{
+    if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
+       if (*pargc > 1  &&  isdigit((*pargv)[1][0])) {
+           (*pargv)++, (*pargc)--;
+           *var = atoi((*pargv)[0]);
+           return 1;
+       } else if (deflt >= 0) {
+           *var = deflt;
+           return 1;
+       } else
+           return 0;
+    } else {                   /* Get value from the rest of this argument */
+       if (isdigit((*pargv)[0][2])) {
+           *var = atoi((*pargv)[0] + 2);
+           return 1;
+       } else {
+           return 0;
+       }
+    }
+}
+
+
+u_long host_addr(name)
+    char *name;
+{
+    struct hostent *e = gethostbyname(name);
+    int addr;
+
+    if (e)
+       memcpy(&addr, e->h_addr_list[0], e->h_length);
+    else {
+       addr = inet_addr(name);
+       if (addr == -1)
+           addr = 0;
+    }
+
+    return addr;
+}
+
+
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    int flood = FALSE, graph = FALSE;
+    
+#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][0] == '-') {
+       switch (argv[0][1]) {
+         case 'd':
+           if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+               goto usage;
+           break;
+         case 'f':
+           flood = TRUE;
+           break;
+         case 'g':
+           graph = TRUE;
+           break;
+         case 'n':
+           show_names = FALSE;
+           break;
+         case 'r':
+           if (!get_number(&retries, -1, &argv, &argc))
+               goto usage;
+           break;
+         case 't':
+           if (!get_number(&timeout, -1, &argv, &argc))
+               goto usage;
+           break;
+         default:
+           goto usage;
+       }
+       argv++, argc--;
+    }
+
+    if (argc > 1) {
+      usage:   
+       fprintf(stderr,
+               "Usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n",
+               "[-r retries] [-d [debug-level]] [router]");
+        fprintf(stderr, "\t-f  Flood the routing graph with queries\n");
+        fprintf(stderr, "\t    (True by default unless `router' is given)\n");
+        fprintf(stderr, "\t-g  Generate output in GraphEd format\n");
+        fprintf(stderr, "\t-n  Don't look up DNS names for routers\n");
+       exit(1);
+    } else if (argc == 1 && !(target_addr = host_addr(argv[0]))) {
+       fprintf(stderr, "Unknown host: %s\n", argv[0]);
+       exit(2);
+    }
+
+    if (debug)
+       fprintf(stderr, "Debug level %u\n", debug);
+
+    init_igmp();
+
+    {                          /* Find a good local address for us. */
+       int udp;
+       struct sockaddr_in addr;
+       int addrlen = sizeof(addr);
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = dvmrp_group;
+       addr.sin_port = htons(2000); /* any port over 1024 will do... */
+       if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+           || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+           || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
+           perror("Determining local address");
+           exit(-1);
+       }
+       close(udp);
+       our_addr = addr.sin_addr.s_addr;
+    }
+
+    /* Send initial seed message to all local routers */
+    ask(target_addr ? target_addr : allhosts_group);
+
+    if (target_addr) {
+       Node *n = find_node(target_addr, &routers);
+
+       n->tries = 1;
+
+       if (flood)
+           target_addr = 0;
+    }
+
+    /* Main receive loop */
+    for(;;) {
+       fd_set          fds;
+       struct timeval  tv;
+       int             count, recvlen, dummy = 0;
+
+       FD_ZERO(&fds);
+       FD_SET(igmp_socket, &fds);
+
+       tv.tv_sec = timeout;
+       tv.tv_usec = 0;
+
+       count = select(igmp_socket + 1, &fds, 0, 0, &tv);
+
+       if (count < 0) {
+           if (errno != EINTR)
+               perror("select");
+           continue;
+       } else if (count == 0) {
+           log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+           if (retry_requests(routers))
+               continue;
+           else
+               break;
+       }
+
+       recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
+                          0, NULL, &dummy);
+       if (recvlen >= 0)
+           accept_igmp(recvlen);
+       else if (errno != EINTR)
+           perror("recvfrom");
+    }
+
+    printf("\n");
+
+    if (graph)
+       graph_map();
+    else {
+       if (!target_addr)
+           printf("Multicast Router Connectivity:\n\n");
+       print_map(routers);
+    }
+
+    exit(0);
+}
diff --git a/sbin/mrouted/mrinfo.c b/sbin/mrouted/mrinfo.c
new file mode 100644 (file)
index 0000000..ee94b6b
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * This tool requests configuration info from a multicast router
+ * and prints the reply (if any).  Invoke it as:
+ *
+ *     mrinfo router-name-or-address
+ *
+ * Written Wed Mar 24 1993 by Van Jacobson (adapted from the
+ * multicast mapper written by Pavel Curtis).
+ *
+ * The lawyers insist we include the following UC copyright notice.
+ * The mapper from which this is derived contained a Xerox copyright
+ * notice which follows the UC one.  Try not to get depressed noting
+ * that the legal gibberish is larger than the program.
+ *
+ * Copyright (c) 1993 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the Computer Systems
+ *     Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ---------------------------------
+ * Copyright (c) Xerox Corporation 1992. All rights reserved.
+ * 
+ * License is granted to copy, to use, and to make and to use derivative works
+ * for research and evaluation purposes, provided that Xerox is acknowledged
+ * in all documentation pertaining to any such copy or derivative work. Xerox
+ * grants no other licenses expressed or implied. The Xerox trade name should
+ * not be used in any advertising without its written permission.
+ * 
+ * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR
+ * ANY PARTICULAR PURPOSE.  The software is provided "as is" without express
+ * or implied warranty of any kind.
+ * 
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#ifndef lint
+static char rcsid[] =
+    "@(#) $Id: mrinfo.c,v 1.3 1993/06/24 05:11:16 deering Exp $";
+/*  original rcsid:
+    "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
+*/
+#endif
+
+#include <netdb.h>
+#include <sys/time.h>
+#include "defs.h"
+
+#define DEFAULT_TIMEOUT        4       /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 3      /* How many times to ask each router */
+
+u_long  our_addr, target_addr = 0;     /* in NET order */
+int     debug = 0;
+int     retries = DEFAULT_RETRIES;
+int     timeout = DEFAULT_TIMEOUT;
+int    target_level;
+
+char   *
+inet_name(addr)
+       u_long  addr;
+{
+       struct hostent *e;
+
+       if (addr == 0)
+               return "local";
+
+       e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+       return e ? e->h_name : "?";
+}
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level.  For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+void 
+log(severity, syserr, format, a, b, c, d, e)
+       int     severity, syserr;
+       char   *format;
+       int     a, b, c, d, e;
+{
+       char    fmt[100];
+
+       switch (debug) {
+       case 0:
+               if (severity > LOG_WARNING)
+                       return;
+       case 1:
+               if (severity > LOG_NOTICE)
+                       return;
+       case 2:
+               if (severity > LOG_INFO)
+                       return;
+       default:
+               fmt[0] = '\0';
+               if (severity == LOG_WARNING)
+                       strcat(fmt, "warning - ");
+               strncat(fmt, format, 80);
+               fprintf(stderr, fmt, a, b, c, d, e);
+               if (syserr == 0)
+                       fprintf(stderr, "\n");
+               else if (syserr < sys_nerr)
+                       fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+               else
+                       fprintf(stderr, ": errno %d\n", syserr);
+       }
+
+       if (severity <= LOG_ERR)
+               exit(-1);
+}
+
+/*
+ * Send a neighbors-list request.
+ */
+void 
+ask(dst)
+       u_long  dst;
+{
+       send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+                       htonl(MROUTED_LEVEL), 0);
+}
+
+void 
+ask2(dst)
+       u_long  dst;
+{
+       send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+                       htonl(MROUTED_LEVEL), 0);
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void 
+accept_neighbors(src, dst, p, datalen)
+       u_long  src, dst;
+       u_char *p;
+       int     datalen;
+{
+       u_char *ep = p + datalen;
+#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
+                    a += ((u_long)*p++ << 8), a += *p++)
+
+       printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
+       while (p < ep) {
+               register u_long laddr;
+               register u_char metric;
+               register u_char thresh;
+               register int ncount;
+
+               GET_ADDR(laddr);
+               laddr = htonl(laddr);
+               metric = *p++;
+               thresh = *p++;
+               ncount = *p++;
+               while (--ncount >= 0) {
+                       register u_long neighbor;
+                       GET_ADDR(neighbor);
+                       neighbor = htonl(neighbor);
+                       printf("  %s -> ", inet_fmt(laddr, s1));
+                       printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
+                              inet_name(neighbor), metric, thresh);
+               }
+       }
+}
+
+void 
+accept_neighbors2(src, dst, p, datalen)
+       u_long  src, dst;
+       u_char *p;
+       int     datalen;
+{
+       u_char *ep = p + datalen;
+
+       printf("%s (%s) [version %d.%d]:\n", inet_fmt(src, s1), inet_name(src),
+              target_level & 0xff, (target_level >> 8) & 0xff);
+       while (p < ep) {
+               register u_char metric;
+               register u_char thresh;
+               register u_char flags;
+               register int ncount;
+               register u_long laddr = *(u_long*)p;
+
+               p += 4;
+               metric = *p++;
+               thresh = *p++;
+               flags = *p++;
+               ncount = *p++;
+               while (--ncount >= 0) {
+                       register u_long neighbor = *(u_long*)p;
+                       p += 4;
+                       printf("  %s -> ", inet_fmt(laddr, s1));
+                       printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
+                              inet_name(neighbor), metric, thresh);
+                       if (flags & DVMRP_NF_TUNNEL)
+                               printf("/tunnel");
+                       if (flags & DVMRP_NF_SRCRT)
+                               printf("/srcrt");
+                       if (flags & DVMRP_NF_QUERIER)
+                               printf("/querier");
+                       if (flags & DVMRP_NF_DISABLED)
+                               printf("/disabled");
+                       if (flags & DVMRP_NF_DOWN)
+                               printf("/down");
+                       printf("]\n");
+               }
+       }
+}
+
+int 
+get_number(var, deflt, pargv, pargc)
+       int    *var, *pargc, deflt;
+       char ***pargv;
+{
+       if ((*pargv)[0][2] == '\0') {   /* Get the value from the next
+                                        * argument */
+               if (*pargc > 1 && isdigit((*pargv)[1][0])) {
+                       (*pargv)++, (*pargc)--;
+                       *var = atoi((*pargv)[0]);
+                       return 1;
+               } else if (deflt >= 0) {
+                       *var = deflt;
+                       return 1;
+               } else
+                       return 0;
+       } else {                /* Get value from the rest of this argument */
+               if (isdigit((*pargv)[0][2])) {
+                       *var = atoi((*pargv)[0] + 2);
+                       return 1;
+               } else {
+                       return 0;
+               }
+       }
+}
+
+u_long 
+host_addr(name)
+       char   *name;
+{
+       struct hostent *e = gethostbyname(name);
+       int     addr;
+
+       if (e)
+               memcpy(&addr, e->h_addr_list[0], e->h_length);
+       else {
+               addr = inet_addr(name);
+               if (addr == -1)
+                       addr = 0;
+       }
+
+       return addr;
+}
+
+void
+usage()
+{
+       fprintf(stderr, "Usage: mrinfo [-t timeout] [-r retries] router\n");
+       exit(1);
+}
+
+main(argc, argv)
+       int     argc;
+       char   *argv[];
+{
+       setlinebuf(stderr);
+
+       if (geteuid() != 0) {
+               fprintf(stderr, "mrinfo: must be root\n");
+               exit(1);
+       }
+       argv++, argc--;
+       while (argc > 0 && argv[0][0] == '-') {
+               switch (argv[0][1]) {
+               case 'd':
+                       if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+                               usage();
+                       break;
+               case 'r':
+                       if (!get_number(&retries, -1, &argv, &argc))
+                               usage();
+                       break;
+               case 't':
+                       if (!get_number(&timeout, -1, &argv, &argc))
+                               usage();
+                       break;
+               default:
+                       usage();
+               }
+               argv++, argc--;
+       }
+       if (argc != 1)
+               usage();
+
+       target_addr = host_addr(argv[0]);
+       if (target_addr == 0) {
+               fprintf(stderr, "mrinfo: %s: no such host\n", argv[0]);
+               exit(1);
+       }
+       if (debug)
+               fprintf(stderr, "Debug level %u\n", debug);
+
+       init_igmp();
+
+       {                       /* Find a good local address for us. */
+               int     udp;
+               struct sockaddr_in addr;
+               int     addrlen = sizeof(addr);
+
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = target_addr;
+               addr.sin_port = htons(2000);    /* any port over 1024 will
+                                                * do... */
+               if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+               || connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0
+                   || getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0) {
+                       perror("Determining local address");
+                       exit(-1);
+               }
+               close(udp);
+               our_addr = addr.sin_addr.s_addr;
+       }
+
+       ask(target_addr);
+
+       /* Main receive loop */
+       for (;;) {
+               fd_set  fds;
+               struct timeval tv;
+               int     count, recvlen, dummy = 0;
+               register u_long src, dst, group;
+               struct ip *ip;
+               struct igmp *igmp;
+               int     ipdatalen, iphdrlen, igmpdatalen;
+
+               FD_ZERO(&fds);
+               FD_SET(igmp_socket, &fds);
+
+               tv.tv_sec = timeout;
+               tv.tv_usec = 0;
+
+               count = select(igmp_socket + 1, &fds, 0, 0, &tv);
+
+               if (count < 0) {
+                       if (errno != EINTR)
+                               perror("select");
+                       continue;
+               } else if (count == 0) {
+                       log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+                       if (--retries < 0)
+                               exit(1);
+                       if (target_level == 0)
+                               ask(target_addr);
+                       else
+                               ask2(target_addr);
+                       continue;
+               }
+               recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
+                                  0, NULL, &dummy);
+               if (recvlen <= 0) {
+                       if (recvlen && errno != EINTR)
+                               perror("recvfrom");
+                       continue;
+               }
+
+               if (recvlen < sizeof(struct ip)) {
+                       log(LOG_WARNING, 0,
+                           "packet too short (%u bytes) for IP header",
+                           recvlen);
+                       continue;
+               }
+               ip = (struct ip *) recv_buf;
+               src = ip->ip_src.s_addr;
+               if (src != target_addr) {
+                       fprintf(stderr, "mrinfo: got reply from %s",
+                               inet_fmt(src, s1));
+                       fprintf(stderr, " instead of %s\n",
+                               inet_fmt(target_addr, s1));
+                       continue;
+               }
+               dst = ip->ip_dst.s_addr;
+               iphdrlen = ip->ip_hl << 2;
+               ipdatalen = ip->ip_len;
+               if (iphdrlen + ipdatalen != recvlen) {
+                       log(LOG_WARNING, 0,
+                           "packet shorter (%u bytes) than hdr+data length (%u+%u)",
+                           recvlen, iphdrlen, ipdatalen);
+                       continue;
+               }
+               igmp = (struct igmp *) (recv_buf + iphdrlen);
+               group = igmp->igmp_group.s_addr;
+               igmpdatalen = ipdatalen - IGMP_MINLEN;
+               if (igmpdatalen < 0) {
+                       log(LOG_WARNING, 0,
+                           "IP data field too short (%u bytes) for IGMP, from %s",
+                           ipdatalen, inet_fmt(src, s1));
+                       continue;
+               }
+               if (igmp->igmp_type != IGMP_DVMRP)
+                       continue;
+
+               switch (igmp->igmp_code) {
+
+               case DVMRP_NEIGHBORS:
+                       if (group) {
+                               /* knows about DVMRP_NEIGHBORS2 msg */
+                               if (target_level == 0) {
+                                       target_level = ntohl(group);
+                                       ask2(target_addr);
+                               }
+                       } else {
+                               accept_neighbors(src, dst, (char *)(igmp + 1),
+                                                igmpdatalen);
+                               exit(0);
+                       }
+                       break;
+
+               case DVMRP_NEIGHBORS2:
+                       accept_neighbors2(src, dst, (char *)(igmp + 1),
+                                         igmpdatalen);
+                       exit(0);
+               }
+       }
+}
+
+/* dummies */
+void accept_probe()
+{
+}
+void accept_group_report()
+{
+}
+void accept_neighbor_request2()
+{
+}
+void accept_report()
+{
+}
+void accept_neighbor_request()
+{
+}
+void check_vif_state()
+{
+}
diff --git a/sbin/mrouted/mrouted.8 b/sbin/mrouted/mrouted.8
new file mode 100644 (file)
index 0000000..5013bc7
--- /dev/null
@@ -0,0 +1,242 @@
+'\"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 \-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
+forwarding algorithm called Truncated Reverse Path Broadcasting (TRPB).
+.PP
+.I Mrouted 
+forwards a multicast datagram along a shortest (reverse) path tree
+rooted at the subnet on which the datagram originates.  It is a 
+.I broadcast
+tree, which means it includes 
+.I all
+subnets reachable by a cooperating set of 
+.I mrouted
+routers.  However, the datagram will not be forwarded onto
+.I leaf
+subnets of the tree if those subnets do not have members of the destination
+group.  Furthermore, the IP time-to-live of a multicast datagram may prevent
+it from being forwarded along the entire tree.
+.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 inserted on entry to a tunnel, and stripped out
+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
+encapsulate using IP source routing, which puts a heavy load on some
+types of routers.
+This version supports IP source route encapsulation only for backwards 
+compatibility.
+.PP
+The tunnel 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
+or inter-operation with other multicast routing protocols.
+.PP
+.I Mrouted 
+handles multicast routing only; there may or may not be a unicast
+router running on the same host 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.
+.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 two types of configuration command:
+.nf
+
+    phyint <local-addr>   [disable]   [metric <m>] [threshold <t>]
+
+    tunnel <local-addr> <remote-addr> [metric <m>] [threshold <t>] [srcrt]
+
+.fi
+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.
+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 ends before it will be used.
+For backwards compatibility with older
+.IR mrouted s,
+the srcrt keyword specifies 
+encapsulation using IP source routing.
+.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.  When in doubt, the following metrics are recommended:
+.ne 5
+.IP 1
+LAN, or tunnel across a single LAN
+.IP 2
+serial link, or tunnel across a single serial link
+.IP 3
+multi-hop tunnel
+.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.
+Suggested thresholds:
+.IP 32    
+for links that separate sites,
+.IP 64    
+for links that separate regions,
+.IP 128    
+for links that separate continents.
+.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
+.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 SIGNALS
+.PP
+.I Mrouted 
+responds to the following signals:
+.IP HUP
+.sp -.5v
+.IP TERM
+.sp -.5v
+.IP INT
+terminates execution gracefully (i.e., by sending
+good-bye messages to all neighboring routers).
+.IP USR1
+dumps the internal routing tables to /usr/tmp/mrouted.dump.
+.IP QUIT
+dumps the internal routing tables to stderr (only if
+.I mrouted 
+was invoked with a non-zero debug level). 
+.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
+  1   36.11.0.1     subnet: 36.11         1       1    querier
+                    groups: 224.0.2.1      
+                            224.0.1.0      
+                            224.0.0.4      
+  2   36.2.0.8      tunnel: 36.8.0.77     3       1   
+                    peers : 36.8.0.77      
+  3   36.2.0.8      tunnel: 36.8.0.110    3       1   
+
+Multicast Routing Table
+ Origin-Subnet   From-Gateway    Metric  In-Vif  Out-Vifs
+ 36.2                               1       0    1* 2  3*
+ 36.8            36.8.0.77          4       2    0* 1* 3*
+ 36.11                              1       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.
+.PP
+Associated with each subnet from which a multicast datagram can originate
+is the address of the previous hop gateway (unless the subnet is directly-
+connected), the metric of the path back to the origin, 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.
+.SH FILES
+/etc/mrouted.conf
+.SH SEE ALSO
+TRPB 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 AUTHOR
+Steve Deering
diff --git a/sbin/mrouted/mrouted.conf b/sbin/mrouted/mrouted.conf
new file mode 100644 (file)
index 0000000..8f3135d
--- /dev/null
@@ -0,0 +1,15 @@
+# $Id: mrouted.conf,v 1.3 1993/05/30 02:10:11 deering Exp $
+#
+#   This is the configuration file for "mrouted", an IP multicast router.
+#   mrouted looks for it in "/etc/mrouted.conf".
+#
+#   Command formats:
+#
+#      phyint <local-addr> [disable] [metric <m>] [threshold <t>]
+#      tunnel <local-addr> <remote-addr> [srcrt] [metric <m>] [threshold <t>]
+#
+#   any phyint commands MUST precede any tunnel commands
+#
+# See the Mbone FAQ on ftp.isi.edu for metric, thresholds and connection info.
+#
+tunnel 129.89.9.63 129.89.9.50 metric 3 threshold 64   # <-- EXAMPLE; REPLACE OR DELETE
diff --git a/sbin/mrouted/route.c b/sbin/mrouted/route.c
new file mode 100644 (file)
index 0000000..25518c1
--- /dev/null
@@ -0,0 +1,900 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: route.c,v 1.5 1993/06/24 05:11:16 deering Exp $
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * Exported variables.
+ */
+int routes_changed;                    /* 1=>some routes have changed */
+int delay_change_reports;              /* 1=>postpone change reports  */
+
+
+/*
+ * Private variables.
+ */
+static struct rtentry *routing_table;  /* pointer to list of route entries */
+static struct rtentry *rtp;            /* pointer to a route entry         */
+static unsigned 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;
+           }
+           k_update_route(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) {
+               k_del_route(r);
+               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;
+               k_update_route(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_long 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;
+               }
+               k_update_route(r);
+           }
+           else if (r->rt_subordinates[vifi] == addr) {
+               r->rt_subordinates[vifi] = 0;
+               if (v->uv_neighbors == NULL) {
+                   VIFM_SET(vifi, r->rt_leaves);
+                   k_update_route(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;
+               k_update_route(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 = (struct rtentry *)&routing_table;
+}
+
+
+/*
+ * 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_long 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);
+}
+
+
+/*
+ * Search the entire routing table, looking for an entry which conflicts
+ * with the given origin and mask, for example, an entry which has the same
+ * origin under a different mask.  If a conflicting entry is found, return
+ * a pointer to the entry preceding it (to facilitate deletion); if no
+ * conflict is found, return NULL.
+ */
+static struct rtentry *find_conflicting_route(origin, mask)
+    register u_long origin, mask;
+{
+    register struct rtentry *r, *prev_r;
+
+    for (prev_r = (struct rtentry *)&routing_table, r = routing_table;
+        r != NULL;
+        prev_r = r, r = r->rt_next ) {
+       if ((origin & r->rt_originmask) == r->rt_origin ||
+           (r->rt_origin & mask) == origin) {
+           return (prev_r);
+       }
+    }
+    return (NULL);
+}
+  
+
+/*
+ * 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_long origin, mask;
+{
+    register struct rtentry *r;
+
+    if ((r = (struct rtentry *) malloc(sizeof(struct rtentry)
+                               + (3 * 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_long *)(r + 1);
+    r->rt_subordinates = (u_long *)(r->rt_dominants + numvifs);
+    r->rt_leaf_timers  = (u_long *)(r->rt_subordinates + numvifs);
+
+    r->rt_next = rtp->rt_next;
+    rtp->rt_next = 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;
+    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_long origin, mask;
+    int metric;
+    u_long 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)) {
+       /*
+        * 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;
+       }
+
+       /*
+        * If the new origin and mask are inconsistent with an entry
+        * already in the routing table, either ignore this update
+        * (if it came from another router), or delete the conflicting
+        * entry (if the update is for a directly-connected subnet).
+        */
+       if ((prev_r = find_conflicting_route(origin, mask)) != NULL ) {
+           if (src != 0) {
+               log(LOG_WARNING, 0,
+                   "%s reports a conflicting origin (%s) and mask (%08x)",
+                   inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
+               return;
+           }
+           else {
+               r = prev_r->rt_next;
+               log(LOG_WARNING, 0,
+                  "deleting route with conflicting origin (%s), mask (%08x)",
+                  inet_fmt(r->rt_origin, s1), ntohl(r->rt_originmask));
+
+               if (r->rt_metric != UNREACHABLE) {
+                   k_del_route(r);
+               }
+               discard_route(prev_r);
+               if (rtp == r) rtp = prev_r;
+           }
+       }
+
+       /*
+        * OK, create the new routing entry.  'rtp' will be left pointing
+        * to the new entry.
+        */
+       create_route(origin, mask);
+
+       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);
+       k_add_route(r);
+       r->rt_gateway  = src;
+       r->rt_timer    = 0;
+       r->rt_metric   = adj_metric;
+       r->rt_flags   |= RTF_CHANGED;
+       routes_changed = TRUE;
+    }
+    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) {
+           k_del_route(r);
+           r->rt_timer = ROUTE_EXPIRE_TIME;
+       }
+       else if (adj_metric < r->rt_metric) {
+           if (init_children_and_leaves(r, vifi)) {
+               k_update_route(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)) {
+               k_update_route(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;
+               k_update_route(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;
+                   k_update_route(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);
+                   k_update_route(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;
+           }
+           k_update_route(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 = (struct rtentry *)&routing_table, 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){
+                           VIFM_SET(vifi, r->rt_leaves);
+                           k_update_route(r);
+                       }
+                       else {
+                           r->rt_flags |= RTF_LEAF_TIMING;
+                       }
+                   }
+               }
+           }
+       }
+       else if (r->rt_timer >= ROUTE_DISCARD_TIME) {
+           /*
+            * Time to garbage-collect the route entry.
+            */
+           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 {
+               k_del_route(r);
+               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;
+    }
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void accept_probe(src, dst)
+    u_long src, dst;
+{
+    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))
+       return;
+
+    report(ALL_ROUTES, vifi, src);
+}
+
+
+/*
+ * Process an incoming route report message.
+ */
+void accept_report(src, dst, p, datalen)
+    u_long src, dst;
+    register char *p;
+    register int datalen;
+{
+    vifi_t vifi;
+    register int width, i;
+    int metric;
+    u_long mask;
+    u_long origin;
+
+    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))
+       return;
+
+    start_route_updates();
+
+    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;
+       }
+       ((char *)&mask)[0] = 0xff;            width = 1;
+       if ((((char *)&mask)[1] = *p++) != 0) width = 2;
+       if ((((char *)&mask)[2] = *p++) != 0) width = 3;
+       if ((((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;
+
+           update_route(origin, mask, (metric & 0x7f), src, vifi);
+
+       } while (!(metric & 0x80));
+    }
+
+    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_long dst;
+{
+    register struct rtentry *r;
+    register char *p;
+    register int i;
+    int datalen;
+    int width;
+    u_long mask;
+    u_long src;
+
+    src = uvifs[vifi].uv_lcl_addr;
+
+    p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+    datalen = 0;
+    mask = 0;
+
+    for (r = routing_table; r != NULL; r = r->rt_next) {
+
+       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), 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), 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;
+}
+
+
+/*
+ * Print the contents of the routing table on file 'fp'.
+ */
+void dump_routes(fp)
+    FILE *fp;
+{
+    register struct rtentry *r;
+    register int i;
+
+    fprintf(fp,
+    "Multicast Routing Table (%u %s)\n%s",
+    nroutes, (nroutes == 1) ? "entry" : "entries",
+    " Origin-Subnet   From-Gateway    Metric  In-Vif  Out-Vifs\n");
+
+    for (r = routing_table; r != NULL; r = r->rt_next) {
+
+       fprintf(fp, " %-15s %-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, "%7u   ",
+               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");
+}
diff --git a/sbin/mrouted/route.h b/sbin/mrouted/route.h
new file mode 100644 (file)
index 0000000..2e7aa33
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: route.h,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+/*
+ * 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 singly-linked list of these structures,
+ * ordered by increasing value of rt_originmask and, secondarily, by
+ * increasing 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_long          rt_origin;         /* subnet origin of multicasts      */
+    u_long          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_long          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_long         *rt_dominants;      /* per vif dominant gateways        */
+    u_long         *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   */
+};
+
+#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/sbin/mrouted/vif.c b/sbin/mrouted/vif.c
new file mode 100644 (file)
index 0000000..16700f9
--- /dev/null
@@ -0,0 +1,782 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: vif.c,v 1.5 1993/06/24 05:11:16 deering Exp $
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * Exported variables.
+ */
+struct uvif    uvifs[MAXVIFS]; /* array of virtual interfaces */
+vifi_t         numvifs;        /* number of vifs in use       */
+int            vifs_down;      /* 1=>some interfaces are down */
+int            udp_socket;     /* Since the honkin' kernel doesn't support */
+                               /* ioctls on raw IP sockets, we need a UDP  */
+                               /* socket as well as our IGMP (raw) socket. */
+                               /* How dumb.                                */
+
+/*
+ * Forward declarations.
+ */
+static void start_vif();
+static void stop_vif();
+
+
+/*
+ * Initialize the virtual interfaces.
+ */
+void init_vifs()
+{
+    vifi_t vifi;
+    struct uvif *v;
+    int enabled_vifs, enabled_phyints;
+
+    numvifs = 0;
+    vifs_down = FALSE;
+
+    /*
+     * Configure the vifs based on the interface configuration of the
+     * the kernel and the contents of the configuration file.
+     * (Open a UDP socket for ioctl use in the config procedures.)
+     */
+    if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+       log(LOG_ERR, errno, "UDP socket");
+    config_vifs_from_kernel();
+    config_vifs_from_file();
+
+    /*
+     * Quit if there are fewer than two enabled vifs.
+     */
+    enabled_vifs    = 0;
+    enabled_phyints = 0;
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+       if (!(v->uv_flags & VIFF_DISABLED)) {
+           ++enabled_vifs;
+           if (!(v->uv_flags & VIFF_TUNNEL))
+               ++enabled_phyints;
+       }
+    }
+    if (enabled_vifs < 2)
+       log(LOG_ERR, 0, "can't forward: %s",
+           enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif");
+
+    if (enabled_phyints == 0)
+       log(LOG_WARNING, 0,
+           "no enabled interfaces, forwarding via tunnels only");
+
+    /*
+     * Start routing on all virtual interfaces that are not down or
+     * administratively disabled.
+     */
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+       if (!(v->uv_flags & VIFF_DISABLED)) {
+           if (!(v->uv_flags & VIFF_DOWN))
+               start_vif(vifi);
+           else log(LOG_INFO, 0,
+                   "%s is not yet up; vif #%u not in service",
+                   v->uv_name, vifi);
+       }
+    }
+}
+
+
+/*
+ * See if any interfaces have changed from up state to down, or vice versa,
+ * including any non-multicast-capable interfaces that are in use as local
+ * tunnel end-points.  Ignore interfaces that have been administratively
+ * disabled.
+ */
+void check_vif_state()
+{
+    register vifi_t vifi;
+    register struct uvif *v;
+    struct ifreq ifr;
+
+    vifs_down = FALSE;
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+
+       if (v->uv_flags & VIFF_DISABLED) continue;
+
+       strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ);
+       if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
+           log(LOG_ERR, errno,
+               "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+
+       if (v->uv_flags & VIFF_DOWN) {
+           if (ifr.ifr_flags & IFF_UP) {
+               v->uv_flags &= ~VIFF_DOWN;
+               start_vif(vifi);
+               log(LOG_INFO, 0,
+                   "%s has come up; vif #%u now in service",
+                   v->uv_name, vifi);
+           }
+           else vifs_down = TRUE;
+       }
+       else {
+           if (!(ifr.ifr_flags & IFF_UP)) {
+               stop_vif(vifi);
+               v->uv_flags |= VIFF_DOWN;
+               log(LOG_INFO, 0,
+                   "%s has gone down; vif #%u taken out of service",
+                   v->uv_name, vifi);
+               vifs_down = TRUE;
+           }
+       }
+    }
+}
+
+
+/*
+ * Start routing on the specified virtual interface.
+ */
+static void start_vif(vifi)
+    vifi_t vifi;
+{
+    struct uvif *v;
+    u_long src, dst;
+
+    v   = &uvifs[vifi];
+    src = v->uv_lcl_addr;
+    dst = (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr : dvmrp_group;
+
+    /*
+     * Install the interface in the kernel's vif structure.
+     */
+    k_add_vif(vifi, &uvifs[vifi]);
+
+    /*
+     * Update the existing route entries to take into account the new vif.
+     */
+    add_vif_to_routes(vifi);
+
+    if (!(v->uv_flags & VIFF_TUNNEL)) {
+       /*
+        * Join the DVMRP multicast group on the interface.
+        * (This is not strictly necessary, since the kernel promiscuously
+        * receives IGMP packets addressed to ANY IP multicast group while
+        * multicast routing is enabled.  However, joining the group allows
+        * this host to receive non-IGMP packets as well, such as 'pings'.)
+        */
+       k_join(dvmrp_group, src);
+
+       /*
+        * Install an entry in the routing table for the subnet to which
+        * the interface is connected.
+        */
+       start_route_updates();
+       update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi);
+
+       /*
+        * Until neighbors are discovered, assume responsibility for sending
+        * periodic group membership queries to the subnet.  Send the first
+        * query.
+        */
+       v->uv_flags |= VIFF_QUERIER;
+       send_igmp(src, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY, 0, 0, 0);
+    }
+
+    /*
+     * Send a probe via the new vif to look for neighbors.
+     */
+    send_igmp(src, dst, IGMP_DVMRP, DVMRP_PROBE, htonl(MROUTED_LEVEL), 0);
+}
+
+
+/*
+ * Stop routing on the specified virtual interface.
+ */
+static void stop_vif(vifi)
+    vifi_t vifi;
+{
+    struct uvif *v;
+    struct listaddr *a;
+
+    v = &uvifs[vifi];
+
+    if (!(v->uv_flags & VIFF_TUNNEL)) {
+       /*
+        * Depart from the DVMRP multicast group on the interface.
+        */
+       k_leave(dvmrp_group, v->uv_lcl_addr);
+
+       /*
+        * Update the entry in the routing table for the subnet to which
+        * the interface is connected, to take into account the interface
+        * failure.
+        */
+       start_route_updates();
+       update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi);
+
+       /*
+        * Discard all group addresses.  (No need to tell kernel;
+        * the k_del_vif() call, below, will clean up kernel state.)
+        */
+       while (v->uv_groups != NULL) {
+           a = v->uv_groups;
+           v->uv_groups = a->al_next;
+           free((char *)a);
+       }
+
+       v->uv_flags &= ~VIFF_QUERIER;
+    }
+
+    /*
+     * Update the existing route entries to take into account the vif failure.
+     */
+    delete_vif_from_routes(vifi);
+
+    /*
+     * Delete the interface from the kernel's vif structure.
+     */
+    k_del_vif(vifi);
+
+    /*
+     * Discard all neighbor addresses.
+     */
+    while (v->uv_neighbors != NULL) {
+       a = v->uv_neighbors;
+       v->uv_neighbors = a->al_next;
+       free((char *)a);
+    }
+}
+
+
+/*
+ * Find the virtual interface from which an incoming packet arrived,
+ * based on the packet's source and destination IP addresses.
+ */
+vifi_t find_vif(src, dst)
+    register u_long src;
+    register u_long dst;
+{
+    register vifi_t vifi;
+    register struct uvif *v;
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+       if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
+           if (v->uv_flags & VIFF_TUNNEL) {
+               if (src == v->uv_rmt_addr && dst == v->uv_lcl_addr)
+                   return(vifi);
+           }
+           else {
+               if ((src & v->uv_subnetmask) == v->uv_subnet &&
+                   src != v->uv_subnetbcast)
+                   return(vifi);
+           }
+       }
+    }
+    return (NO_VIF);
+}
+
+
+/*
+ * 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, 0, 0, 0);
+       }
+    }
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void accept_group_report(src, dst, group)
+    u_long src, dst, group;
+{
+    register vifi_t vifi;
+    register struct uvif *v;
+    register struct listaddr *g;
+
+    if ((vifi = find_vif(src, dst)) == NO_VIF ||
+       (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
+       log(LOG_INFO, 0,
+           "ignoring group membership report from non-adjacent host %s",
+           inet_fmt(src, s1));
+       return;
+    }
+
+    v = &uvifs[vifi];
+
+    /*
+     * Look for the group in our group list; if found, reset its timer.
+     */
+    for (g = v->uv_groups; g != NULL; g = g->al_next) {
+       if (group == g->al_addr) {
+           g->al_timer = 0;
+           break;
+       }
+    }
+
+    /*
+     * If not found, add it to the list and tell the kernel.
+     */
+    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;
+       g->al_timer  = 0;
+       g->al_next   = v->uv_groups;
+       v->uv_groups = g;
+
+       k_add_group(vifi, group);
+    }
+}
+
+
+/*
+ * Send a probe on all vifs from which no neighbors have been heard recently.
+ */
+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)) &&
+           v->uv_neighbors == NULL) {
+           send_igmp(v->uv_lcl_addr,
+                     (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
+                                                 : dvmrp_group,
+                     IGMP_DVMRP, DVMRP_PROBE, htonl(MROUTED_LEVEL), 0);
+       }
+    }
+}
+
+
+/*
+ * Send a list of all of our neighbors to the requestor, `src'.
+ */
+void accept_neighbor_request(src, dst)
+    u_long src, dst;
+{
+    vifi_t vifi;
+    struct uvif *v;
+    u_char *p, *ncount;
+    struct listaddr *la;
+    int        datalen;
+    u_long temp_addr, us, them = src;
+
+    /* Determine which of our addresses to use as the source of our response
+     * to this query.
+     */
+    if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */
+       int udp;                /* find best interface to reply on */
+       struct sockaddr_in addr;
+       int addrlen = sizeof(addr);
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = dst;
+       addr.sin_port = htons(2000); /* any port over 1024 will do... */
+       if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+           || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+           || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
+           log(LOG_WARNING, errno, "Determining local address");
+           close(udp);
+           return;
+       }
+       close(udp);
+       us = addr.sin_addr.s_addr;
+    } else                     /* query sent to us alone */
+       us = dst;       
+
+#define PUT_ADDR(a)    temp_addr = ntohl(a); \
+                       *p++ = temp_addr >> 24; \
+                       *p++ = (temp_addr >> 16) & 0xFF; \
+                       *p++ = (temp_addr >> 8) & 0xFF; \
+                       *p++ = temp_addr & 0xFF;
+
+    p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+    datalen = 0;
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+       if (v->uv_flags & VIFF_DISABLED)
+           continue;
+
+       ncount = 0;
+
+       for (la = v->uv_neighbors; la; la = la->al_next) {
+
+           /* Make sure that there's room for this neighbor... */
+           if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) {
+               send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
+                         htonl(MROUTED_LEVEL), datalen);
+               p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+               datalen = 0;
+               ncount = 0;
+           }
+
+           /* Put out the header for this neighbor list... */
+           if (ncount == 0) {
+               PUT_ADDR(v->uv_lcl_addr);
+               *p++ = v->uv_metric;
+               *p++ = v->uv_threshold;
+               ncount = p;
+               *p++ = 0;
+               datalen += 4 + 3;
+           }
+
+           PUT_ADDR(la->al_addr);
+           datalen += 4;
+           (*ncount)++;
+       }
+    }
+
+    if (datalen != 0)
+       send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(MROUTED_LEVEL),
+                 datalen);
+}
+
+/*
+ * Send a list of all of our neighbors to the requestor, `src'.
+ */
+void accept_neighbor_request2(src, dst)
+    u_long src, dst;
+{
+    vifi_t vifi;
+    struct uvif *v;
+    u_char *p, *ncount;
+    struct listaddr *la;
+    int        datalen;
+    u_long temp_addr, us, them = src;
+
+    /* Determine which of our addresses to use as the source of our response
+     * to this query.
+     */
+    if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */
+       int udp;                /* find best interface to reply on */
+       struct sockaddr_in addr;
+       int addrlen = sizeof(addr);
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = dst;
+       addr.sin_port = htons(2000); /* any port over 1024 will do... */
+       if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+           || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+           || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
+           log(LOG_WARNING, errno, "Determining local address");
+           close(udp);
+           return;
+       }
+       close(udp);
+       us = addr.sin_addr.s_addr;
+    } else                     /* query sent to us alone */
+       us = dst;       
+
+    p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+    datalen = 0;
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+       register u_short vflags = v->uv_flags;
+       register u_char rflags = 0;
+       if (vflags & VIFF_TUNNEL)
+           rflags |= DVMRP_NF_TUNNEL;
+       if (vflags & VIFF_SRCRT)
+           rflags |= DVMRP_NF_SRCRT;
+       if (vflags & VIFF_DOWN)
+           rflags |= DVMRP_NF_DOWN;
+       if (vflags & VIFF_DISABLED)
+           rflags |= DVMRP_NF_DISABLED;
+       if (vflags & VIFF_QUERIER)
+           rflags |= DVMRP_NF_QUERIER;
+       ncount = 0;
+       la = v->uv_neighbors;
+       if (la == NULL) {
+               /*
+                * include down & disabled interfaces and interfaces on
+                * leaf nets.
+                */
+               if (rflags & DVMRP_NF_TUNNEL)
+                   rflags |= DVMRP_NF_DOWN;
+               if (datalen > MAX_DVMRP_DATA_LEN - 12) {
+                   send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+                             htonl(MROUTED_LEVEL), datalen);
+                   p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+                   datalen = 0;
+               }
+               *(u_int*)p = v->uv_lcl_addr;
+               p += 4;
+               *p++ = v->uv_metric;
+               *p++ = v->uv_threshold;
+               *p++ = rflags;
+               *p++ = 1;
+               *(u_int*)p =  v->uv_rmt_addr;
+               p += 4;
+               datalen += 12;
+       } else {
+           for ( ; la; la = la->al_next) {
+               /* Make sure that there's room for this neighbor... */
+               if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) {
+                   send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+                             htonl(MROUTED_LEVEL), datalen);
+                   p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+                   datalen = 0;
+                   ncount = 0;
+               }
+               /* Put out the header for this neighbor list... */
+               if (ncount == 0) {
+                   *(u_int*)p = v->uv_lcl_addr;
+                   p += 4;
+                   *p++ = v->uv_metric;
+                   *p++ = v->uv_threshold;
+                   *p++ = rflags;
+                   ncount = p;
+                   *p++ = 0;
+                   datalen += 4 + 4;
+               }
+               *(u_int*)p = la->al_addr;
+               p += 4;
+               datalen += 4;
+               (*ncount)++;
+           }
+       }
+    }
+    if (datalen != 0)
+       send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL),
+                 datalen);
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors(src, dst, p, datalen, level)
+    u_long src, dst, level;
+    char *p;
+    int datalen;
+{
+    log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors2(src, dst, p, datalen, level)
+    u_long src, dst, level;
+    char *p;
+    int datalen;
+{
+    log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Update the neighbor entry for neighbor 'addr' on vif 'vifi'.
+ * 'msgtype' is the type of DVMRP message received from the neighbor.
+ * Return TRUE if 'addr' is a valid neighbor, FALSE otherwise.
+ */
+int update_neighbor(vifi, addr, msgtype)
+    vifi_t vifi;
+    u_long addr;
+    int msgtype;
+{
+    register struct uvif *v;
+    register struct listaddr *n;
+
+    v = &uvifs[vifi];
+
+    /*
+     * Confirm that 'addr' is a valid neighbor address on vif 'vifi'.
+     * IT IS ASSUMED that this was preceded by a call to find_vif(), which
+     * checks that 'addr' is either a valid remote tunnel endpoint or a
+     * non-broadcast address belonging to a directly-connected subnet.
+     * Therefore, here we check only that 'addr' is not our own address
+     * (due to an impostor or erroneous loopback) or an address of the form
+     * {subnet,0} ("the unknown host").  These checks are not performed in
+     * find_vif() because those types of address are acceptable for some
+     * types of IGMP message (such as group membership reports).
+     */
+    if (!(v->uv_flags & VIFF_TUNNEL) &&
+       (addr == v->uv_lcl_addr ||
+        addr == v->uv_subnet )) {
+       log(LOG_WARNING, 0,
+           "received DVMRP message from 'the unknown host' or self: %s",
+           inet_fmt(addr, s1));
+       return (FALSE);
+    }
+
+    /*
+     * If we have received a route report from a neighbor, and we believed
+     * that we had no neighbors on this vif, send a full route report to
+     * all neighbors on the vif.
+     */
+
+    if (msgtype == DVMRP_REPORT && v->uv_neighbors == NULL)
+       report(ALL_ROUTES, vifi,
+              (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group);
+
+    /*
+     * 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;
+           break;
+       }
+    }
+
+    /*
+     * If not found, add it to the list.  If the neighbor has a lower
+     * IP address than me, yield querier duties to it.
+     */
+    if (n == NULL) {
+       n = (struct listaddr *)malloc(sizeof(struct listaddr));
+       if (n == NULL)
+           log(LOG_ERR, 0, "ran out of memory");    /* fatal */
+
+       n->al_addr      = addr;
+       n->al_timer     = 0;
+       n->al_next      = v->uv_neighbors;
+       v->uv_neighbors = n;
+
+       if (!(v->uv_flags & VIFF_TUNNEL) &&
+           ntohl(addr) < ntohl(v->uv_lcl_addr))
+           v->uv_flags &= ~VIFF_QUERIER;
+    }
+
+    return (TRUE);
+}
+
+
+/*
+ * On every timer interrupt, advance the timer in each neighbor and
+ * group entry on every vif.
+ */
+void age_vifs()
+{
+    register vifi_t vifi;
+    register struct uvif *v;
+    register struct listaddr *a, *prev_a, *n;
+    register u_long addr;
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) {
+
+       for (prev_a = (struct listaddr *)&(v->uv_neighbors),
+            a = v->uv_neighbors;
+            a != NULL;
+            prev_a = a, a = a->al_next) {
+
+           if ((a->al_timer += TIMER_INTERVAL) < NEIGHBOR_EXPIRE_TIME)
+               continue;
+
+           /*
+            * Neighbor has expired; delete it from the neighbor list,
+            * delete it from the 'dominants' and 'subordinates arrays of
+            * any route entries and assume querier duties unless there is
+            * another neighbor with a lower IP address than mine.
+            */
+           addr = a->al_addr;
+           prev_a->al_next = a->al_next;
+           free((char *)a);
+           a = prev_a;
+
+           delete_neighbor_from_routes(addr, vifi);
+
+           if (!(v->uv_flags & VIFF_TUNNEL)) {
+               v->uv_flags |= VIFF_QUERIER;
+               for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
+                   if (ntohl(n->al_addr) < ntohl(v->uv_lcl_addr)) {
+                       v->uv_flags &= ~VIFF_QUERIER;
+                       break;
+                   }
+               }
+           }
+       }
+
+       for (prev_a = (struct listaddr *)&(v->uv_groups),
+            a = v->uv_groups;
+            a != NULL;
+            prev_a = a, a = a->al_next) {
+
+           if ((a->al_timer += TIMER_INTERVAL) < GROUP_EXPIRE_TIME)
+               continue;
+
+           /*
+            * Group has expired; tell kernel and delete from group list.
+            */
+           k_del_group(vifi, a->al_addr);
+
+           prev_a->al_next = a->al_next;
+           free((char *)a);
+           a = prev_a;
+       }
+    }
+}
+
+
+/*
+ * 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;
+
+    fprintf(fp,
+    "\nVirtual Interface Table\n%s",
+    " Vif  Local-Address                           Metric  Thresh  Flags\n");
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+
+       fprintf(fp, " %2u   %-15s %6s: %-15s %4u %7u   ",
+               vifi,
+               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);
+
+       if (v->uv_flags & VIFF_DOWN)     fprintf(fp, " down");
+       if (v->uv_flags & VIFF_DISABLED) fprintf(fp, " disabled");
+       if (v->uv_flags & VIFF_QUERIER)  fprintf(fp, " querier");
+       if (v->uv_flags & VIFF_SRCRT)    fprintf(fp, " src-rt");
+       fprintf(fp, "\n");
+
+       if (v->uv_neighbors != NULL) {
+           fprintf(fp, "                      peers : %-15s\n",
+                   inet_fmt(v->uv_neighbors->al_addr, s1));
+           for (a = v->uv_neighbors->al_next; a != NULL; a = a->al_next) {
+               fprintf(fp, "                              %-15s\n",
+                       inet_fmt(a->al_addr, s1));
+           }
+       }
+
+       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));
+           }
+       }
+    }
+    fprintf(fp, "\n");
+}
diff --git a/sbin/mrouted/vif.h b/sbin/mrouted/vif.h
new file mode 100644 (file)
index 0000000..8b4b46b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: vif.h,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+/*
+ * 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_char          uv_threshold;  /* min ttl required to forward on vif   */
+    u_long          uv_lcl_addr;   /* local address of this vif            */
+    u_long          uv_rmt_addr;   /* remote end-point addr (tunnels only) */
+    u_long          uv_subnet;     /* subnet number         (phyints only) */
+    u_long          uv_subnetmask; /* subnet mask           (phyints only) */
+    u_long          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          */
+};
+
+#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 */
+
+
+struct listaddr {
+    struct listaddr *al_next;          /* link to next addr, MUST BE FIRST */
+    u_long          al_addr;           /* local group or neighbor address  */
+    u_long          al_timer;          /* for timing out group or neighbor */
+};
+
+
+#define NO_VIF         ((vifi_t)MAXVIFS)  /* An invalid vif index */
diff --git a/usr.sbin/mrouted/.depend b/usr.sbin/mrouted/.depend
new file mode 100644 (file)
index 0000000..50bbd6c
--- /dev/null
@@ -0,0 +1,90 @@
+igmp.o : igmp.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+inet.o : inet.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+kern.o : kern.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+config.o : config.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+main.o : main.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+route.o : route.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+vif.o : vif.c defs.h /usr/include/stdio.h /usr/include/sys/cdefs.h /usr/include/machine/ansi.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/sys/time.h /usr/include/time.h \
+  /usr/include/net/if_arp.h /usr/include/netinet/in.h /usr/include/netinet/in_systm.h \
+  /usr/include/netinet/ip.h /usr/include/netinet/igmp.h /usr/include/netinet/ip_mroute.h \
+  dvmrp.h vif.h route.h 
+mapper.o : mapper.c /usr/include/netdb.h /usr/include/sys/cdefs.h /usr/include/sys/time.h \
+  /usr/include/time.h /usr/include/machine/ansi.h defs.h /usr/include/stdio.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/net/if_arp.h /usr/include/netinet/in.h \
+  /usr/include/netinet/in_systm.h /usr/include/netinet/ip.h /usr/include/netinet/igmp.h \
+  /usr/include/netinet/ip_mroute.h dvmrp.h vif.h route.h 
+mrinfo.o : mrinfo.c /usr/include/netdb.h /usr/include/sys/cdefs.h /usr/include/sys/time.h \
+  /usr/include/time.h /usr/include/machine/ansi.h defs.h /usr/include/stdio.h \
+  /usr/include/ctype.h /usr/include/errno.h /usr/include/syslog.h /usr/include/stdarg.h \
+  /usr/include/machine/stdarg.h /usr/include/signal.h /usr/include/machine/trap.h \
+  /usr/include/sys/types.h /usr/include/machine/types.h /usr/include/sys/param.h \
+  /usr/include/sys/syslimits.h /usr/include/sys/signal.h /usr/include/machine/param.h \
+  /usr/include/machine/endian.h /usr/include/machine/limits.h /usr/include/sys/socket.h \
+  /usr/include/sys/ioctl.h /usr/include/net/if.h /usr/include/net/if_arp.h /usr/include/netinet/in.h \
+  /usr/include/netinet/in_systm.h /usr/include/netinet/ip.h /usr/include/netinet/igmp.h \
+  /usr/include/netinet/ip_mroute.h dvmrp.h vif.h route.h 
diff --git a/usr.sbin/mrouted/LICENSE b/usr.sbin/mrouted/LICENSE
new file mode 100644 (file)
index 0000000..ef7da47
--- /dev/null
@@ -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 (file)
index 0000000..b3c1777
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# Makefile for mrouted, a multicast router, and its auxiliary programs,
+# map-mbone and mrinfo.
+#
+# $Id: Makefile,v 1.5 1993/06/24 05:11:16 deering Exp $
+#
+# Modified by: Jim Lowe for FreeBSD 5/17/94
+#
+
+IGMP_SRCS=     igmp.c inet.c kern.c
+IGMP_OBJS=     igmp.o inet.o kern.o
+ROUTER_SRCS=   config.c main.c route.c vif.c
+ROUTER_OBJS=   config.o main.o route.o vif.o
+MAPPER_SRCS=   mapper.c
+MAPPER_OBJS=   mapper.o
+MRINFO_SRCS=   mrinfo.c
+MRINFO_OBJS=   mrinfo.o
+HDRS=          defs.h dvmrp.h route.h vif.h
+SRCS= ${IGMP_SRCS} ${ROUTER_SRCS} ${MAPPER_SRCS} ${MRINFO_SRCS}
+OBJS= ${IGMP_OBJS} ${ROUTER_OBJS} ${MAPPER_OBJS} ${MRINFO_OBJS}
+
+MAN8= mrouted.8
+
+all: mrouted map-mbone mrinfo
+
+mrouted: ${IGMP_OBJS} ${ROUTER_OBJS}
+       rm -f $@
+       ${CC} ${LDFLAGS} -o $@ ${CFLAGS} ${IGMP_OBJS} ${ROUTER_OBJS}
+
+map-mbone: ${IGMP_OBJS} ${MAPPER_OBJS}
+       rm -f $@
+       ${CC} ${LDFLAGS} -o $@ ${CFLAGS} ${IGMP_OBJS} ${MAPPER_OBJS}
+
+mrinfo: ${IGMP_OBJS} ${MRINFO_OBJS}
+       rm -f $@
+       ${CC} ${LDFLAGS} -o $@ ${CFLAGS} ${IGMP_OBJS} ${MRINFO_OBJS}
+
+install: mrouted mrinfo map-mbone
+       install -c mrouted ${BINDIR}
+       install -c -o root -m 500 mrinfo ${BINDIR}
+       install -c -o root -m 500 map-mbone ${BINDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c
new file mode 100644 (file)
index 0000000..9285ccb
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: config.c,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+
+#include "defs.h"
+
+
+char *configfilename = "/etc/mrouted.conf";
+
+
+/*
+ * Forward declarations.
+ */
+static char *next_word();
+
+
+/*
+ * 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, *mp;
+    struct ifconf ifc;
+    register struct uvif *v;
+    register vifi_t vifi;
+    int i, n;
+    u_long addr, mask, subnet;
+    u_int 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.
+        */
+       addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr;
+       if (ifrp->ifr_addr.sa_family != AF_INET)
+           continue;
+
+       /*
+        * 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 (%08x)",
+               ifr.ifr_name, inet_fmt(addr, s1), ntohl(mask));
+           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_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;
+
+       log(LOG_INFO, 0, "installing %s (%s on subnet %s) as vif #%u",
+           v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2),
+           numvifs);
+
+       ++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;
+       }
+    }
+}
+
+static struct ifreq *
+ifconfaddr(ifcp, a)
+    struct ifconf *ifcp;
+    u_long 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 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);
+}
+/*
+ * Read the config file to learn about tunnel vifs and
+ * non-default phyint parameters.
+ */
+void config_vifs_from_file()
+{
+    FILE *f;
+    char linebuf[100];
+    char *w, *s, c;
+    u_long lcl_addr, rmt_addr;
+    struct ifconf ifc;
+    struct ifreq *ifr;
+    struct ifreq ffr;
+    int i;
+    u_int n;
+    struct ifreq ifbuf[32];
+    vifi_t vifi;
+    struct uvif *v;
+
+    f = fopen(configfilename, "r");
+    if (f == NULL) {
+       if (errno != ENOENT)
+           log(LOG_WARNING, 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");
+
+    while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
+
+       s = linebuf;
+       if (EQUAL((w = next_word(&s)), "")) {
+           /*
+            * blank or comment line; ignore
+            */
+       }
+
+       else if (EQUAL(w, "phyint")) {
+           /*
+            * phyint <local-addr> [disable] [metric <m>] [threshold <t>]
+            */
+
+           /*
+            * Parse the local address.
+            */
+           if (EQUAL((w = next_word(&s)), "")) {
+               log(LOG_WARNING, 0,
+                   "missing phyint address in %s",
+                   configfilename);
+               continue;
+           }
+           if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
+               !inet_valid_host(lcl_addr)) {
+               log(LOG_WARNING, 0,
+                   "invalid phyint address '%s' in %s",
+                   w, configfilename);
+               continue;
+           }
+
+           /*
+            * Look up the vif with the specified local address.
+            */
+           for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+               if (!(v->uv_flags & VIFF_TUNNEL) &&
+                   lcl_addr == v->uv_lcl_addr) {
+                   break;
+               }
+           }
+           if (vifi == numvifs) {
+               log(LOG_WARNING, 0,
+                   "phyint %s in %s is not a configured interface",
+                   inet_fmt(lcl_addr, s1), configfilename);
+               continue;
+           }
+
+           /*
+            * Look for "disable", "metric" and "threshold" options.
+            */
+           while (!EQUAL((w = next_word(&s)), "")) {
+               if (EQUAL(w, "disable")) {
+                   v->uv_flags |= VIFF_DISABLED;
+               }
+               else if (EQUAL(w, "metric")) {
+                   if(EQUAL((w = next_word(&s)), "")) {
+                       log(LOG_WARNING, 0,
+                           "missing metric for phyint %s in %s",
+                           inet_fmt(lcl_addr, s1), configfilename);
+                       w = "garbage";
+                       break;
+                   }
+                   if(sscanf(w, "%u%c", &n, &c) != 1 ||
+                             n < 1 || n >= UNREACHABLE ) {
+                       log(LOG_WARNING, 0,
+                           "invalid metric '%s' for phyint %s in %s",
+                           w, inet_fmt(lcl_addr, s1), configfilename);
+                       break;
+                   }
+                   v->uv_metric = n;
+               }
+               else if (EQUAL(w, "threshold")) {
+                   if(EQUAL((w = next_word(&s)), "")) {
+                       log(LOG_WARNING, 0,
+                           "missing threshold for phyint %s in %s",
+                           inet_fmt(lcl_addr, s1), configfilename);
+                       w = "garbage";
+                       break;
+                   }
+                   if(sscanf(w, "%u%c", &n, &c) != 1 ||
+                             n < 1 || n > 255 ) {
+                       log(LOG_WARNING, 0,
+                           "invalid threshold '%s' for phyint %s in %s",
+                           w, inet_fmt(lcl_addr, s1), configfilename);
+                       break;
+                   }
+                   v->uv_threshold = n;
+               }
+               else break;
+           }
+           if (!EQUAL(w, "")) continue;
+       }
+
+       else if (EQUAL(w, "tunnel")) {
+           /*
+            * tunnel <local-addr> <remote-addr> [srcrt] [metric <m>] [threshold <t>]
+            */
+
+           /*
+            * Parse the local address.
+            */
+           if (EQUAL((w = next_word(&s)), "")) {
+               log(LOG_WARNING, 0,
+                   "missing tunnel local address in %s",
+                   configfilename);
+               continue;
+           }
+           if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
+               !inet_valid_host(lcl_addr)) {
+               log(LOG_WARNING, 0,
+                   "invalid tunnel local address '%s' in %s",
+                   w, configfilename);
+               continue;
+           }
+
+           /*
+            * Make sure the local address is one of ours.
+            */
+           ifr = ifconfaddr(&ifc, lcl_addr);
+           if (ifr == 0) {
+               log(LOG_WARNING, 0,
+                   "tunnel local address %s in %s is not one of ours",
+                   inet_fmt(lcl_addr, s1), configfilename);
+               continue;
+           }
+
+           /*
+            * Make sure the local address doesn't name a loopback interface..
+            */
+           strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
+           if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr) < 0) {
+               log(LOG_ERR, errno,
+                   "ioctl SIOCGIFFLAGS for %s", ffr.ifr_name);
+           }
+           if (ffr.ifr_flags & IFF_LOOPBACK) {
+               log(LOG_WARNING, 0,
+                   "tunnel local address %s in %s is a loopback interface",
+                   inet_fmt(lcl_addr, s1), configfilename);
+               continue;
+           }
+
+           /*
+            * Parse the remote address.
+            */
+           if (EQUAL((w = next_word(&s)), "")) {
+               log(LOG_WARNING, 0,
+                   "missing tunnel remote address in %s",
+                   configfilename);
+               continue;
+           }
+           if ((rmt_addr = inet_parse(w)) == 0xffffffff ||
+               !inet_valid_host(rmt_addr)) {
+               log(LOG_WARNING, 0,
+                   "invalid tunnel remote address %s in %s",
+                   w, configfilename);
+               continue;
+           }
+
+           /*
+            * Make sure the remote address is not one of ours.
+            */
+           if (ifconfaddr(&ifc, rmt_addr) != 0) {
+               log(LOG_WARNING, 0,
+                   "tunnel remote address %s in %s is one of ours",
+                   inet_fmt(rmt_addr, s1), configfilename);
+               continue;
+           }
+
+           /*
+            * Make sure the remote address has not been used for another
+            * tunnel and does not belong to a subnet to which we have direct
+            * access on an enabled phyint.
+            */
+           for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+               if (v->uv_flags & VIFF_TUNNEL) {
+                   if (rmt_addr == v->uv_rmt_addr) {
+                       log(LOG_WARNING, 0,
+                           "duplicate tunnel remote address %s in %s",
+                           inet_fmt(rmt_addr, s1), configfilename);
+                       break;
+                   }
+               }
+               else if (!(v->uv_flags & VIFF_DISABLED)) {
+                   if ((rmt_addr & v->uv_subnetmask) == v->uv_subnet) {
+                       log(LOG_WARNING, 0,
+                           "unnecessary tunnel remote address %s in %s",
+                           inet_fmt(rmt_addr, s1), configfilename);
+                       break;
+                   }
+               }
+           }
+           if (vifi != numvifs) continue;
+
+           /*
+            * OK, let's initialize a uvif structure for the tunnel.
+            */
+           if (numvifs == MAXVIFS) {
+               log(LOG_WARNING, 0, "too many vifs, ignoring tunnel to %s",
+                   inet_fmt(rmt_addr, s1));
+               continue;
+           }
+           v  = &uvifs[numvifs];
+           v->uv_flags       = VIFF_TUNNEL;
+           v->uv_metric      = DEFAULT_METRIC;
+           v->uv_threshold   = DEFAULT_THRESHOLD;
+           v->uv_lcl_addr    = lcl_addr;
+           v->uv_rmt_addr    = rmt_addr;
+           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;
+
+           /*
+            * Look for "metric" and "threshold" options.
+            */
+           while (!EQUAL((w = next_word(&s)), "")) {
+               if (EQUAL(w, "metric")) {
+                   if(EQUAL((w = next_word(&s)), "")) {
+                       log(LOG_WARNING, 0,
+                           "missing metric for tunnel to %s in %s",
+                           inet_fmt(rmt_addr, s1), configfilename);
+                       w = "garbage";
+                       break;
+                   }
+                   if(sscanf(w, "%u%c", &n, &c) != 1 ||
+                             n < 1 || n >= UNREACHABLE ) {
+                       log(LOG_WARNING, 0,
+                           "invalid metric '%s' for tunnel to %s in %s",
+                           w, inet_fmt(rmt_addr, s1), configfilename);
+                       break;
+                   }
+                   v->uv_metric = n;
+               }
+               else if (EQUAL(w, "threshold")) {
+                   if(EQUAL((w = next_word(&s)), "")) {
+                       log(LOG_WARNING, 0,
+                           "missing threshold for tunnel to %s in %s",
+                           inet_fmt(rmt_addr, s1), configfilename);
+                       w = "garbage";
+                       break;
+                   }
+                   if(sscanf(w, "%u%c", &n, &c) != 1 ||
+                             n < 1 || n > 255 ) {
+                       log(LOG_WARNING, 0,
+                           "invalid threshold '%s' for tunnel to %s in %s",
+                           w, inet_fmt(rmt_addr, s1), configfilename);
+                       break;
+                   }
+                   v->uv_threshold = n;
+               }
+               else if (EQUAL(w, "srcrt") || EQUAL(w, "sourceroute")) {
+                   v->uv_flags |= VIFF_SRCRT;
+               }
+               else break;
+           }
+           if (!EQUAL(w, "")) continue;
+
+           log(LOG_INFO, 0,
+               "installing %stunnel from %s to %s as vif #%u",
+               v->uv_flags & VIFF_SRCRT? "srcrt " : "",
+               inet_fmt(lcl_addr, s1), inet_fmt(rmt_addr, s2), numvifs);
+
+           ++numvifs;
+
+           if (!(ffr.ifr_flags & IFF_UP)) {
+               v->uv_flags |= VIFF_DOWN;
+               vifs_down = TRUE;
+           }
+       }
+
+       else {
+           log(LOG_WARNING, 0,
+               "unknown command '%s' in %s", w, configfilename);
+       }
+    }
+
+    close(f);
+}
+
+
+/*
+ * Return a pointer to the next "word" in the string to which '*s' points,
+ * lower-cased and null terminated, and advance '*s' to point beyond the word.
+ * Words are separated by blanks and/or tabs, and the input string is
+ * considered to terminate at a newline, '#' (comment), or null character.
+ * If no words remain, a pointer to a null string ("") is returned.  
+ * Warning: This function clobbers the input string.
+ */
+static char *next_word(s)
+    char **s;
+{
+    char *w;
+
+    w = *s;
+    while (*w == ' ' || *w == '\t')
+       ++w;
+
+    *s = w;
+    for(;;) {
+       switch (**s) {
+
+           case ' '  :
+           case '\t' : **s = '\0';
+                       ++*s;
+                       return (w);
+
+           case '\n' :
+           case '#'  : **s = '\0';
+                       return (w);
+
+           case '\0' : return (w);
+
+           default   : if (isascii(**s) && isupper(**s))
+                           **s = tolower(**s);
+                       ++*s;
+       }
+    }
+}
diff --git a/usr.sbin/mrouted/defs.h b/usr.sbin/mrouted/defs.h
new file mode 100644 (file)
index 0000000..f01179f
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: defs.h,v 1.4 1993/06/24 05:11:16 deering Exp $
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.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 "dvmrp.h"
+#include "vif.h"
+#include "route.h"
+
+
+/*
+ * Miscellaneous constants and macros.
+ */
+#define FALSE          0
+#define TRUE           1
+
+#define EQUAL(s1, s2)  (strcmp((s1), (s2)) == 0)
+
+#define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY
+
+#define PROTOCOL_VERSION 2  /* increment when packet format/content changes */
+
+#define MROUTED_VERSION  0  /* increment on local changes or bug fixes, */
+                           /* reset to 0 whever PROTOCOL_VERSION increments */
+
+#define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION )
+                           /* for IGMP 'group' field of DVMRP messages */
+
+/*
+ * External declarations for global variables and functions.
+ */
+extern char            recv_buf[MAX_IP_PACKET_LEN];
+extern char            send_buf[MAX_IP_PACKET_LEN];
+extern int             igmp_socket;
+extern u_long          allhosts_group;
+extern u_long          dvmrp_group;
+
+#define DEFAULT_DEBUG  2       /* default if "-d" given without value */
+
+extern int             debug;
+
+extern int             routes_changed;
+extern int             delay_change_reports;
+
+extern struct uvif     uvifs[MAXVIFS];
+extern vifi_t          numvifs;
+extern int             vifs_down;
+extern int             udp_socket;
+
+extern char            s1[];
+extern char            s2[];
+extern char            s3[];
+
+extern int             errno;
+extern int             sys_nerr;
+extern char *          sys_errlist[];
+
+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            accept_probe();
+extern void            accept_report();
+extern void            report();
+extern void            report_to_all_neighbors();
+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            accept_group_report();
+extern void            query_groups();
+extern void            probe_for_neighbors();
+extern int             update_neighbor();
+extern void            accept_neighbor_request();
+
+extern void            config_vifs_from_kernel();
+extern void            config_vifs_from_file();
+
+extern int             inet_valid_host();
+extern int             inet_valid_subnet();
+extern char *          inet_fmt();
+extern char *          inet_fmts();
+extern u_long          inet_parse();
+extern int             inet_cksum();
+
+extern char *          malloc();
+extern char *          fgets();
+extern FILE *          fopen();
+
+#ifndef htonl
+extern u_long          htonl();
+extern u_long          ntohl();
+#endif
diff --git a/usr.sbin/mrouted/dvmrp.h b/usr.sbin/mrouted/dvmrp.h
new file mode 100644 (file)
index 0000000..662f1cb
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: dvmrp.h,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+/*
+ * 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
+
+/*
+ * '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_DOWN          0x10    /* kernel state of interface */
+#define DVMRP_NF_DISABLED      0x20    /* administratively disabled */
+#define DVMRP_NF_QUERIER       0x40    /* I am the subnet's querier */
+
+/*
+ * 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_long)0xe0000004    /* 224.0.0.4 */
+
+#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        190     /* 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 UNREACHABLE            32      /* "infinity" metric, must be <= 64 */
+#define DEFAULT_METRIC         1       /* default subnet/tunnel metric     */
+#define DEFAULT_THRESHOLD      1       /* default subnet/tunnel threshold  */
diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c
new file mode 100644 (file)
index 0000000..24c8099
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: igmp.c,v 1.5 1993/06/23 18:47:17 pavel Exp $
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * Exported variables.
+ */
+char           recv_buf[MAX_IP_PACKET_LEN]; /* input packet buffer         */
+char           send_buf[MAX_IP_PACKET_LEN]; /* output packet buffer        */
+int            igmp_socket;                 /* socket for all network I/O  */
+u_long         allhosts_group;              /* allhosts  addr in net order */
+u_long         dvmrp_group;                 /* DVMRP grp addr in net order */
+
+
+/*
+ * Open and initialize the igmp socket, and fill in the non-changing
+ * IP header fields in the output packet buffer.
+ */
+void init_igmp()
+{
+    struct ip *ip;
+
+    if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) 
+       log(LOG_ERR, errno, "IGMP socket");
+
+    k_hdr_include(TRUE);       /* include IP header when sending */
+    k_set_rcvbuf(48*1024);     /* lots of input buffering        */
+    k_set_ttl(1);              /* restrict multicasts to one hop */
+    k_set_loop(FALSE);         /* disable multicast loopback     */
+
+    ip         = (struct ip *)send_buf;
+    ip->ip_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);
+}
+
+static char *packet_kind(type, code)
+     u_char type, code;
+{
+    switch (type) {
+       case IGMP_HOST_MEMBERSHIP_QUERY:        return "membership query  ";
+       case IGMP_HOST_MEMBERSHIP_REPORT:       return "membership report ";
+       case IGMP_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   ";
+           default:                            return "unknown DVMRP msg ";
+         }
+       default:                                return "unknown IGMP msg  ";
+    }
+}
+
+/*
+ * Process a newly received IGMP packet that is sitting in the input
+ * packet buffer.
+ */
+void accept_igmp(recvlen)
+    int recvlen;
+{
+    register vifi_t vifi;
+    register u_long src, dst, group;
+    struct ip *ip;
+    struct igmp *igmp;
+    int ipdatalen, iphdrlen, igmpdatalen;
+
+    if (recvlen < sizeof(struct ip)) {
+       log(LOG_WARNING, 0,
+           "received packet too short (%u bytes) for IP header", recvlen);
+       return;
+    }
+
+    ip        = (struct ip *)recv_buf;
+    src       = ip->ip_src.s_addr;
+    dst       = ip->ip_dst.s_addr;
+    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:
+           return;     /* Answered automatically by the kernel. */
+
+       case IGMP_HOST_MEMBERSHIP_REPORT:
+           accept_group_report(src, dst, group);
+           return;
+
+       case IGMP_DVMRP:
+           switch (igmp->igmp_code) {
+
+               case DVMRP_PROBE:
+                   accept_probe(src, dst);
+                   return;
+
+               case DVMRP_REPORT:
+                   accept_report(src, dst,
+                                 (char *)(igmp+1), igmpdatalen);
+                   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;
+
+               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;
+           }
+
+       default:
+           log(LOG_INFO, 0,
+               "ignoring unknown IGMP message type %u from %s to %s",
+               igmp->igmp_type, inet_fmt(src, s1),
+               inet_fmt(dst, s2));
+           return;
+    }
+}
+
+
+/*
+ * Construct an IGMP message in the output packet buffer.  The caller may
+ * have already placed data in that buffer, of length 'datalen'.  Then send
+ * the message from the interface with IP address 'src' to destination 'dst'.
+ */
+void send_igmp(src, dst, type, code, group, datalen)
+    u_long src, dst;
+    int type, code;
+    u_long group;
+    int datalen;
+{
+    static struct sockaddr_in sdst = {AF_INET};
+    struct ip *ip;
+    struct igmp *igmp;
+
+    ip                      = (struct ip *)send_buf;
+    ip->ip_src.s_addr       = src;
+    ip->ip_dst.s_addr       = dst;
+    ip->ip_len              = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+
+    igmp                    = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
+    igmp->igmp_type         = type;
+    igmp->igmp_code         = code;
+    igmp->igmp_group.s_addr = group;
+    igmp->igmp_cksum        = 0;
+    igmp->igmp_cksum        = inet_cksum((u_short *)igmp,
+                                        IGMP_MINLEN + datalen);
+
+    if (IN_MULTICAST(ntohl(dst))) k_set_if(src);
+    if (dst == allhosts_group) k_set_loop(TRUE);
+
+    sdst.sin_addr.s_addr = dst;
+    if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
+                       (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
+       if (errno == ENETDOWN) check_vif_state();
+       else log(LOG_WARNING, errno, "sendto on %s", inet_fmt(src, s1));
+    }
+
+    if (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 (file)
index 0000000..5d7442b
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: inet.c,v 1.4 1993/05/30 01:36:38 deering Exp $
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * Exported variables.
+ */
+char s1[16];           /* buffers to hold the string representations  */
+char s2[16];           /* of IP addresses, to be passed to inet_fmt() */
+char s3[16];           /* or inet_fmts().                             */
+
+
+/*
+ * 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_long naddr;
+{
+    register u_long 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.
+ */
+int inet_valid_subnet(nsubnet, nmask)
+    u_long nsubnet, nmask;
+{
+    register u_long subnet, mask;
+
+    subnet = ntohl(nsubnet);
+    mask   = ntohl(nmask);
+
+    if ((subnet & mask) != subnet) return (FALSE);
+
+    if (IN_CLASSA(subnet)) {
+       if (mask < 0xff000000 ||
+          (subnet & 0xff000000) == 0 ||
+          (subnet & 0xff000000) == 0x7f000000) return (FALSE);
+    }
+    else if (IN_CLASSB(subnet)) {
+       if (mask < 0xffff0000) return (FALSE);
+    }
+    else if (IN_CLASSC(subnet)) {
+       if (mask < 0xffffff00) return (FALSE);
+    }
+    else return (FALSE);
+
+    return (TRUE);
+}
+
+
+/*
+ * Convert an IP address in u_long (network) format into a printable string.
+ */
+char *inet_fmt(addr, s)
+    u_long 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.
+ */
+char *inet_fmts(addr, mask, s)
+    u_long addr, mask;
+    char *s;
+{
+    register u_char *a, *m;
+
+    a = (u_char *)&addr;
+    m = (u_char *)&mask;
+
+    if      (m[3] != 0) sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
+    else if (m[2] != 0) sprintf(s, "%u.%u.%u",    a[0], a[1], a[2]);
+    else if (m[1] != 0) sprintf(s, "%u.%u",       a[0], a[1]);
+    else                sprintf(s, "%u",          a[0]);
+
+    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_long inet_parse(s)
+    char *s;
+{
+    u_long a;
+    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 (file)
index 0000000..548247b
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: kern.c,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+
+#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_long 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_long grp;
+    u_long 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_long grp;
+    u_long 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()
+{
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_INIT,
+                       (char *)NULL, 0) < 0)
+       log(LOG_ERR, errno, "can't enable DVMRP routing in kernel");
+}
+
+
+void k_stop_dvmrp()
+{
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DONE,
+                       (char *)NULL, 0) < 0)
+       log(LOG_WARNING, errno, "can't disable DVMRP 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_lcl_addr.s_addr = v->uv_lcl_addr;
+    vc.vifc_rmt_addr.s_addr = v->uv_rmt_addr;
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_VIF,
+                       (char *)&vc, sizeof(vc)) < 0)
+       log(LOG_ERR, errno, "setsockopt DVMRP_ADD_VIF");
+}
+
+
+void k_del_vif(vifi)
+    vifi_t vifi;
+{
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_VIF,
+                       (char *)&vifi, sizeof(vifi)) < 0)
+       log(LOG_ERR, errno, "setsockopt DVMRP_DEL_VIF");
+}
+
+
+void k_add_group(vifi, group)
+    vifi_t vifi;
+    u_long group;
+{
+    struct lgrplctl lc;
+
+    lc.lgc_vifi         = vifi;
+    lc.lgc_gaddr.s_addr = group;
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_LGRP,
+                       (char *)&lc, sizeof(lc)) < 0)
+       log(LOG_WARNING, errno, "setsockopt DVMRP_ADD_LGRP");
+}
+
+
+void k_del_group(vifi, group)
+    vifi_t vifi;
+    u_long group;
+{
+    struct lgrplctl lc;
+
+    lc.lgc_vifi         = vifi;
+    lc.lgc_gaddr.s_addr = group;
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_LGRP,
+                       (char *)&lc, sizeof(lc)) < 0)
+       log(LOG_WARNING, errno, "setsockopt DVMRP_DEL_LGRP");
+}
+
+
+void k_add_route(r)
+    struct rtentry *r;
+{
+    struct mrtctl mc;
+
+    mc.mrtc_origin.s_addr     = r->rt_origin;
+    mc.mrtc_originmask.s_addr = r->rt_originmask;
+    mc.mrtc_parent            = r->rt_parent;
+    VIFM_COPY(r->rt_children, mc.mrtc_children);
+    VIFM_COPY(r->rt_leaves,   mc.mrtc_leaves);
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_MRT,
+                       (char *)&mc, sizeof(mc)) < 0)
+       log(LOG_WARNING, errno, "setsockopt DVMRP_ADD_MRT");
+}
+
+
+void k_update_route(r)
+    struct rtentry *r;
+{
+    k_add_route(r);
+}
+
+
+void k_del_route(r)
+    struct rtentry *r;
+{
+    struct in_addr orig;
+
+    orig.s_addr = r->rt_origin;
+
+    if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_MRT,
+                       (char *)&orig, sizeof(orig)) < 0)
+       log(LOG_WARNING, errno, "setsockopt DVMRP_DEL_MRT");
+}
diff --git a/usr.sbin/mrouted/main.c b/usr.sbin/mrouted/main.c
new file mode 100644 (file)
index 0000000..6352a97
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: main.c,v 1.5 1993/06/24 05:11:16 deering Exp $
+ */
+
+/*
+ * 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"
+
+extern char *configfilename;
+
+static char pidfilename[]  = "/etc/mrouted.pid";
+static char dumpfilename[] = "/usr/tmp/mrouted.dump";
+
+static int debug = 0;
+
+
+/*
+ * Forward declarations.
+ */
+static void timer();
+static void hup();
+static void dump();
+static void fdump();
+
+
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    register int recvlen;
+    register int omask;
+    int dummy;
+    FILE *fp;
+    extern uid_t geteuid();
+
+    setlinebuf(stderr);
+
+    if (geteuid() != 0) {
+       fprintf(stderr, "mrouted: 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
+           goto usage;
+       argv++, argc--;
+    }
+
+    if (argc > 0) {
+usage: fprintf(stderr, "usage: mrouted [-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);
+       t = open("/dev/tty", 2);
+       if (t >= 0) {
+           (void)ioctl(t, TIOCNOTTY, (char *)0);
+           (void)close(t);
+       }
+    }
+    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);
+
+    fp = fopen(pidfilename, "w");
+    if (fp != NULL) {
+       fprintf(fp, "%d\n", getpid());
+       (void) fclose(fp);
+    }
+
+    srandom(gethostid());
+
+    init_igmp();
+    k_init_dvmrp();            /* enable DVMRP routing in kernel */
+    init_routes();
+    init_vifs();
+
+    if (debug >= 2) dump();
+
+    (void)signal(SIGALRM, timer);
+    (void)signal(SIGHUP,  hup);
+    (void)signal(SIGTERM, hup);
+    (void)signal(SIGINT,  hup);
+    (void)signal(SIGUSR1, fdump);
+    if (debug != 0)
+       (void)signal(SIGQUIT, dump);
+
+    (void)alarm(TIMER_INTERVAL);        /* schedule first timer interrupt */
+
+    /*
+     * Main receive loop.
+     */
+    dummy = 0;
+    for(;;) {
+       recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
+                          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);
+    }
+}
+
+
+/*
+ * 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()
+{
+    int next_interval;
+
+    age_routes();      /* Advance the timers in the route entries     */
+    age_vifs();                /* Advance the timers for neighbors and groups */
+
+    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;
+    next_interval = TIMER_INTERVAL;
+
+    if (virtual_time % ROUTE_REPORT_INTERVAL == 0) {
+       /*
+        * Time for the periodic report of all routes to all neighbors.
+        */
+       report_to_all_neighbors(ALL_ROUTES);
+
+       /*
+        * Schedule the next timer interrupt for a random time between
+        * 1 and TIMER_INTERVAL seconds from now.  This randomization is
+        * intended to counteract the undesirable synchronizing tendency
+        * of periodic transmissions from multiple sources.
+        */
+       next_interval = (random() % TIMER_INTERVAL) + 1;
+    }
+    else 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);
+    }
+
+    /*
+     * Advance virtual time and schedule the next timer interrupt.
+     */
+    virtual_time += TIMER_INTERVAL;
+    (void)alarm(next_interval);
+}
+
+
+/*
+ * On hangup signal, let everyone know we're going away.
+ */
+static void hup()
+{
+    log(LOG_INFO, 0, "hup");
+    expire_all_routes();
+    report_to_all_neighbors(ALL_ROUTES);
+    exit(1);
+}
+
+
+/*
+ * 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);
+    }
+}
+
+
+/*
+ * 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.
+ */
+void log(severity, syserr, format, a, b, c, d, e)
+    int severity, syserr;
+    char *format;
+    int a, b, c, d, e;
+{
+    char fmt[100];
+
+    switch (debug) {
+       case 0: break;
+       case 1: if (severity > LOG_NOTICE) break;
+       case 2: if (severity > LOG_INFO  ) break;
+       default:
+           fmt[0] = '\0';
+           if (severity == LOG_WARNING) strcat(fmt, "warning - ");
+           strncat(fmt, format, 80);
+           fprintf(stderr, fmt, a, b, c, d, e);
+           if (syserr == 0)
+               fprintf(stderr, "\n");
+           else if(syserr < sys_nerr)
+               fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+           else
+               fprintf(stderr, ": errno %d\n", syserr);
+    }
+
+    if (severity <= LOG_NOTICE) {
+       fmt[0] = '\0';
+       if (severity == LOG_WARNING) strcat(fmt, "warning - ");
+       strncat(fmt, format, 80);
+       if (syserr != 0) {
+               strcat(fmt, ": %m");
+               errno = syserr;
+       }
+       syslog(severity, fmt, a, b, c, d, e);
+
+       if (severity <= LOG_ERR) exit(-1);
+    }
+}
diff --git a/usr.sbin/mrouted/mapper.c b/usr.sbin/mrouted/mapper.c
new file mode 100644 (file)
index 0000000..287df84
--- /dev/null
@@ -0,0 +1,932 @@
+/* Mapper for connections between MRouteD multicast routers.
+ * Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
+ *
+ * $Id: mapper.c,v 1.4 1993/06/24 05:11:16 deering Exp $
+ */
+
+/*
+ * Copyright (c) Xerox Corporation 1992. All rights reserved.
+ *  
+ * License is granted to copy, to use, and to make and to use derivative
+ * works for research and evaluation purposes, provided that Xerox is
+ * acknowledged in all documentation pertaining to any such copy or derivative
+ * work. Xerox grants no other licenses expressed or implied. The Xerox trade
+ * name should not be used in any advertising without its written permission.
+ *  
+ * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
+ * FOR ANY PARTICULAR PURPOSE.  The software is provided "as is" without
+ * express or implied warranty of any kind.
+ *  
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#include <netdb.h>
+#include <sys/time.h>
+#include "defs.h"
+
+#define DEFAULT_TIMEOUT        2       /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 1      /* How many times to ask each router */
+
+
+/* All IP addresses are stored in the data structure in NET order. */
+
+typedef struct neighbor {
+    struct neighbor    *next;
+    u_long             addr;           /* IP address in NET order */
+    u_char             metric;         /* TTL cost of forwarding */
+    u_char             threshold;      /* TTL threshold to forward */
+    u_short            flags;          /* flags on connection */
+#define NF_PRESENT 0x8000      /* True if flags are meaningful */
+} Neighbor;
+
+typedef struct interface {
+    struct interface *next;
+    u_long     addr;           /* IP address of the interface in NET order */
+    Neighbor   *neighbors;     /* List of neighbors' IP addresses */
+} Interface;
+
+typedef struct node {
+    u_long     addr;           /* IP address of this entry in NET order */
+    u_long     version;        /* which mrouted version is running */
+    int                tries;          /* How many requests sent?  -1 for aliases */
+    union {
+       struct node *alias;             /* If alias, to what? */
+       struct interface *interfaces;   /* Else, neighbor data */
+    } u;
+    struct node *left, *right;
+} Node;
+
+
+Node   *routers = 0;
+u_long our_addr, target_addr = 0;              /* in NET order */
+int    debug = 0;
+int    retries = DEFAULT_RETRIES;
+int    timeout = DEFAULT_TIMEOUT;
+int    show_names = TRUE;
+
+
+Node *find_node(addr, ptr)
+    u_long addr;
+    Node **ptr;
+{
+    Node *n = *ptr;
+
+    if (!n) {
+       *ptr = n = (Node *) malloc(sizeof(Node));
+       n->addr = addr;
+       n->version = 0;
+       n->tries = 0;
+       n->u.interfaces = 0;
+       n->left = n->right = 0;
+       return n;
+    } else if (addr == n->addr)
+       return n;
+    else if (addr < n->addr)
+       return find_node(addr, &(n->left));
+    else
+       return find_node(addr, &(n->right));
+}
+
+
+Interface *find_interface(addr, node)
+    u_long addr;
+    Node *node;
+{
+    Interface *ifc;
+
+    for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+       if (ifc->addr == addr)
+           return ifc;
+
+    ifc = (Interface *) malloc(sizeof(Interface));
+    ifc->addr = addr;
+    ifc->next = node->u.interfaces;
+    node->u.interfaces = ifc;
+    ifc->neighbors = 0;
+
+    return ifc;
+}
+
+
+Neighbor *find_neighbor(addr, node)
+    u_long addr;
+    Node *node;
+{
+    Interface *ifc;
+
+    for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+       Neighbor *nb;
+
+       for (nb = ifc->neighbors; nb; nb = nb->next)
+           if (nb->addr == addr)
+               return nb;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level.  For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+void log(severity, syserr, format, a, b, c, d, e)
+    int severity, syserr;
+    char *format;
+    int a, b, c, d, e;
+{
+    char fmt[100];
+
+    switch (debug) {
+       case 0: if (severity > LOG_WARNING) return;
+       case 1: if (severity > LOG_NOTICE ) return;
+       case 2: if (severity > LOG_INFO   ) return;
+       default:
+           fmt[0] = '\0';
+           if (severity == LOG_WARNING)
+               strcat(fmt, "warning - ");
+           strncat(fmt, format, 80);
+           fprintf(stderr, fmt, a, b, c, d, e);
+           if (syserr == 0)
+               fprintf(stderr, "\n");
+           else if (syserr < sys_nerr)
+               fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+           else
+               fprintf(stderr, ": errno %d\n", syserr);
+    }
+
+    if (severity <= LOG_ERR)
+       exit(-1);
+}
+
+
+/*
+ * Send a neighbors-list request.
+ */
+void ask(dst)
+    u_long dst;
+{
+    send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+               htonl(MROUTED_LEVEL), 0);
+}
+
+void ask2(dst)
+    u_long dst;
+{
+    send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+               htonl(MROUTED_LEVEL), 0);
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void accept_group_report(src, dst, group)
+    u_long src, dst, group;
+{
+    log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void accept_probe(src, dst)
+    u_long src, dst;
+{
+    log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming route report message.
+ */
+void accept_report(src, dst, p, datalen)
+    u_long src, dst;
+    char *p;
+    int datalen;
+{
+    log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list request message.
+ */
+void accept_neighbor_request(src, dst)
+    u_long src, dst;
+{
+    if (src != our_addr)
+       log(LOG_INFO, 0,
+           "ignoring spurious DVMRP neighbor request from %s to %s",
+           inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+void accept_neighbor_request2(src, dst)
+    u_long src, dst;
+{
+    if (src != our_addr)
+       log(LOG_INFO, 0,
+           "ignoring spurious DVMRP neighbor request2 from %s to %s",
+           inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors(src, dst, p, datalen, level)
+    u_long src, dst, level;
+    u_char *p;
+    int datalen;
+{
+    Node       *node = find_node(src, &routers);
+
+    if (node->tries == 0)      /* Never heard of 'em; must have hit them at */
+       node->tries = 1;        /* least once, though...*/
+    else if (node->tries == -1)        /* follow alias link */
+       node = node->u.alias;
+
+#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
+                    a += ((u_long)*p++ << 8), a += *p++)
+
+    /* if node is running a recent mrouted, ask for additional info */
+    if (level != 0) {
+       node->version = ntohl(level);
+       node->tries = 0;
+       ask2(src);
+       return;
+    }
+
+    if (debug > 3) {
+       int i;
+
+       fprintf(stderr, "    datalen = %d\n", datalen);
+       for (i = 0; i < datalen; i++) {
+           if ((i & 0xF) == 0)
+               fprintf(stderr, "   ");
+           fprintf(stderr, " %02x", p[i]);
+           if ((i & 0xF) == 0xF)
+               fprintf(stderr, "\n");
+       }
+       if ((datalen & 0xF) != 0xF)
+           fprintf(stderr, "\n");
+    }
+
+    while (datalen > 0) {      /* loop through interfaces */
+       u_long          ifc_addr;
+       u_char          metric, threshold, ncount;
+       Node           *ifc_node;
+       Interface      *ifc;
+       Neighbor       *old_neighbors;
+
+       if (datalen < 4 + 3) {
+           log(LOG_WARNING, 0, "received truncated interface record from %s",
+               inet_fmt(src, s1));
+           return;
+       }
+
+       GET_ADDR(ifc_addr);
+       ifc_addr = htonl(ifc_addr);
+       metric = *p++;
+       threshold = *p++;
+       ncount = *p++;
+       datalen -= 4 + 3;
+
+       /* Fix up any alias information */
+       ifc_node = find_node(ifc_addr, &routers);
+       if (ifc_node->tries == 0) { /* new node */
+           ifc_node->tries = -1;
+           ifc_node->u.alias = node;
+       } else if (ifc_node != node
+                  && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
+           /* must merge two hosts' nodes */
+           Interface  *ifc_i, *next_ifc_i;
+
+           if (ifc_node->tries == -1) {
+               Node *tmp = ifc_node->u.alias;
+
+               ifc_node->u.alias = node;
+               ifc_node = tmp;
+           }
+
+           /* Merge ifc_node (foo_i) into node (foo_n) */
+
+           if (ifc_node->tries > node->tries)
+               node->tries = ifc_node->tries;
+
+           for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+               Neighbor *nb_i, *next_nb_i, *nb_n;
+               Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+               old_neighbors = ifc_n->neighbors;
+               for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+                   next_nb_i = nb_i->next;
+                   for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+                       if (nb_i->addr == nb_n->addr) {
+                           if (nb_i->metric != nb_n->metric
+                               || nb_i->threshold != nb_i->threshold)
+                               log(LOG_WARNING, 0,
+                                   "inconsistent %s for neighbor %s of %s",
+                                   "metric/threshold",
+                                   inet_fmt(nb_i->addr, s1),
+                                   inet_fmt(node->addr, s2));
+                           free(nb_i);
+                           break;
+                       }
+                   if (!nb_n) { /* no match for this neighbor yet */
+                       nb_i->next = ifc_n->neighbors;
+                       ifc_n->neighbors = nb_i;
+                   }
+               }
+
+               next_ifc_i = ifc_i->next;
+               free(ifc_i);
+           }
+
+           ifc_node->tries = -1;
+           ifc_node->u.alias = node;
+       }
+       
+       ifc = find_interface(ifc_addr, node);
+       old_neighbors = ifc->neighbors;
+       
+       /* Add the neighbors for this interface */
+       while (ncount--) {
+           u_long      neighbor;
+           Neighbor   *nb;
+           Node       *n_node;
+
+           if (datalen < 4) {
+               log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+                   inet_fmt(src, s1));
+               return;
+           }
+
+           GET_ADDR(neighbor);
+           neighbor = htonl(neighbor);
+           datalen -= 4;
+
+           for (nb = old_neighbors; nb; nb = nb->next)
+               if (nb->addr == neighbor) {
+                   if (metric != nb->metric || threshold != nb->threshold)
+                       log(LOG_WARNING, 0,
+                           "inconsistent %s for neighbor %s of %s",
+                           "metric/threshold",
+                           inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+                   goto next_neighbor;
+               }
+
+           nb = (Neighbor *) malloc(sizeof(Neighbor));
+           nb->next = ifc->neighbors;
+           ifc->neighbors = nb;
+           nb->addr = neighbor;
+           nb->metric = metric;
+           nb->threshold = threshold;
+           nb->flags = 0;
+
+           n_node = find_node(neighbor, &routers);
+           if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
+               ask(neighbor);
+               n_node->tries = 1;
+           }
+
+         next_neighbor: ;
+       }
+    }
+}
+
+void accept_neighbors2(src, dst, p, datalen)
+    u_long src, dst;
+    u_char *p;
+    int datalen;
+{
+    Node       *node = find_node(src, &routers);
+
+    if (node->tries == 0)      /* Never heard of 'em; must have hit them at */
+       node->tries = 1;        /* least once, though...*/
+    else if (node->tries == -1)        /* follow alias link */
+       node = node->u.alias;
+
+    while (datalen > 0) {      /* loop through interfaces */
+       u_long          ifc_addr;
+       u_char          metric, threshold, ncount, flags;
+       Node           *ifc_node;
+       Interface      *ifc;
+       Neighbor       *old_neighbors;
+
+       if (datalen < 4 + 4) {
+           log(LOG_WARNING, 0, "received truncated interface record from %s",
+               inet_fmt(src, s1));
+           return;
+       }
+
+       ifc_addr = *(u_long*)p;
+       p += 4;
+       metric = *p++;
+       threshold = *p++;
+       flags = *p++;
+       ncount = *p++;
+       datalen -= 4 + 4;
+
+       /* Fix up any alias information */
+       ifc_node = find_node(ifc_addr, &routers);
+       if (ifc_node->tries == 0) { /* new node */
+           ifc_node->tries = -1;
+           ifc_node->u.alias = node;
+       } else if (ifc_node != node
+                  && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
+           /* must merge two hosts' nodes */
+           Interface  *ifc_i, *next_ifc_i;
+
+           if (ifc_node->tries == -1) {
+               Node *tmp = ifc_node->u.alias;
+
+               ifc_node->u.alias = node;
+               ifc_node = tmp;
+           }
+
+           /* Merge ifc_node (foo_i) into node (foo_n) */
+
+           if (ifc_node->tries > node->tries)
+               node->tries = ifc_node->tries;
+
+           for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+               Neighbor *nb_i, *next_nb_i, *nb_n;
+               Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+               old_neighbors = ifc_n->neighbors;
+               for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+                   next_nb_i = nb_i->next;
+                   for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+                       if (nb_i->addr == nb_n->addr) {
+                           if (nb_i->metric != nb_n->metric
+                               || nb_i->threshold != nb_i->threshold)
+                               log(LOG_WARNING, 0,
+                                   "inconsistent %s for neighbor %s of %s",
+                                   "metric/threshold",
+                                   inet_fmt(nb_i->addr, s1),
+                                   inet_fmt(node->addr, s2));
+                           free(nb_i);
+                           break;
+                       }
+                   if (!nb_n) { /* no match for this neighbor yet */
+                       nb_i->next = ifc_n->neighbors;
+                       ifc_n->neighbors = nb_i;
+                   }
+               }
+
+               next_ifc_i = ifc_i->next;
+               free(ifc_i);
+           }
+
+           ifc_node->tries = -1;
+           ifc_node->u.alias = node;
+       }
+       
+       ifc = find_interface(ifc_addr, node);
+       old_neighbors = ifc->neighbors;
+       
+       /* Add the neighbors for this interface */
+       while (ncount--) {
+           u_long      neighbor;
+           Neighbor   *nb;
+           Node       *n_node;
+
+           if (datalen < 4) {
+               log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+                   inet_fmt(src, s1));
+               return;
+           }
+
+           neighbor = *(u_long*)p;
+           p += 4;
+           datalen -= 4;
+           if (neighbor == 0)
+               /* make leaf nets point to themselves */
+               neighbor = ifc_addr;
+
+           for (nb = old_neighbors; nb; nb = nb->next)
+               if (nb->addr == neighbor) {
+                   if (metric != nb->metric || threshold != nb->threshold)
+                       log(LOG_WARNING, 0,
+                           "inconsistent %s for neighbor %s of %s",
+                           "metric/threshold",
+                           inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+                   goto next_neighbor;
+               }
+
+           nb = (Neighbor *) malloc(sizeof(Neighbor));
+           nb->next = ifc->neighbors;
+           ifc->neighbors = nb;
+           nb->addr = neighbor;
+           nb->metric = metric;
+           nb->threshold = threshold;
+           nb->flags = flags | NF_PRESENT;
+
+           n_node = find_node(neighbor, &routers);
+           if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
+               ask(neighbor);
+               n_node->tries = 1;
+           }
+
+         next_neighbor: ;
+       }
+    }
+}
+
+
+void check_vif_state()
+{
+    log(LOG_NOTICE, 0, "network marked down...");
+}
+
+
+int retry_requests(node)
+    Node *node;
+{
+    int        result;
+
+    if (node) {
+       result = retry_requests(node->left);
+       if (node->tries > 0  &&  node->tries < retries) {
+           if (node->version)
+               ask2(node->addr);
+           else
+               ask(node->addr);
+           node->tries++;
+           result = 1;
+       }
+       return retry_requests(node->right) || result;
+    } else
+       return 0;
+}
+
+
+char *inet_name(addr)
+    u_long addr;
+{
+    struct hostent *e;
+
+    e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+    return e ? e->h_name : 0;
+}
+
+
+void print_map(node)
+    Node *node;
+{
+    if (node) {
+       char *name, *addr;
+       
+       print_map(node->left);
+
+       addr = inet_fmt(node->addr, s1);
+       if (!target_addr
+           || (node->tries >= 0 && node->u.interfaces)
+           || (node->tries == -1
+               && node->u.alias->tries >= 0
+               && node->u.alias->u.interfaces)) {
+           if (show_names && (name = inet_name(node->addr)))
+               printf("%s (%s):", addr, name);
+           else
+               printf("%s:", addr);
+           if (node->tries < 0)
+               printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1));
+           else if (!node->u.interfaces)
+               printf(" no response to query\n\n");
+           else {
+               Interface *ifc;
+
+               if (node->version)
+                   printf(" <v%d.%d>", node->version & 0xff,
+                                       (node->version >> 8) & 0xff);
+               printf("\n");
+               for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+                   Neighbor *nb;
+                   char *ifc_name = inet_fmt(ifc->addr, s1);
+                   int ifc_len = strlen(ifc_name);
+                   int count = 0;
+
+                   printf("    %s:", ifc_name);
+                   for (nb = ifc->neighbors; nb; nb = nb->next) {
+                       if (count > 0)
+                           printf("%*s", ifc_len + 5, "");
+                       printf("  %s", inet_fmt(nb->addr, s1));
+                       if (show_names  &&  (name = inet_name(nb->addr)))
+                           printf(" (%s)", name);
+                       printf(" [%d/%d", nb->metric, nb->threshold);
+                       if (nb->flags) {
+                           u_short flags = nb->flags;
+                           if (flags & DVMRP_NF_TUNNEL)
+                                   printf("/tunnel");
+                           if (flags & DVMRP_NF_SRCRT)
+                                   printf("/srcrt");
+                           if (flags & DVMRP_NF_QUERIER)
+                                   printf("/querier");
+                           if (flags & DVMRP_NF_DISABLED)
+                                   printf("/disabled");
+                           if (flags & DVMRP_NF_DOWN)
+                                   printf("/down");
+                       }
+                        printf("]\n");
+                       count++;
+                   }
+               }
+               printf("\n");
+           }
+       }
+       print_map(node->right);
+    }
+}
+
+
+char *graph_name(addr, buf)
+    u_long addr;
+    char *buf;
+{
+    char *name;
+
+    if (show_names  &&  (name = inet_name(addr)))
+       strcpy(buf, name);
+    else
+       inet_fmt(addr, buf);
+
+    return buf;
+}
+
+
+void graph_edges(node)
+    Node *node;
+{
+    Interface *ifc;
+    Neighbor *nb;
+    char name[100];
+
+    if (node) {
+       graph_edges(node->left);
+       if (node->tries >= 0) {
+           printf("  %d {$ NP %d0 %d0 $} \"%s%s\" \n",
+                  (int) node->addr,
+                  node->addr & 0xFF, (node->addr >> 8) & 0xFF,
+                  graph_name(node->addr, name),
+                  node->u.interfaces ? "" : "*");
+           for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+               for (nb = ifc->neighbors; nb; nb = nb->next) {
+                   Node *nb_node = find_node(nb->addr, &routers);
+                   Neighbor *nb2;
+
+                   if (nb_node->tries < 0)
+                       nb_node = nb_node->u.alias;
+
+                   if (node != nb_node &&
+                       (!(nb2 = find_neighbor(node->addr, nb_node))
+                        || node->addr < nb_node->addr)) {
+                       printf("    %d \"%d/%d",
+                              nb_node->addr, nb->metric, nb->threshold);
+                       if (nb2 && (nb2->metric != nb->metric
+                                   || nb2->threshold != nb->threshold))
+                           printf(",%d/%d", nb2->metric, nb2->threshold);
+                       if (nb->flags & NF_PRESENT)
+                           printf("%s%s",
+                                  nb->flags & DVMRP_NF_SRCRT ? "" :
+                                  nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
+                                  nb->flags & DVMRP_NF_DOWN ? "D" : "");
+                       printf("\"\n");
+                   }
+               }
+           printf("    ;\n");
+       }
+       graph_edges(node->right);
+    }
+}
+
+void elide_aliases(node)
+    Node *node;
+{
+    if (node) {
+       elide_aliases(node->left);
+       if (node->tries >= 0) {
+           Interface *ifc;
+
+           for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+               Neighbor *nb;
+
+               for (nb = ifc->neighbors; nb; nb = nb->next) {
+                   Node *nb_node = find_node(nb->addr, &routers);
+
+                   if (nb_node->tries < 0)
+                       nb->addr = nb_node->u.alias->addr;
+               }
+           }
+       }
+       elide_aliases(node->right);
+    }
+}
+
+void graph_map()
+{
+    time_t now = time(0);
+    char *nowstr = ctime(&now);
+
+    nowstr[24] = '\0';         /* Kill the newline at the end */
+    elide_aliases(routers);
+    printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
+          nowstr);
+    graph_edges(routers);
+    printf("END\n");
+}
+
+
+int get_number(var, deflt, pargv, pargc)
+    int *var, *pargc, deflt;
+    char ***pargv;
+{
+    if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
+       if (*pargc > 1  &&  isdigit((*pargv)[1][0])) {
+           (*pargv)++, (*pargc)--;
+           *var = atoi((*pargv)[0]);
+           return 1;
+       } else if (deflt >= 0) {
+           *var = deflt;
+           return 1;
+       } else
+           return 0;
+    } else {                   /* Get value from the rest of this argument */
+       if (isdigit((*pargv)[0][2])) {
+           *var = atoi((*pargv)[0] + 2);
+           return 1;
+       } else {
+           return 0;
+       }
+    }
+}
+
+
+u_long host_addr(name)
+    char *name;
+{
+    struct hostent *e = gethostbyname(name);
+    int addr;
+
+    if (e)
+       memcpy(&addr, e->h_addr_list[0], e->h_length);
+    else {
+       addr = inet_addr(name);
+       if (addr == -1)
+           addr = 0;
+    }
+
+    return addr;
+}
+
+
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    int flood = FALSE, graph = FALSE;
+    
+#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][0] == '-') {
+       switch (argv[0][1]) {
+         case 'd':
+           if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+               goto usage;
+           break;
+         case 'f':
+           flood = TRUE;
+           break;
+         case 'g':
+           graph = TRUE;
+           break;
+         case 'n':
+           show_names = FALSE;
+           break;
+         case 'r':
+           if (!get_number(&retries, -1, &argv, &argc))
+               goto usage;
+           break;
+         case 't':
+           if (!get_number(&timeout, -1, &argv, &argc))
+               goto usage;
+           break;
+         default:
+           goto usage;
+       }
+       argv++, argc--;
+    }
+
+    if (argc > 1) {
+      usage:   
+       fprintf(stderr,
+               "Usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n",
+               "[-r retries] [-d [debug-level]] [router]");
+        fprintf(stderr, "\t-f  Flood the routing graph with queries\n");
+        fprintf(stderr, "\t    (True by default unless `router' is given)\n");
+        fprintf(stderr, "\t-g  Generate output in GraphEd format\n");
+        fprintf(stderr, "\t-n  Don't look up DNS names for routers\n");
+       exit(1);
+    } else if (argc == 1 && !(target_addr = host_addr(argv[0]))) {
+       fprintf(stderr, "Unknown host: %s\n", argv[0]);
+       exit(2);
+    }
+
+    if (debug)
+       fprintf(stderr, "Debug level %u\n", debug);
+
+    init_igmp();
+
+    {                          /* Find a good local address for us. */
+       int udp;
+       struct sockaddr_in addr;
+       int addrlen = sizeof(addr);
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = dvmrp_group;
+       addr.sin_port = htons(2000); /* any port over 1024 will do... */
+       if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+           || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+           || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
+           perror("Determining local address");
+           exit(-1);
+       }
+       close(udp);
+       our_addr = addr.sin_addr.s_addr;
+    }
+
+    /* Send initial seed message to all local routers */
+    ask(target_addr ? target_addr : allhosts_group);
+
+    if (target_addr) {
+       Node *n = find_node(target_addr, &routers);
+
+       n->tries = 1;
+
+       if (flood)
+           target_addr = 0;
+    }
+
+    /* Main receive loop */
+    for(;;) {
+       fd_set          fds;
+       struct timeval  tv;
+       int             count, recvlen, dummy = 0;
+
+       FD_ZERO(&fds);
+       FD_SET(igmp_socket, &fds);
+
+       tv.tv_sec = timeout;
+       tv.tv_usec = 0;
+
+       count = select(igmp_socket + 1, &fds, 0, 0, &tv);
+
+       if (count < 0) {
+           if (errno != EINTR)
+               perror("select");
+           continue;
+       } else if (count == 0) {
+           log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+           if (retry_requests(routers))
+               continue;
+           else
+               break;
+       }
+
+       recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
+                          0, NULL, &dummy);
+       if (recvlen >= 0)
+           accept_igmp(recvlen);
+       else if (errno != EINTR)
+           perror("recvfrom");
+    }
+
+    printf("\n");
+
+    if (graph)
+       graph_map();
+    else {
+       if (!target_addr)
+           printf("Multicast Router Connectivity:\n\n");
+       print_map(routers);
+    }
+
+    exit(0);
+}
diff --git a/usr.sbin/mrouted/mrinfo.c b/usr.sbin/mrouted/mrinfo.c
new file mode 100644 (file)
index 0000000..ee94b6b
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * This tool requests configuration info from a multicast router
+ * and prints the reply (if any).  Invoke it as:
+ *
+ *     mrinfo router-name-or-address
+ *
+ * Written Wed Mar 24 1993 by Van Jacobson (adapted from the
+ * multicast mapper written by Pavel Curtis).
+ *
+ * The lawyers insist we include the following UC copyright notice.
+ * The mapper from which this is derived contained a Xerox copyright
+ * notice which follows the UC one.  Try not to get depressed noting
+ * that the legal gibberish is larger than the program.
+ *
+ * Copyright (c) 1993 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the Computer Systems
+ *     Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ---------------------------------
+ * Copyright (c) Xerox Corporation 1992. All rights reserved.
+ * 
+ * License is granted to copy, to use, and to make and to use derivative works
+ * for research and evaluation purposes, provided that Xerox is acknowledged
+ * in all documentation pertaining to any such copy or derivative work. Xerox
+ * grants no other licenses expressed or implied. The Xerox trade name should
+ * not be used in any advertising without its written permission.
+ * 
+ * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR
+ * ANY PARTICULAR PURPOSE.  The software is provided "as is" without express
+ * or implied warranty of any kind.
+ * 
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#ifndef lint
+static char rcsid[] =
+    "@(#) $Id: mrinfo.c,v 1.3 1993/06/24 05:11:16 deering Exp $";
+/*  original rcsid:
+    "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
+*/
+#endif
+
+#include <netdb.h>
+#include <sys/time.h>
+#include "defs.h"
+
+#define DEFAULT_TIMEOUT        4       /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 3      /* How many times to ask each router */
+
+u_long  our_addr, target_addr = 0;     /* in NET order */
+int     debug = 0;
+int     retries = DEFAULT_RETRIES;
+int     timeout = DEFAULT_TIMEOUT;
+int    target_level;
+
+char   *
+inet_name(addr)
+       u_long  addr;
+{
+       struct hostent *e;
+
+       if (addr == 0)
+               return "local";
+
+       e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+       return e ? e->h_name : "?";
+}
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level.  For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+void 
+log(severity, syserr, format, a, b, c, d, e)
+       int     severity, syserr;
+       char   *format;
+       int     a, b, c, d, e;
+{
+       char    fmt[100];
+
+       switch (debug) {
+       case 0:
+               if (severity > LOG_WARNING)
+                       return;
+       case 1:
+               if (severity > LOG_NOTICE)
+                       return;
+       case 2:
+               if (severity > LOG_INFO)
+                       return;
+       default:
+               fmt[0] = '\0';
+               if (severity == LOG_WARNING)
+                       strcat(fmt, "warning - ");
+               strncat(fmt, format, 80);
+               fprintf(stderr, fmt, a, b, c, d, e);
+               if (syserr == 0)
+                       fprintf(stderr, "\n");
+               else if (syserr < sys_nerr)
+                       fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+               else
+                       fprintf(stderr, ": errno %d\n", syserr);
+       }
+
+       if (severity <= LOG_ERR)
+               exit(-1);
+}
+
+/*
+ * Send a neighbors-list request.
+ */
+void 
+ask(dst)
+       u_long  dst;
+{
+       send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+                       htonl(MROUTED_LEVEL), 0);
+}
+
+void 
+ask2(dst)
+       u_long  dst;
+{
+       send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+                       htonl(MROUTED_LEVEL), 0);
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void 
+accept_neighbors(src, dst, p, datalen)
+       u_long  src, dst;
+       u_char *p;
+       int     datalen;
+{
+       u_char *ep = p + datalen;
+#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
+                    a += ((u_long)*p++ << 8), a += *p++)
+
+       printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
+       while (p < ep) {
+               register u_long laddr;
+               register u_char metric;
+               register u_char thresh;
+               register int ncount;
+
+               GET_ADDR(laddr);
+               laddr = htonl(laddr);
+               metric = *p++;
+               thresh = *p++;
+               ncount = *p++;
+               while (--ncount >= 0) {
+                       register u_long neighbor;
+                       GET_ADDR(neighbor);
+                       neighbor = htonl(neighbor);
+                       printf("  %s -> ", inet_fmt(laddr, s1));
+                       printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
+                              inet_name(neighbor), metric, thresh);
+               }
+       }
+}
+
+void 
+accept_neighbors2(src, dst, p, datalen)
+       u_long  src, dst;
+       u_char *p;
+       int     datalen;
+{
+       u_char *ep = p + datalen;
+
+       printf("%s (%s) [version %d.%d]:\n", inet_fmt(src, s1), inet_name(src),
+              target_level & 0xff, (target_level >> 8) & 0xff);
+       while (p < ep) {
+               register u_char metric;
+               register u_char thresh;
+               register u_char flags;
+               register int ncount;
+               register u_long laddr = *(u_long*)p;
+
+               p += 4;
+               metric = *p++;
+               thresh = *p++;
+               flags = *p++;
+               ncount = *p++;
+               while (--ncount >= 0) {
+                       register u_long neighbor = *(u_long*)p;
+                       p += 4;
+                       printf("  %s -> ", inet_fmt(laddr, s1));
+                       printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
+                              inet_name(neighbor), metric, thresh);
+                       if (flags & DVMRP_NF_TUNNEL)
+                               printf("/tunnel");
+                       if (flags & DVMRP_NF_SRCRT)
+                               printf("/srcrt");
+                       if (flags & DVMRP_NF_QUERIER)
+                               printf("/querier");
+                       if (flags & DVMRP_NF_DISABLED)
+                               printf("/disabled");
+                       if (flags & DVMRP_NF_DOWN)
+                               printf("/down");
+                       printf("]\n");
+               }
+       }
+}
+
+int 
+get_number(var, deflt, pargv, pargc)
+       int    *var, *pargc, deflt;
+       char ***pargv;
+{
+       if ((*pargv)[0][2] == '\0') {   /* Get the value from the next
+                                        * argument */
+               if (*pargc > 1 && isdigit((*pargv)[1][0])) {
+                       (*pargv)++, (*pargc)--;
+                       *var = atoi((*pargv)[0]);
+                       return 1;
+               } else if (deflt >= 0) {
+                       *var = deflt;
+                       return 1;
+               } else
+                       return 0;
+       } else {                /* Get value from the rest of this argument */
+               if (isdigit((*pargv)[0][2])) {
+                       *var = atoi((*pargv)[0] + 2);
+                       return 1;
+               } else {
+                       return 0;
+               }
+       }
+}
+
+u_long 
+host_addr(name)
+       char   *name;
+{
+       struct hostent *e = gethostbyname(name);
+       int     addr;
+
+       if (e)
+               memcpy(&addr, e->h_addr_list[0], e->h_length);
+       else {
+               addr = inet_addr(name);
+               if (addr == -1)
+                       addr = 0;
+       }
+
+       return addr;
+}
+
+void
+usage()
+{
+       fprintf(stderr, "Usage: mrinfo [-t timeout] [-r retries] router\n");
+       exit(1);
+}
+
+main(argc, argv)
+       int     argc;
+       char   *argv[];
+{
+       setlinebuf(stderr);
+
+       if (geteuid() != 0) {
+               fprintf(stderr, "mrinfo: must be root\n");
+               exit(1);
+       }
+       argv++, argc--;
+       while (argc > 0 && argv[0][0] == '-') {
+               switch (argv[0][1]) {
+               case 'd':
+                       if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+                               usage();
+                       break;
+               case 'r':
+                       if (!get_number(&retries, -1, &argv, &argc))
+                               usage();
+                       break;
+               case 't':
+                       if (!get_number(&timeout, -1, &argv, &argc))
+                               usage();
+                       break;
+               default:
+                       usage();
+               }
+               argv++, argc--;
+       }
+       if (argc != 1)
+               usage();
+
+       target_addr = host_addr(argv[0]);
+       if (target_addr == 0) {
+               fprintf(stderr, "mrinfo: %s: no such host\n", argv[0]);
+               exit(1);
+       }
+       if (debug)
+               fprintf(stderr, "Debug level %u\n", debug);
+
+       init_igmp();
+
+       {                       /* Find a good local address for us. */
+               int     udp;
+               struct sockaddr_in addr;
+               int     addrlen = sizeof(addr);
+
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = target_addr;
+               addr.sin_port = htons(2000);    /* any port over 1024 will
+                                                * do... */
+               if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+               || connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0
+                   || getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0) {
+                       perror("Determining local address");
+                       exit(-1);
+               }
+               close(udp);
+               our_addr = addr.sin_addr.s_addr;
+       }
+
+       ask(target_addr);
+
+       /* Main receive loop */
+       for (;;) {
+               fd_set  fds;
+               struct timeval tv;
+               int     count, recvlen, dummy = 0;
+               register u_long src, dst, group;
+               struct ip *ip;
+               struct igmp *igmp;
+               int     ipdatalen, iphdrlen, igmpdatalen;
+
+               FD_ZERO(&fds);
+               FD_SET(igmp_socket, &fds);
+
+               tv.tv_sec = timeout;
+               tv.tv_usec = 0;
+
+               count = select(igmp_socket + 1, &fds, 0, 0, &tv);
+
+               if (count < 0) {
+                       if (errno != EINTR)
+                               perror("select");
+                       continue;
+               } else if (count == 0) {
+                       log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+                       if (--retries < 0)
+                               exit(1);
+                       if (target_level == 0)
+                               ask(target_addr);
+                       else
+                               ask2(target_addr);
+                       continue;
+               }
+               recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
+                                  0, NULL, &dummy);
+               if (recvlen <= 0) {
+                       if (recvlen && errno != EINTR)
+                               perror("recvfrom");
+                       continue;
+               }
+
+               if (recvlen < sizeof(struct ip)) {
+                       log(LOG_WARNING, 0,
+                           "packet too short (%u bytes) for IP header",
+                           recvlen);
+                       continue;
+               }
+               ip = (struct ip *) recv_buf;
+               src = ip->ip_src.s_addr;
+               if (src != target_addr) {
+                       fprintf(stderr, "mrinfo: got reply from %s",
+                               inet_fmt(src, s1));
+                       fprintf(stderr, " instead of %s\n",
+                               inet_fmt(target_addr, s1));
+                       continue;
+               }
+               dst = ip->ip_dst.s_addr;
+               iphdrlen = ip->ip_hl << 2;
+               ipdatalen = ip->ip_len;
+               if (iphdrlen + ipdatalen != recvlen) {
+                       log(LOG_WARNING, 0,
+                           "packet shorter (%u bytes) than hdr+data length (%u+%u)",
+                           recvlen, iphdrlen, ipdatalen);
+                       continue;
+               }
+               igmp = (struct igmp *) (recv_buf + iphdrlen);
+               group = igmp->igmp_group.s_addr;
+               igmpdatalen = ipdatalen - IGMP_MINLEN;
+               if (igmpdatalen < 0) {
+                       log(LOG_WARNING, 0,
+                           "IP data field too short (%u bytes) for IGMP, from %s",
+                           ipdatalen, inet_fmt(src, s1));
+                       continue;
+               }
+               if (igmp->igmp_type != IGMP_DVMRP)
+                       continue;
+
+               switch (igmp->igmp_code) {
+
+               case DVMRP_NEIGHBORS:
+                       if (group) {
+                               /* knows about DVMRP_NEIGHBORS2 msg */
+                               if (target_level == 0) {
+                                       target_level = ntohl(group);
+                                       ask2(target_addr);
+                               }
+                       } else {
+                               accept_neighbors(src, dst, (char *)(igmp + 1),
+                                                igmpdatalen);
+                               exit(0);
+                       }
+                       break;
+
+               case DVMRP_NEIGHBORS2:
+                       accept_neighbors2(src, dst, (char *)(igmp + 1),
+                                         igmpdatalen);
+                       exit(0);
+               }
+       }
+}
+
+/* dummies */
+void accept_probe()
+{
+}
+void accept_group_report()
+{
+}
+void accept_neighbor_request2()
+{
+}
+void accept_report()
+{
+}
+void accept_neighbor_request()
+{
+}
+void check_vif_state()
+{
+}
diff --git a/usr.sbin/mrouted/mrouted.8 b/usr.sbin/mrouted/mrouted.8
new file mode 100644 (file)
index 0000000..5013bc7
--- /dev/null
@@ -0,0 +1,242 @@
+'\"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 \-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
+forwarding algorithm called Truncated Reverse Path Broadcasting (TRPB).
+.PP
+.I Mrouted 
+forwards a multicast datagram along a shortest (reverse) path tree
+rooted at the subnet on which the datagram originates.  It is a 
+.I broadcast
+tree, which means it includes 
+.I all
+subnets reachable by a cooperating set of 
+.I mrouted
+routers.  However, the datagram will not be forwarded onto
+.I leaf
+subnets of the tree if those subnets do not have members of the destination
+group.  Furthermore, the IP time-to-live of a multicast datagram may prevent
+it from being forwarded along the entire tree.
+.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 inserted on entry to a tunnel, and stripped out
+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
+encapsulate using IP source routing, which puts a heavy load on some
+types of routers.
+This version supports IP source route encapsulation only for backwards 
+compatibility.
+.PP
+The tunnel 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
+or inter-operation with other multicast routing protocols.
+.PP
+.I Mrouted 
+handles multicast routing only; there may or may not be a unicast
+router running on the same host 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.
+.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 two types of configuration command:
+.nf
+
+    phyint <local-addr>   [disable]   [metric <m>] [threshold <t>]
+
+    tunnel <local-addr> <remote-addr> [metric <m>] [threshold <t>] [srcrt]
+
+.fi
+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.
+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 ends before it will be used.
+For backwards compatibility with older
+.IR mrouted s,
+the srcrt keyword specifies 
+encapsulation using IP source routing.
+.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.  When in doubt, the following metrics are recommended:
+.ne 5
+.IP 1
+LAN, or tunnel across a single LAN
+.IP 2
+serial link, or tunnel across a single serial link
+.IP 3
+multi-hop tunnel
+.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.
+Suggested thresholds:
+.IP 32    
+for links that separate sites,
+.IP 64    
+for links that separate regions,
+.IP 128    
+for links that separate continents.
+.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
+.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 SIGNALS
+.PP
+.I Mrouted 
+responds to the following signals:
+.IP HUP
+.sp -.5v
+.IP TERM
+.sp -.5v
+.IP INT
+terminates execution gracefully (i.e., by sending
+good-bye messages to all neighboring routers).
+.IP USR1
+dumps the internal routing tables to /usr/tmp/mrouted.dump.
+.IP QUIT
+dumps the internal routing tables to stderr (only if
+.I mrouted 
+was invoked with a non-zero debug level). 
+.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
+  1   36.11.0.1     subnet: 36.11         1       1    querier
+                    groups: 224.0.2.1      
+                            224.0.1.0      
+                            224.0.0.4      
+  2   36.2.0.8      tunnel: 36.8.0.77     3       1   
+                    peers : 36.8.0.77      
+  3   36.2.0.8      tunnel: 36.8.0.110    3       1   
+
+Multicast Routing Table
+ Origin-Subnet   From-Gateway    Metric  In-Vif  Out-Vifs
+ 36.2                               1       0    1* 2  3*
+ 36.8            36.8.0.77          4       2    0* 1* 3*
+ 36.11                              1       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.
+.PP
+Associated with each subnet from which a multicast datagram can originate
+is the address of the previous hop gateway (unless the subnet is directly-
+connected), the metric of the path back to the origin, 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.
+.SH FILES
+/etc/mrouted.conf
+.SH SEE ALSO
+TRPB 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 AUTHOR
+Steve Deering
diff --git a/usr.sbin/mrouted/mrouted.conf b/usr.sbin/mrouted/mrouted.conf
new file mode 100644 (file)
index 0000000..8f3135d
--- /dev/null
@@ -0,0 +1,15 @@
+# $Id: mrouted.conf,v 1.3 1993/05/30 02:10:11 deering Exp $
+#
+#   This is the configuration file for "mrouted", an IP multicast router.
+#   mrouted looks for it in "/etc/mrouted.conf".
+#
+#   Command formats:
+#
+#      phyint <local-addr> [disable] [metric <m>] [threshold <t>]
+#      tunnel <local-addr> <remote-addr> [srcrt] [metric <m>] [threshold <t>]
+#
+#   any phyint commands MUST precede any tunnel commands
+#
+# See the Mbone FAQ on ftp.isi.edu for metric, thresholds and connection info.
+#
+tunnel 129.89.9.63 129.89.9.50 metric 3 threshold 64   # <-- EXAMPLE; REPLACE OR DELETE
diff --git a/usr.sbin/mrouted/route.c b/usr.sbin/mrouted/route.c
new file mode 100644 (file)
index 0000000..25518c1
--- /dev/null
@@ -0,0 +1,900 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: route.c,v 1.5 1993/06/24 05:11:16 deering Exp $
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * Exported variables.
+ */
+int routes_changed;                    /* 1=>some routes have changed */
+int delay_change_reports;              /* 1=>postpone change reports  */
+
+
+/*
+ * Private variables.
+ */
+static struct rtentry *routing_table;  /* pointer to list of route entries */
+static struct rtentry *rtp;            /* pointer to a route entry         */
+static unsigned 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;
+           }
+           k_update_route(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) {
+               k_del_route(r);
+               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;
+               k_update_route(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_long 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;
+               }
+               k_update_route(r);
+           }
+           else if (r->rt_subordinates[vifi] == addr) {
+               r->rt_subordinates[vifi] = 0;
+               if (v->uv_neighbors == NULL) {
+                   VIFM_SET(vifi, r->rt_leaves);
+                   k_update_route(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;
+               k_update_route(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 = (struct rtentry *)&routing_table;
+}
+
+
+/*
+ * 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_long 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);
+}
+
+
+/*
+ * Search the entire routing table, looking for an entry which conflicts
+ * with the given origin and mask, for example, an entry which has the same
+ * origin under a different mask.  If a conflicting entry is found, return
+ * a pointer to the entry preceding it (to facilitate deletion); if no
+ * conflict is found, return NULL.
+ */
+static struct rtentry *find_conflicting_route(origin, mask)
+    register u_long origin, mask;
+{
+    register struct rtentry *r, *prev_r;
+
+    for (prev_r = (struct rtentry *)&routing_table, r = routing_table;
+        r != NULL;
+        prev_r = r, r = r->rt_next ) {
+       if ((origin & r->rt_originmask) == r->rt_origin ||
+           (r->rt_origin & mask) == origin) {
+           return (prev_r);
+       }
+    }
+    return (NULL);
+}
+  
+
+/*
+ * 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_long origin, mask;
+{
+    register struct rtentry *r;
+
+    if ((r = (struct rtentry *) malloc(sizeof(struct rtentry)
+                               + (3 * 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_long *)(r + 1);
+    r->rt_subordinates = (u_long *)(r->rt_dominants + numvifs);
+    r->rt_leaf_timers  = (u_long *)(r->rt_subordinates + numvifs);
+
+    r->rt_next = rtp->rt_next;
+    rtp->rt_next = 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;
+    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_long origin, mask;
+    int metric;
+    u_long 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)) {
+       /*
+        * 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;
+       }
+
+       /*
+        * If the new origin and mask are inconsistent with an entry
+        * already in the routing table, either ignore this update
+        * (if it came from another router), or delete the conflicting
+        * entry (if the update is for a directly-connected subnet).
+        */
+       if ((prev_r = find_conflicting_route(origin, mask)) != NULL ) {
+           if (src != 0) {
+               log(LOG_WARNING, 0,
+                   "%s reports a conflicting origin (%s) and mask (%08x)",
+                   inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
+               return;
+           }
+           else {
+               r = prev_r->rt_next;
+               log(LOG_WARNING, 0,
+                  "deleting route with conflicting origin (%s), mask (%08x)",
+                  inet_fmt(r->rt_origin, s1), ntohl(r->rt_originmask));
+
+               if (r->rt_metric != UNREACHABLE) {
+                   k_del_route(r);
+               }
+               discard_route(prev_r);
+               if (rtp == r) rtp = prev_r;
+           }
+       }
+
+       /*
+        * OK, create the new routing entry.  'rtp' will be left pointing
+        * to the new entry.
+        */
+       create_route(origin, mask);
+
+       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);
+       k_add_route(r);
+       r->rt_gateway  = src;
+       r->rt_timer    = 0;
+       r->rt_metric   = adj_metric;
+       r->rt_flags   |= RTF_CHANGED;
+       routes_changed = TRUE;
+    }
+    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) {
+           k_del_route(r);
+           r->rt_timer = ROUTE_EXPIRE_TIME;
+       }
+       else if (adj_metric < r->rt_metric) {
+           if (init_children_and_leaves(r, vifi)) {
+               k_update_route(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)) {
+               k_update_route(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;
+               k_update_route(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;
+                   k_update_route(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);
+                   k_update_route(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;
+           }
+           k_update_route(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 = (struct rtentry *)&routing_table, 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){
+                           VIFM_SET(vifi, r->rt_leaves);
+                           k_update_route(r);
+                       }
+                       else {
+                           r->rt_flags |= RTF_LEAF_TIMING;
+                       }
+                   }
+               }
+           }
+       }
+       else if (r->rt_timer >= ROUTE_DISCARD_TIME) {
+           /*
+            * Time to garbage-collect the route entry.
+            */
+           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 {
+               k_del_route(r);
+               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;
+    }
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void accept_probe(src, dst)
+    u_long src, dst;
+{
+    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))
+       return;
+
+    report(ALL_ROUTES, vifi, src);
+}
+
+
+/*
+ * Process an incoming route report message.
+ */
+void accept_report(src, dst, p, datalen)
+    u_long src, dst;
+    register char *p;
+    register int datalen;
+{
+    vifi_t vifi;
+    register int width, i;
+    int metric;
+    u_long mask;
+    u_long origin;
+
+    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))
+       return;
+
+    start_route_updates();
+
+    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;
+       }
+       ((char *)&mask)[0] = 0xff;            width = 1;
+       if ((((char *)&mask)[1] = *p++) != 0) width = 2;
+       if ((((char *)&mask)[2] = *p++) != 0) width = 3;
+       if ((((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;
+
+           update_route(origin, mask, (metric & 0x7f), src, vifi);
+
+       } while (!(metric & 0x80));
+    }
+
+    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_long dst;
+{
+    register struct rtentry *r;
+    register char *p;
+    register int i;
+    int datalen;
+    int width;
+    u_long mask;
+    u_long src;
+
+    src = uvifs[vifi].uv_lcl_addr;
+
+    p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+    datalen = 0;
+    mask = 0;
+
+    for (r = routing_table; r != NULL; r = r->rt_next) {
+
+       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), 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), 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;
+}
+
+
+/*
+ * Print the contents of the routing table on file 'fp'.
+ */
+void dump_routes(fp)
+    FILE *fp;
+{
+    register struct rtentry *r;
+    register int i;
+
+    fprintf(fp,
+    "Multicast Routing Table (%u %s)\n%s",
+    nroutes, (nroutes == 1) ? "entry" : "entries",
+    " Origin-Subnet   From-Gateway    Metric  In-Vif  Out-Vifs\n");
+
+    for (r = routing_table; r != NULL; r = r->rt_next) {
+
+       fprintf(fp, " %-15s %-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, "%7u   ",
+               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");
+}
diff --git a/usr.sbin/mrouted/route.h b/usr.sbin/mrouted/route.h
new file mode 100644 (file)
index 0000000..2e7aa33
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: route.h,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+/*
+ * 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 singly-linked list of these structures,
+ * ordered by increasing value of rt_originmask and, secondarily, by
+ * increasing 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_long          rt_origin;         /* subnet origin of multicasts      */
+    u_long          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_long          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_long         *rt_dominants;      /* per vif dominant gateways        */
+    u_long         *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   */
+};
+
+#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/vif.c b/usr.sbin/mrouted/vif.c
new file mode 100644 (file)
index 0000000..16700f9
--- /dev/null
@@ -0,0 +1,782 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: vif.c,v 1.5 1993/06/24 05:11:16 deering Exp $
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * Exported variables.
+ */
+struct uvif    uvifs[MAXVIFS]; /* array of virtual interfaces */
+vifi_t         numvifs;        /* number of vifs in use       */
+int            vifs_down;      /* 1=>some interfaces are down */
+int            udp_socket;     /* Since the honkin' kernel doesn't support */
+                               /* ioctls on raw IP sockets, we need a UDP  */
+                               /* socket as well as our IGMP (raw) socket. */
+                               /* How dumb.                                */
+
+/*
+ * Forward declarations.
+ */
+static void start_vif();
+static void stop_vif();
+
+
+/*
+ * Initialize the virtual interfaces.
+ */
+void init_vifs()
+{
+    vifi_t vifi;
+    struct uvif *v;
+    int enabled_vifs, enabled_phyints;
+
+    numvifs = 0;
+    vifs_down = FALSE;
+
+    /*
+     * Configure the vifs based on the interface configuration of the
+     * the kernel and the contents of the configuration file.
+     * (Open a UDP socket for ioctl use in the config procedures.)
+     */
+    if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+       log(LOG_ERR, errno, "UDP socket");
+    config_vifs_from_kernel();
+    config_vifs_from_file();
+
+    /*
+     * Quit if there are fewer than two enabled vifs.
+     */
+    enabled_vifs    = 0;
+    enabled_phyints = 0;
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+       if (!(v->uv_flags & VIFF_DISABLED)) {
+           ++enabled_vifs;
+           if (!(v->uv_flags & VIFF_TUNNEL))
+               ++enabled_phyints;
+       }
+    }
+    if (enabled_vifs < 2)
+       log(LOG_ERR, 0, "can't forward: %s",
+           enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif");
+
+    if (enabled_phyints == 0)
+       log(LOG_WARNING, 0,
+           "no enabled interfaces, forwarding via tunnels only");
+
+    /*
+     * Start routing on all virtual interfaces that are not down or
+     * administratively disabled.
+     */
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+       if (!(v->uv_flags & VIFF_DISABLED)) {
+           if (!(v->uv_flags & VIFF_DOWN))
+               start_vif(vifi);
+           else log(LOG_INFO, 0,
+                   "%s is not yet up; vif #%u not in service",
+                   v->uv_name, vifi);
+       }
+    }
+}
+
+
+/*
+ * See if any interfaces have changed from up state to down, or vice versa,
+ * including any non-multicast-capable interfaces that are in use as local
+ * tunnel end-points.  Ignore interfaces that have been administratively
+ * disabled.
+ */
+void check_vif_state()
+{
+    register vifi_t vifi;
+    register struct uvif *v;
+    struct ifreq ifr;
+
+    vifs_down = FALSE;
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+
+       if (v->uv_flags & VIFF_DISABLED) continue;
+
+       strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ);
+       if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
+           log(LOG_ERR, errno,
+               "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+
+       if (v->uv_flags & VIFF_DOWN) {
+           if (ifr.ifr_flags & IFF_UP) {
+               v->uv_flags &= ~VIFF_DOWN;
+               start_vif(vifi);
+               log(LOG_INFO, 0,
+                   "%s has come up; vif #%u now in service",
+                   v->uv_name, vifi);
+           }
+           else vifs_down = TRUE;
+       }
+       else {
+           if (!(ifr.ifr_flags & IFF_UP)) {
+               stop_vif(vifi);
+               v->uv_flags |= VIFF_DOWN;
+               log(LOG_INFO, 0,
+                   "%s has gone down; vif #%u taken out of service",
+                   v->uv_name, vifi);
+               vifs_down = TRUE;
+           }
+       }
+    }
+}
+
+
+/*
+ * Start routing on the specified virtual interface.
+ */
+static void start_vif(vifi)
+    vifi_t vifi;
+{
+    struct uvif *v;
+    u_long src, dst;
+
+    v   = &uvifs[vifi];
+    src = v->uv_lcl_addr;
+    dst = (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr : dvmrp_group;
+
+    /*
+     * Install the interface in the kernel's vif structure.
+     */
+    k_add_vif(vifi, &uvifs[vifi]);
+
+    /*
+     * Update the existing route entries to take into account the new vif.
+     */
+    add_vif_to_routes(vifi);
+
+    if (!(v->uv_flags & VIFF_TUNNEL)) {
+       /*
+        * Join the DVMRP multicast group on the interface.
+        * (This is not strictly necessary, since the kernel promiscuously
+        * receives IGMP packets addressed to ANY IP multicast group while
+        * multicast routing is enabled.  However, joining the group allows
+        * this host to receive non-IGMP packets as well, such as 'pings'.)
+        */
+       k_join(dvmrp_group, src);
+
+       /*
+        * Install an entry in the routing table for the subnet to which
+        * the interface is connected.
+        */
+       start_route_updates();
+       update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi);
+
+       /*
+        * Until neighbors are discovered, assume responsibility for sending
+        * periodic group membership queries to the subnet.  Send the first
+        * query.
+        */
+       v->uv_flags |= VIFF_QUERIER;
+       send_igmp(src, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY, 0, 0, 0);
+    }
+
+    /*
+     * Send a probe via the new vif to look for neighbors.
+     */
+    send_igmp(src, dst, IGMP_DVMRP, DVMRP_PROBE, htonl(MROUTED_LEVEL), 0);
+}
+
+
+/*
+ * Stop routing on the specified virtual interface.
+ */
+static void stop_vif(vifi)
+    vifi_t vifi;
+{
+    struct uvif *v;
+    struct listaddr *a;
+
+    v = &uvifs[vifi];
+
+    if (!(v->uv_flags & VIFF_TUNNEL)) {
+       /*
+        * Depart from the DVMRP multicast group on the interface.
+        */
+       k_leave(dvmrp_group, v->uv_lcl_addr);
+
+       /*
+        * Update the entry in the routing table for the subnet to which
+        * the interface is connected, to take into account the interface
+        * failure.
+        */
+       start_route_updates();
+       update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi);
+
+       /*
+        * Discard all group addresses.  (No need to tell kernel;
+        * the k_del_vif() call, below, will clean up kernel state.)
+        */
+       while (v->uv_groups != NULL) {
+           a = v->uv_groups;
+           v->uv_groups = a->al_next;
+           free((char *)a);
+       }
+
+       v->uv_flags &= ~VIFF_QUERIER;
+    }
+
+    /*
+     * Update the existing route entries to take into account the vif failure.
+     */
+    delete_vif_from_routes(vifi);
+
+    /*
+     * Delete the interface from the kernel's vif structure.
+     */
+    k_del_vif(vifi);
+
+    /*
+     * Discard all neighbor addresses.
+     */
+    while (v->uv_neighbors != NULL) {
+       a = v->uv_neighbors;
+       v->uv_neighbors = a->al_next;
+       free((char *)a);
+    }
+}
+
+
+/*
+ * Find the virtual interface from which an incoming packet arrived,
+ * based on the packet's source and destination IP addresses.
+ */
+vifi_t find_vif(src, dst)
+    register u_long src;
+    register u_long dst;
+{
+    register vifi_t vifi;
+    register struct uvif *v;
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+       if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
+           if (v->uv_flags & VIFF_TUNNEL) {
+               if (src == v->uv_rmt_addr && dst == v->uv_lcl_addr)
+                   return(vifi);
+           }
+           else {
+               if ((src & v->uv_subnetmask) == v->uv_subnet &&
+                   src != v->uv_subnetbcast)
+                   return(vifi);
+           }
+       }
+    }
+    return (NO_VIF);
+}
+
+
+/*
+ * 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, 0, 0, 0);
+       }
+    }
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void accept_group_report(src, dst, group)
+    u_long src, dst, group;
+{
+    register vifi_t vifi;
+    register struct uvif *v;
+    register struct listaddr *g;
+
+    if ((vifi = find_vif(src, dst)) == NO_VIF ||
+       (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
+       log(LOG_INFO, 0,
+           "ignoring group membership report from non-adjacent host %s",
+           inet_fmt(src, s1));
+       return;
+    }
+
+    v = &uvifs[vifi];
+
+    /*
+     * Look for the group in our group list; if found, reset its timer.
+     */
+    for (g = v->uv_groups; g != NULL; g = g->al_next) {
+       if (group == g->al_addr) {
+           g->al_timer = 0;
+           break;
+       }
+    }
+
+    /*
+     * If not found, add it to the list and tell the kernel.
+     */
+    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;
+       g->al_timer  = 0;
+       g->al_next   = v->uv_groups;
+       v->uv_groups = g;
+
+       k_add_group(vifi, group);
+    }
+}
+
+
+/*
+ * Send a probe on all vifs from which no neighbors have been heard recently.
+ */
+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)) &&
+           v->uv_neighbors == NULL) {
+           send_igmp(v->uv_lcl_addr,
+                     (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
+                                                 : dvmrp_group,
+                     IGMP_DVMRP, DVMRP_PROBE, htonl(MROUTED_LEVEL), 0);
+       }
+    }
+}
+
+
+/*
+ * Send a list of all of our neighbors to the requestor, `src'.
+ */
+void accept_neighbor_request(src, dst)
+    u_long src, dst;
+{
+    vifi_t vifi;
+    struct uvif *v;
+    u_char *p, *ncount;
+    struct listaddr *la;
+    int        datalen;
+    u_long temp_addr, us, them = src;
+
+    /* Determine which of our addresses to use as the source of our response
+     * to this query.
+     */
+    if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */
+       int udp;                /* find best interface to reply on */
+       struct sockaddr_in addr;
+       int addrlen = sizeof(addr);
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = dst;
+       addr.sin_port = htons(2000); /* any port over 1024 will do... */
+       if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+           || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+           || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
+           log(LOG_WARNING, errno, "Determining local address");
+           close(udp);
+           return;
+       }
+       close(udp);
+       us = addr.sin_addr.s_addr;
+    } else                     /* query sent to us alone */
+       us = dst;       
+
+#define PUT_ADDR(a)    temp_addr = ntohl(a); \
+                       *p++ = temp_addr >> 24; \
+                       *p++ = (temp_addr >> 16) & 0xFF; \
+                       *p++ = (temp_addr >> 8) & 0xFF; \
+                       *p++ = temp_addr & 0xFF;
+
+    p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+    datalen = 0;
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+       if (v->uv_flags & VIFF_DISABLED)
+           continue;
+
+       ncount = 0;
+
+       for (la = v->uv_neighbors; la; la = la->al_next) {
+
+           /* Make sure that there's room for this neighbor... */
+           if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) {
+               send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
+                         htonl(MROUTED_LEVEL), datalen);
+               p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+               datalen = 0;
+               ncount = 0;
+           }
+
+           /* Put out the header for this neighbor list... */
+           if (ncount == 0) {
+               PUT_ADDR(v->uv_lcl_addr);
+               *p++ = v->uv_metric;
+               *p++ = v->uv_threshold;
+               ncount = p;
+               *p++ = 0;
+               datalen += 4 + 3;
+           }
+
+           PUT_ADDR(la->al_addr);
+           datalen += 4;
+           (*ncount)++;
+       }
+    }
+
+    if (datalen != 0)
+       send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(MROUTED_LEVEL),
+                 datalen);
+}
+
+/*
+ * Send a list of all of our neighbors to the requestor, `src'.
+ */
+void accept_neighbor_request2(src, dst)
+    u_long src, dst;
+{
+    vifi_t vifi;
+    struct uvif *v;
+    u_char *p, *ncount;
+    struct listaddr *la;
+    int        datalen;
+    u_long temp_addr, us, them = src;
+
+    /* Determine which of our addresses to use as the source of our response
+     * to this query.
+     */
+    if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */
+       int udp;                /* find best interface to reply on */
+       struct sockaddr_in addr;
+       int addrlen = sizeof(addr);
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = dst;
+       addr.sin_port = htons(2000); /* any port over 1024 will do... */
+       if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+           || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+           || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
+           log(LOG_WARNING, errno, "Determining local address");
+           close(udp);
+           return;
+       }
+       close(udp);
+       us = addr.sin_addr.s_addr;
+    } else                     /* query sent to us alone */
+       us = dst;       
+
+    p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+    datalen = 0;
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+       register u_short vflags = v->uv_flags;
+       register u_char rflags = 0;
+       if (vflags & VIFF_TUNNEL)
+           rflags |= DVMRP_NF_TUNNEL;
+       if (vflags & VIFF_SRCRT)
+           rflags |= DVMRP_NF_SRCRT;
+       if (vflags & VIFF_DOWN)
+           rflags |= DVMRP_NF_DOWN;
+       if (vflags & VIFF_DISABLED)
+           rflags |= DVMRP_NF_DISABLED;
+       if (vflags & VIFF_QUERIER)
+           rflags |= DVMRP_NF_QUERIER;
+       ncount = 0;
+       la = v->uv_neighbors;
+       if (la == NULL) {
+               /*
+                * include down & disabled interfaces and interfaces on
+                * leaf nets.
+                */
+               if (rflags & DVMRP_NF_TUNNEL)
+                   rflags |= DVMRP_NF_DOWN;
+               if (datalen > MAX_DVMRP_DATA_LEN - 12) {
+                   send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+                             htonl(MROUTED_LEVEL), datalen);
+                   p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+                   datalen = 0;
+               }
+               *(u_int*)p = v->uv_lcl_addr;
+               p += 4;
+               *p++ = v->uv_metric;
+               *p++ = v->uv_threshold;
+               *p++ = rflags;
+               *p++ = 1;
+               *(u_int*)p =  v->uv_rmt_addr;
+               p += 4;
+               datalen += 12;
+       } else {
+           for ( ; la; la = la->al_next) {
+               /* Make sure that there's room for this neighbor... */
+               if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) {
+                   send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+                             htonl(MROUTED_LEVEL), datalen);
+                   p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+                   datalen = 0;
+                   ncount = 0;
+               }
+               /* Put out the header for this neighbor list... */
+               if (ncount == 0) {
+                   *(u_int*)p = v->uv_lcl_addr;
+                   p += 4;
+                   *p++ = v->uv_metric;
+                   *p++ = v->uv_threshold;
+                   *p++ = rflags;
+                   ncount = p;
+                   *p++ = 0;
+                   datalen += 4 + 4;
+               }
+               *(u_int*)p = la->al_addr;
+               p += 4;
+               datalen += 4;
+               (*ncount)++;
+           }
+       }
+    }
+    if (datalen != 0)
+       send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL),
+                 datalen);
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors(src, dst, p, datalen, level)
+    u_long src, dst, level;
+    char *p;
+    int datalen;
+{
+    log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors2(src, dst, p, datalen, level)
+    u_long src, dst, level;
+    char *p;
+    int datalen;
+{
+    log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s",
+       inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Update the neighbor entry for neighbor 'addr' on vif 'vifi'.
+ * 'msgtype' is the type of DVMRP message received from the neighbor.
+ * Return TRUE if 'addr' is a valid neighbor, FALSE otherwise.
+ */
+int update_neighbor(vifi, addr, msgtype)
+    vifi_t vifi;
+    u_long addr;
+    int msgtype;
+{
+    register struct uvif *v;
+    register struct listaddr *n;
+
+    v = &uvifs[vifi];
+
+    /*
+     * Confirm that 'addr' is a valid neighbor address on vif 'vifi'.
+     * IT IS ASSUMED that this was preceded by a call to find_vif(), which
+     * checks that 'addr' is either a valid remote tunnel endpoint or a
+     * non-broadcast address belonging to a directly-connected subnet.
+     * Therefore, here we check only that 'addr' is not our own address
+     * (due to an impostor or erroneous loopback) or an address of the form
+     * {subnet,0} ("the unknown host").  These checks are not performed in
+     * find_vif() because those types of address are acceptable for some
+     * types of IGMP message (such as group membership reports).
+     */
+    if (!(v->uv_flags & VIFF_TUNNEL) &&
+       (addr == v->uv_lcl_addr ||
+        addr == v->uv_subnet )) {
+       log(LOG_WARNING, 0,
+           "received DVMRP message from 'the unknown host' or self: %s",
+           inet_fmt(addr, s1));
+       return (FALSE);
+    }
+
+    /*
+     * If we have received a route report from a neighbor, and we believed
+     * that we had no neighbors on this vif, send a full route report to
+     * all neighbors on the vif.
+     */
+
+    if (msgtype == DVMRP_REPORT && v->uv_neighbors == NULL)
+       report(ALL_ROUTES, vifi,
+              (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group);
+
+    /*
+     * 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;
+           break;
+       }
+    }
+
+    /*
+     * If not found, add it to the list.  If the neighbor has a lower
+     * IP address than me, yield querier duties to it.
+     */
+    if (n == NULL) {
+       n = (struct listaddr *)malloc(sizeof(struct listaddr));
+       if (n == NULL)
+           log(LOG_ERR, 0, "ran out of memory");    /* fatal */
+
+       n->al_addr      = addr;
+       n->al_timer     = 0;
+       n->al_next      = v->uv_neighbors;
+       v->uv_neighbors = n;
+
+       if (!(v->uv_flags & VIFF_TUNNEL) &&
+           ntohl(addr) < ntohl(v->uv_lcl_addr))
+           v->uv_flags &= ~VIFF_QUERIER;
+    }
+
+    return (TRUE);
+}
+
+
+/*
+ * On every timer interrupt, advance the timer in each neighbor and
+ * group entry on every vif.
+ */
+void age_vifs()
+{
+    register vifi_t vifi;
+    register struct uvif *v;
+    register struct listaddr *a, *prev_a, *n;
+    register u_long addr;
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) {
+
+       for (prev_a = (struct listaddr *)&(v->uv_neighbors),
+            a = v->uv_neighbors;
+            a != NULL;
+            prev_a = a, a = a->al_next) {
+
+           if ((a->al_timer += TIMER_INTERVAL) < NEIGHBOR_EXPIRE_TIME)
+               continue;
+
+           /*
+            * Neighbor has expired; delete it from the neighbor list,
+            * delete it from the 'dominants' and 'subordinates arrays of
+            * any route entries and assume querier duties unless there is
+            * another neighbor with a lower IP address than mine.
+            */
+           addr = a->al_addr;
+           prev_a->al_next = a->al_next;
+           free((char *)a);
+           a = prev_a;
+
+           delete_neighbor_from_routes(addr, vifi);
+
+           if (!(v->uv_flags & VIFF_TUNNEL)) {
+               v->uv_flags |= VIFF_QUERIER;
+               for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
+                   if (ntohl(n->al_addr) < ntohl(v->uv_lcl_addr)) {
+                       v->uv_flags &= ~VIFF_QUERIER;
+                       break;
+                   }
+               }
+           }
+       }
+
+       for (prev_a = (struct listaddr *)&(v->uv_groups),
+            a = v->uv_groups;
+            a != NULL;
+            prev_a = a, a = a->al_next) {
+
+           if ((a->al_timer += TIMER_INTERVAL) < GROUP_EXPIRE_TIME)
+               continue;
+
+           /*
+            * Group has expired; tell kernel and delete from group list.
+            */
+           k_del_group(vifi, a->al_addr);
+
+           prev_a->al_next = a->al_next;
+           free((char *)a);
+           a = prev_a;
+       }
+    }
+}
+
+
+/*
+ * 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;
+
+    fprintf(fp,
+    "\nVirtual Interface Table\n%s",
+    " Vif  Local-Address                           Metric  Thresh  Flags\n");
+
+    for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+
+       fprintf(fp, " %2u   %-15s %6s: %-15s %4u %7u   ",
+               vifi,
+               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);
+
+       if (v->uv_flags & VIFF_DOWN)     fprintf(fp, " down");
+       if (v->uv_flags & VIFF_DISABLED) fprintf(fp, " disabled");
+       if (v->uv_flags & VIFF_QUERIER)  fprintf(fp, " querier");
+       if (v->uv_flags & VIFF_SRCRT)    fprintf(fp, " src-rt");
+       fprintf(fp, "\n");
+
+       if (v->uv_neighbors != NULL) {
+           fprintf(fp, "                      peers : %-15s\n",
+                   inet_fmt(v->uv_neighbors->al_addr, s1));
+           for (a = v->uv_neighbors->al_next; a != NULL; a = a->al_next) {
+               fprintf(fp, "                              %-15s\n",
+                       inet_fmt(a->al_addr, s1));
+           }
+       }
+
+       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));
+           }
+       }
+    }
+    fprintf(fp, "\n");
+}
diff --git a/usr.sbin/mrouted/vif.h b/usr.sbin/mrouted/vif.h
new file mode 100644 (file)
index 0000000..8b4b46b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE".  Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: vif.h,v 1.3 1993/05/30 01:36:38 deering Exp $
+ */
+
+/*
+ * 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_char          uv_threshold;  /* min ttl required to forward on vif   */
+    u_long          uv_lcl_addr;   /* local address of this vif            */
+    u_long          uv_rmt_addr;   /* remote end-point addr (tunnels only) */
+    u_long          uv_subnet;     /* subnet number         (phyints only) */
+    u_long          uv_subnetmask; /* subnet mask           (phyints only) */
+    u_long          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          */
+};
+
+#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 */
+
+
+struct listaddr {
+    struct listaddr *al_next;          /* link to next addr, MUST BE FIRST */
+    u_long          al_addr;           /* local group or neighbor address  */
+    u_long          al_timer;          /* for timing out group or neighbor */
+};
+
+
+#define NO_VIF         ((vifi_t)MAXVIFS)  /* An invalid vif index */