From 0a4d79af4c6bb76a0027b67b937a8de340145f31 Mon Sep 17 00:00:00 2001 From: "Jordan K. Hubbard" Date: Tue, 17 May 1994 20:59:34 +0000 Subject: [PATCH] The multicast router. Stick it next to routed and XNSrouted since that seems the logical place (though /usr/sbin is more my taste). --- sbin/mrouted/.depend | 90 ++++ sbin/mrouted/LICENSE | 48 ++ sbin/mrouted/Makefile | 43 ++ sbin/mrouted/config.c | 528 +++++++++++++++++++ sbin/mrouted/defs.h | 129 +++++ sbin/mrouted/dvmrp.h | 141 +++++ sbin/mrouted/igmp.c | 217 ++++++++ sbin/mrouted/inet.c | 187 +++++++ sbin/mrouted/kern.c | 213 ++++++++ sbin/mrouted/main.c | 322 ++++++++++++ sbin/mrouted/mapper.c | 932 ++++++++++++++++++++++++++++++++++ sbin/mrouted/mrinfo.c | 469 +++++++++++++++++ sbin/mrouted/mrouted.8 | 242 +++++++++ sbin/mrouted/mrouted.conf | 15 + sbin/mrouted/route.c | 900 ++++++++++++++++++++++++++++++++ sbin/mrouted/route.h | 50 ++ sbin/mrouted/vif.c | 782 ++++++++++++++++++++++++++++ sbin/mrouted/vif.h | 47 ++ usr.sbin/mrouted/.depend | 90 ++++ usr.sbin/mrouted/LICENSE | 48 ++ usr.sbin/mrouted/Makefile | 43 ++ usr.sbin/mrouted/config.c | 528 +++++++++++++++++++ usr.sbin/mrouted/defs.h | 129 +++++ usr.sbin/mrouted/dvmrp.h | 141 +++++ usr.sbin/mrouted/igmp.c | 217 ++++++++ usr.sbin/mrouted/inet.c | 187 +++++++ usr.sbin/mrouted/kern.c | 213 ++++++++ usr.sbin/mrouted/main.c | 322 ++++++++++++ usr.sbin/mrouted/mapper.c | 932 ++++++++++++++++++++++++++++++++++ usr.sbin/mrouted/mrinfo.c | 469 +++++++++++++++++ usr.sbin/mrouted/mrouted.8 | 242 +++++++++ usr.sbin/mrouted/mrouted.conf | 15 + usr.sbin/mrouted/route.c | 900 ++++++++++++++++++++++++++++++++ usr.sbin/mrouted/route.h | 50 ++ usr.sbin/mrouted/vif.c | 782 ++++++++++++++++++++++++++++ usr.sbin/mrouted/vif.h | 47 ++ 36 files changed, 10710 insertions(+) create mode 100644 sbin/mrouted/.depend create mode 100644 sbin/mrouted/LICENSE create mode 100644 sbin/mrouted/Makefile create mode 100644 sbin/mrouted/config.c create mode 100644 sbin/mrouted/defs.h create mode 100644 sbin/mrouted/dvmrp.h create mode 100644 sbin/mrouted/igmp.c create mode 100644 sbin/mrouted/inet.c create mode 100644 sbin/mrouted/kern.c create mode 100644 sbin/mrouted/main.c create mode 100644 sbin/mrouted/mapper.c create mode 100644 sbin/mrouted/mrinfo.c create mode 100644 sbin/mrouted/mrouted.8 create mode 100644 sbin/mrouted/mrouted.conf create mode 100644 sbin/mrouted/route.c create mode 100644 sbin/mrouted/route.h create mode 100644 sbin/mrouted/vif.c create mode 100644 sbin/mrouted/vif.h create mode 100644 usr.sbin/mrouted/.depend create mode 100644 usr.sbin/mrouted/LICENSE create mode 100644 usr.sbin/mrouted/Makefile create mode 100644 usr.sbin/mrouted/config.c create mode 100644 usr.sbin/mrouted/defs.h create mode 100644 usr.sbin/mrouted/dvmrp.h create mode 100644 usr.sbin/mrouted/igmp.c create mode 100644 usr.sbin/mrouted/inet.c create mode 100644 usr.sbin/mrouted/kern.c create mode 100644 usr.sbin/mrouted/main.c create mode 100644 usr.sbin/mrouted/mapper.c create mode 100644 usr.sbin/mrouted/mrinfo.c create mode 100644 usr.sbin/mrouted/mrouted.8 create mode 100644 usr.sbin/mrouted/mrouted.conf create mode 100644 usr.sbin/mrouted/route.c create mode 100644 usr.sbin/mrouted/route.h create mode 100644 usr.sbin/mrouted/vif.c create mode 100644 usr.sbin/mrouted/vif.h diff --git a/sbin/mrouted/.depend b/sbin/mrouted/.depend new file mode 100644 index 0000000000..50bbd6cbdf --- /dev/null +++ b/sbin/mrouted/.depend @@ -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 index 0000000000..ef7da470b1 --- /dev/null +++ b/sbin/mrouted/LICENSE @@ -0,0 +1,48 @@ + +The mrouted program is covered by the following license. Use of the +mrouted program represents acceptance of these terms and conditions. + +1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license +to use, copy and modify the computer software ``mrouted'' (hereinafter +called the ``Program''), upon the terms and conditions hereinafter set +out and until Licensee discontinues use of the Licensed Program. + +2. LICENSEE acknowledges that the Program is a research tool still in +the development state, that it is being supplied ``as is,'' without any +accompanying services from STANFORD, and that this license is entered +into in order to encourage scientific collaboration aimed at further +development and application of the Program. + +3. LICENSEE may copy the Program and may sublicense others to use object +code copies of the Program or any derivative version of the Program. +All copies must contain all copyright and other proprietary notices found +in the Program as provided by STANFORD. Title to copyright to the +Program remains with STANFORD. + +4. LICENSEE may create derivative versions of the Program. LICENSEE +hereby grants STANFORD a royalty-free license to use, copy, modify, +distribute and sublicense any such derivative works. At the time +LICENSEE provides a copy of a derivative version of the Program to a +third party, LICENSEE shall provide STANFORD with one copy of the source +code of the derivative version at no charge to STANFORD. + +5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. +By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION +OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR +THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS, +COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable +for any liability nor for any direct, indirect or consequential damages +with respect to any claim by LICENSEE or any third party on account of or +arising from this Agreement or use of the Program. + +6. This agreement shall be construed, interpreted and applied in +accordance with the State of California and any legal action arising +out of this Agreement or use of the Program shall be filed in a court +in the State of California. + +7. Nothing in this Agreement shall be construed as conferring rights to +use in advertising, publicity or otherwise any trademark or the name +of ``Stanford''. + +The mrouted program is COPYRIGHT 1989 by The Board of Trustees of +Leland Stanford Junior University. diff --git a/sbin/mrouted/Makefile b/sbin/mrouted/Makefile new file mode 100644 index 0000000000..b3c1777fa9 --- /dev/null +++ b/sbin/mrouted/Makefile @@ -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 diff --git a/sbin/mrouted/config.c b/sbin/mrouted/config.c new file mode 100644 index 0000000000..9285ccb95b --- /dev/null +++ b/sbin/mrouted/config.c @@ -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 [disable] [metric ] [threshold ] + */ + + /* + * 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 [srcrt] [metric ] [threshold ] + */ + + /* + * 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 index 0000000000..f01179f5e8 --- /dev/null +++ b/sbin/mrouted/defs.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..662f1cb214 --- /dev/null +++ b/sbin/mrouted/dvmrp.h @@ -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 index 0000000000..24c8099f57 --- /dev/null +++ b/sbin/mrouted/igmp.c @@ -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 index 0000000000..5d7442ba1a --- /dev/null +++ b/sbin/mrouted/inet.c @@ -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 index 0000000000..548247b931 --- /dev/null +++ b/sbin/mrouted/kern.c @@ -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 index 0000000000..6352a97a55 --- /dev/null +++ b/sbin/mrouted/main.c @@ -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 index 0000000000..287df84203 --- /dev/null +++ b/sbin/mrouted/mapper.c @@ -0,0 +1,932 @@ +/* Mapper for connections between MRouteD multicast routers. + * Written by Pavel Curtis + * + * $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 +#include +#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(" ", 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 index 0000000000..ee94b6b1b7 --- /dev/null +++ b/sbin/mrouted/mrinfo.c @@ -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 +#include +#include "defs.h" + +#define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */ +#define DEFAULT_RETRIES 3 /* How many times to ask each router */ + +u_long our_addr, target_addr = 0; /* in NET order */ +int debug = 0; +int retries = DEFAULT_RETRIES; +int timeout = DEFAULT_TIMEOUT; +int target_level; + +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 index 0000000000..5013bc7c9e --- /dev/null +++ b/sbin/mrouted/mrouted.8 @@ -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 [disable] [metric ] [threshold ] + + tunnel [metric ] [threshold ] [srcrt] + +.fi +The phyint command can be used to disable multicast routing on the physical +interface identified by local IP address , 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 and remote IP address , 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 index 0000000000..8f3135d6e6 --- /dev/null +++ b/sbin/mrouted/mrouted.conf @@ -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 [disable] [metric ] [threshold ] +# tunnel [srcrt] [metric ] [threshold ] +# +# 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 index 0000000000..25518c13e4 --- /dev/null +++ b/sbin/mrouted/route.c @@ -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 index 0000000000..2e7aa3303b --- /dev/null +++ b/sbin/mrouted/route.h @@ -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 index 0000000000..16700f96dc --- /dev/null +++ b/sbin/mrouted/vif.c @@ -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 index 0000000000..8b4b46bfa9 --- /dev/null +++ b/sbin/mrouted/vif.h @@ -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 index 0000000000..50bbd6cbdf --- /dev/null +++ b/usr.sbin/mrouted/.depend @@ -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 index 0000000000..ef7da470b1 --- /dev/null +++ b/usr.sbin/mrouted/LICENSE @@ -0,0 +1,48 @@ + +The mrouted program is covered by the following license. Use of the +mrouted program represents acceptance of these terms and conditions. + +1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license +to use, copy and modify the computer software ``mrouted'' (hereinafter +called the ``Program''), upon the terms and conditions hereinafter set +out and until Licensee discontinues use of the Licensed Program. + +2. LICENSEE acknowledges that the Program is a research tool still in +the development state, that it is being supplied ``as is,'' without any +accompanying services from STANFORD, and that this license is entered +into in order to encourage scientific collaboration aimed at further +development and application of the Program. + +3. LICENSEE may copy the Program and may sublicense others to use object +code copies of the Program or any derivative version of the Program. +All copies must contain all copyright and other proprietary notices found +in the Program as provided by STANFORD. Title to copyright to the +Program remains with STANFORD. + +4. LICENSEE may create derivative versions of the Program. LICENSEE +hereby grants STANFORD a royalty-free license to use, copy, modify, +distribute and sublicense any such derivative works. At the time +LICENSEE provides a copy of a derivative version of the Program to a +third party, LICENSEE shall provide STANFORD with one copy of the source +code of the derivative version at no charge to STANFORD. + +5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. +By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION +OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR +THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS, +COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable +for any liability nor for any direct, indirect or consequential damages +with respect to any claim by LICENSEE or any third party on account of or +arising from this Agreement or use of the Program. + +6. This agreement shall be construed, interpreted and applied in +accordance with the State of California and any legal action arising +out of this Agreement or use of the Program shall be filed in a court +in the State of California. + +7. Nothing in this Agreement shall be construed as conferring rights to +use in advertising, publicity or otherwise any trademark or the name +of ``Stanford''. + +The mrouted program is COPYRIGHT 1989 by The Board of Trustees of +Leland Stanford Junior University. diff --git a/usr.sbin/mrouted/Makefile b/usr.sbin/mrouted/Makefile new file mode 100644 index 0000000000..b3c1777fa9 --- /dev/null +++ b/usr.sbin/mrouted/Makefile @@ -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 diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c new file mode 100644 index 0000000000..9285ccb95b --- /dev/null +++ b/usr.sbin/mrouted/config.c @@ -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 [disable] [metric ] [threshold ] + */ + + /* + * 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 [srcrt] [metric ] [threshold ] + */ + + /* + * 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 index 0000000000..f01179f5e8 --- /dev/null +++ b/usr.sbin/mrouted/defs.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..662f1cb214 --- /dev/null +++ b/usr.sbin/mrouted/dvmrp.h @@ -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 index 0000000000..24c8099f57 --- /dev/null +++ b/usr.sbin/mrouted/igmp.c @@ -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 index 0000000000..5d7442ba1a --- /dev/null +++ b/usr.sbin/mrouted/inet.c @@ -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 index 0000000000..548247b931 --- /dev/null +++ b/usr.sbin/mrouted/kern.c @@ -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 index 0000000000..6352a97a55 --- /dev/null +++ b/usr.sbin/mrouted/main.c @@ -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 index 0000000000..287df84203 --- /dev/null +++ b/usr.sbin/mrouted/mapper.c @@ -0,0 +1,932 @@ +/* Mapper for connections between MRouteD multicast routers. + * Written by Pavel Curtis + * + * $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 +#include +#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(" ", 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 index 0000000000..ee94b6b1b7 --- /dev/null +++ b/usr.sbin/mrouted/mrinfo.c @@ -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 +#include +#include "defs.h" + +#define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */ +#define DEFAULT_RETRIES 3 /* How many times to ask each router */ + +u_long our_addr, target_addr = 0; /* in NET order */ +int debug = 0; +int retries = DEFAULT_RETRIES; +int timeout = DEFAULT_TIMEOUT; +int target_level; + +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 index 0000000000..5013bc7c9e --- /dev/null +++ b/usr.sbin/mrouted/mrouted.8 @@ -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 [disable] [metric ] [threshold ] + + tunnel [metric ] [threshold ] [srcrt] + +.fi +The phyint command can be used to disable multicast routing on the physical +interface identified by local IP address , 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 and remote IP address , 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 index 0000000000..8f3135d6e6 --- /dev/null +++ b/usr.sbin/mrouted/mrouted.conf @@ -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 [disable] [metric ] [threshold ] +# tunnel [srcrt] [metric ] [threshold ] +# +# 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 index 0000000000..25518c13e4 --- /dev/null +++ b/usr.sbin/mrouted/route.c @@ -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 index 0000000000..2e7aa3303b --- /dev/null +++ b/usr.sbin/mrouted/route.h @@ -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 index 0000000000..16700f96dc --- /dev/null +++ b/usr.sbin/mrouted/vif.c @@ -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 index 0000000000..8b4b46bfa9 --- /dev/null +++ b/usr.sbin/mrouted/vif.h @@ -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 */ -- 2.20.1