summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/dvmrpd/Makefile19
-rw-r--r--usr.sbin/dvmrpd/ask_nbrs2.c80
-rw-r--r--usr.sbin/dvmrpd/buffer.c218
-rw-r--r--usr.sbin/dvmrpd/control.c297
-rw-r--r--usr.sbin/dvmrpd/control.h50
-rw-r--r--usr.sbin/dvmrpd/dvmrp.h129
-rw-r--r--usr.sbin/dvmrpd/dvmrpd.878
-rw-r--r--usr.sbin/dvmrpd/dvmrpd.c497
-rw-r--r--usr.sbin/dvmrpd/dvmrpd.conf.5167
-rw-r--r--usr.sbin/dvmrpd/dvmrpd.h447
-rw-r--r--usr.sbin/dvmrpd/dvmrpe.c411
-rw-r--r--usr.sbin/dvmrpd/dvmrpe.h212
-rw-r--r--usr.sbin/dvmrpd/graft.c80
-rw-r--r--usr.sbin/dvmrpd/graft_ack.c80
-rw-r--r--usr.sbin/dvmrpd/group.c497
-rw-r--r--usr.sbin/dvmrpd/igmp.c242
-rw-r--r--usr.sbin/dvmrpd/igmp.h111
-rw-r--r--usr.sbin/dvmrpd/imsg.c178
-rw-r--r--usr.sbin/dvmrpd/in_cksum.c90
-rw-r--r--usr.sbin/dvmrpd/interface.c688
-rw-r--r--usr.sbin/dvmrpd/kmroute.c256
-rw-r--r--usr.sbin/dvmrpd/kroute.c398
-rw-r--r--usr.sbin/dvmrpd/log.c225
-rw-r--r--usr.sbin/dvmrpd/log.h33
-rw-r--r--usr.sbin/dvmrpd/nbrs2.c80
-rw-r--r--usr.sbin/dvmrpd/neighbor.c427
-rw-r--r--usr.sbin/dvmrpd/packet.c288
-rw-r--r--usr.sbin/dvmrpd/parse.y786
-rw-r--r--usr.sbin/dvmrpd/printconf.c79
-rw-r--r--usr.sbin/dvmrpd/probe.c139
-rw-r--r--usr.sbin/dvmrpd/prune.c80
-rw-r--r--usr.sbin/dvmrpd/rde.c281
-rw-r--r--usr.sbin/dvmrpd/rde.h82
-rw-r--r--usr.sbin/dvmrpd/rde_mfc.c218
-rw-r--r--usr.sbin/dvmrpd/rde_srt.c246
-rw-r--r--usr.sbin/dvmrpd/report.c304
36 files changed, 8493 insertions, 0 deletions
diff --git a/usr.sbin/dvmrpd/Makefile b/usr.sbin/dvmrpd/Makefile
new file mode 100644
index 00000000000..13dcb621791
--- /dev/null
+++ b/usr.sbin/dvmrpd/Makefile
@@ -0,0 +1,19 @@
+# $OpenBSD: Makefile,v 1.1 2006/06/01 14:12:20 norby Exp $
+
+PROG= dvmrpd
+SRCS= ask_nbrs2.c buffer.c control.c dvmrpd.c dvmrpe.c graft.c graft_ack.c \
+ group.c igmp.c imsg.c interface.c in_cksum.c kmroute.c kroute.c log.c \
+ nbrs2.c neighbor.c packet.c parse.y printconf.c probe.c prune.c rde.c \
+ rde_mfc.c rde_srt.c report.c
+
+MAN= dvmrpd.8 dvmrpd.conf.5
+
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare -g
+YFLAGS=
+LDADD+= -levent
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dvmrpd/ask_nbrs2.c b/usr.sbin/dvmrpd/ask_nbrs2.c
new file mode 100644
index 00000000000..2080a0a253b
--- /dev/null
+++ b/usr.sbin/dvmrpd/ask_nbrs2.c
@@ -0,0 +1,80 @@
+/* $OpenBSD: ask_nbrs2.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <strings.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+/* DVMRP ask neighbors2 packet handling */
+int
+send_ask_nbrs2(struct iface *iface, struct in_addr addr, void *data, int len)
+{
+ struct sockaddr_in dst;
+ struct buf *buf;
+ struct dvmrp_hdr *dvmrp_hdr;
+ int ret = 0;
+
+ log_debug("send_ask_nbrs2: interface %s addr %s",
+ iface->name, inet_ntoa(addr));
+
+ if (iface->passive)
+ return (0);
+
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_ask_nbrs2");
+
+ /* DVMRP header */
+ if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT_ACK))
+ goto fail;
+
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr.s_addr = addr.s_addr;
+
+ /* update chksum */
+ dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr));
+ dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_ask_nbrs2");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_ask_nbrs2(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ log_debug("recv_ask_nbrs2: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return;
+}
diff --git a/usr.sbin/dvmrpd/buffer.c b/usr.sbin/dvmrpd/buffer.c
new file mode 100644
index 00000000000..751e0ac45eb
--- /dev/null
+++ b/usr.sbin/dvmrpd/buffer.c
@@ -0,0 +1,218 @@
+/* $OpenBSD: buffer.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dvmrpd.h"
+
+int buf_realloc(struct buf *, size_t);
+void buf_enqueue(struct msgbuf *, struct buf *);
+void buf_dequeue(struct msgbuf *, struct buf *);
+
+struct buf *
+buf_open(size_t len)
+{
+ struct buf *buf;
+
+ if ((buf = calloc(1, sizeof(struct buf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = buf->max = len;
+
+ return (buf);
+}
+
+struct buf *
+buf_dynamic(size_t len, size_t max)
+{
+ struct buf *buf;
+
+ if (max < len)
+ return (NULL);
+
+ if ((buf = buf_open(len)) == NULL)
+ return (NULL);
+
+ if (max > 0)
+ buf->max = max;
+
+ return (buf);
+}
+
+int
+buf_realloc(struct buf *buf, size_t len)
+{
+ u_char *b;
+
+ /* on static buffers max is eq size and so the following fails */
+ if (buf->wpos + len > buf->max) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ b = realloc(buf->buf, buf->wpos + len);
+ if (b == NULL)
+ return (-1);
+ buf->buf = b;
+ buf->size = buf->wpos + len;
+
+ return (0);
+}
+
+int
+buf_add(struct buf *buf, void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ if (buf_realloc(buf, len) == -1)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+void *
+buf_reserve(struct buf *buf, size_t len)
+{
+ void *b;
+
+ if (buf->wpos + len > buf->size)
+ if (buf_realloc(buf, len) == -1)
+ return (NULL);
+
+ b = buf->buf + buf->wpos;
+ buf->wpos += len;
+ return (b);
+}
+
+void *
+buf_seek(struct buf *buf, size_t pos, size_t len)
+{
+ /* only allowed to seek in already written parts */
+ if (pos + len > buf->wpos)
+ return (NULL);
+
+ return (buf->buf + pos);
+}
+
+int
+buf_close(struct msgbuf *msgbuf, struct buf *buf)
+{
+ buf_enqueue(msgbuf, buf);
+ return (1);
+}
+
+void
+buf_free(struct buf *buf)
+{
+ free(buf->buf);
+ free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+ msgbuf->queued = 0;
+ msgbuf->fd = -1;
+ TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+ struct buf *buf;
+
+ while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+ buf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct buf *buf, *next;
+ int i = 0;
+ ssize_t n;
+ struct msghdr msg;
+
+ bzero(&iov, sizeof(iov));
+ bzero(&msg, sizeof(msg));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->size - buf->rpos;
+ i++;
+ }
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = i;
+
+ if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+ if (errno == EAGAIN || errno == ENOBUFS ||
+ errno == EINTR) /* try later */
+ return (0);
+ else
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (-2);
+ }
+
+ for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+ buf = next) {
+ next = TAILQ_NEXT(buf, entry);
+ if (buf->rpos + n >= buf->size) {
+ n -= buf->size - buf->rpos;
+ buf_dequeue(msgbuf, buf);
+ } else {
+ buf->rpos += n;
+ n = 0;
+ }
+ }
+
+ return (0);
+}
+
+void
+buf_enqueue(struct msgbuf *msgbuf, struct buf *buf)
+{
+ TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+ msgbuf->queued++;
+}
+
+void
+buf_dequeue(struct msgbuf *msgbuf, struct buf *buf)
+{
+ TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+ msgbuf->queued--;
+ buf_free(buf);
+}
diff --git a/usr.sbin/dvmrpd/control.c b/usr.sbin/dvmrpd/control.c
new file mode 100644
index 00000000000..04664401437
--- /dev/null
+++ b/usr.sbin/dvmrpd/control.c
@@ -0,0 +1,297 @@
+/* $OpenBSD: control.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "dvmrpe.h"
+#include "log.h"
+#include "control.h"
+
+#define CONTROL_BACKLOG 5
+
+int control_imsg_relay(struct imsg *imsg);
+
+struct ctl_conn *control_connbyfd(int);
+struct ctl_conn *control_connbypid(pid_t);
+void control_close(int);
+
+int
+control_init(void)
+{
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("control_init: socket");
+ return (-1);
+ }
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, DVMRPD_SOCKET, sizeof(sun.sun_path));
+
+ if (unlink(DVMRPD_SOCKET) == -1)
+ if (errno != ENOENT) {
+ log_warn("control_init: unlink %s", DVMRPD_SOCKET);
+ close(fd);
+ return (-1);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("control_init: bind: %s", DVMRPD_SOCKET);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(DVMRPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("control_init: chmod");
+ close(fd);
+ (void)unlink(DVMRPD_SOCKET);
+ return (-1);
+ }
+
+ session_socket_blockmode(fd, BM_NONBLOCK);
+ control_state.fd = fd;
+
+ return (0);
+}
+
+int
+control_listen(void)
+{
+
+ if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
+ log_warn("control_listen: listen");
+ return (-1);
+ }
+
+ event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST,
+ control_accept, NULL);
+ event_add(&control_state.ev, NULL);
+
+ return (0);
+}
+
+void
+control_cleanup(void)
+{
+ unlink(DVMRPD_SOCKET);
+}
+
+/* ARGSUSED */
+void
+control_accept(int listenfd, short event, void *bula)
+{
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct ctl_conn *c;
+
+ len = sizeof(sun);
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)&sun, &len)) == -1) {
+ if (errno != EWOULDBLOCK && errno != EINTR)
+ log_warn("control_accept");
+ return;
+ }
+
+ session_socket_blockmode(connfd, BM_NONBLOCK);
+
+ if ((c = malloc(sizeof(struct ctl_conn))) == NULL) {
+ log_warn("control_accept");
+ return;
+ }
+
+ imsg_init(&c->ibuf, connfd, control_dispatch_imsg);
+ c->ibuf.events = EV_READ;
+ event_set(&c->ibuf.ev, c->ibuf.fd, c->ibuf.events,
+ c->ibuf.handler, &c->ibuf);
+ event_add(&c->ibuf.ev, NULL);
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+struct ctl_conn *
+control_connbypid(pid_t pid)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+void
+control_close(int fd)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbyfd(fd)) == NULL)
+ log_warn("control_close: fd %d: not found", fd);
+
+ msgbuf_clear(&c->ibuf.w);
+ TAILQ_REMOVE(&ctl_conns, c, entry);
+
+ event_del(&c->ibuf.ev);
+ close(c->ibuf.fd);
+ free(c);
+}
+
+/* ARGSUSED */
+void
+control_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct ctl_conn *c;
+ struct imsg imsg;
+ int n;
+ unsigned int ifidx;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warn("control_dispatch_imsg: fd %d: not found", fd);
+ return;
+ }
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(&c->ibuf)) <= 0) {
+ control_close(fd);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&c->ibuf.w) < 0) {
+ control_close(fd);
+ return;
+ }
+ imsg_event_add(&c->ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
+ control_close(fd);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_MFC_COUPLE:
+ case IMSG_CTL_MFC_DECOUPLE:
+ case IMSG_CTL_RELOAD:
+ dvmrpe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_IFACE:
+ if (imsg.hdr.len == IMSG_HEADER_SIZE +
+ sizeof(ifidx)) {
+ memcpy(&ifidx, imsg.data, sizeof(ifidx));
+ dvmrpe_iface_ctl(c, ifidx);
+ imsg_compose(&c->ibuf, IMSG_CTL_END, 0,
+ 0, NULL, 0);
+ }
+ break;
+ case IMSG_CTL_SHOW_IGMP:
+ if (imsg.hdr.len == IMSG_HEADER_SIZE +
+ sizeof(ifidx)) {
+ memcpy(&ifidx, imsg.data, sizeof(ifidx));
+ dvmrpe_iface_igmp_ctl(c, ifidx);
+ imsg_compose(&c->ibuf, IMSG_CTL_END, 0,
+ 0, NULL, 0);
+ }
+ break;
+ case IMSG_CTL_SHOW_NBR:
+ dvmrpe_nbr_ctl(c);
+ break;
+ case IMSG_CTL_SHOW_RIB:
+ case IMSG_CTL_SHOW_MFC:
+ case IMSG_CTL_SHOW_SUM:
+ c->ibuf.pid = imsg.hdr.pid;
+ dvmrpe_imsg_compose_rde(imsg.hdr.type, 0, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ default:
+ log_debug("control_dispatch_imsg: "
+ "error handling imsg %d", imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ imsg_event_add(&c->ibuf);
+}
+
+int
+control_imsg_relay(struct imsg *imsg)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
+ return (0);
+
+ return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid,
+ imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
+}
+
+void
+session_socket_blockmode(int fd, enum blockmodes bm)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fnctl F_GETFL");
+
+ if (bm == BM_NONBLOCK)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fnctl F_SETFL");
+}
diff --git a/usr.sbin/dvmrpd/control.h b/usr.sbin/dvmrpd/control.h
new file mode 100644
index 00000000000..d7518e5275a
--- /dev/null
+++ b/usr.sbin/dvmrpd/control.h
@@ -0,0 +1,50 @@
+/* $OpenBSD: control.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CONTROL_H_
+#define _CONTROL_H_
+
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <event.h>
+
+struct {
+ struct event ev;
+ int fd;
+} control_state;
+
+enum blockmodes {
+ BM_NORMAL,
+ BM_NONBLOCK
+};
+
+struct ctl_conn {
+ TAILQ_ENTRY(ctl_conn) entry;
+ struct imsgbuf ibuf;
+};
+
+int control_init(void);
+int control_listen(void);
+void control_accept(int, short, void *);
+void control_dispatch_imsg(int, short, void *);
+int control_imsg_relay(struct imsg *);
+void control_cleanup(void);
+
+void session_socket_blockmode(int, enum blockmodes);
+
+#endif /* _CONTROL_H_ */
diff --git a/usr.sbin/dvmrpd/dvmrp.h b/usr.sbin/dvmrpd/dvmrp.h
new file mode 100644
index 00000000000..cc8e264df2c
--- /dev/null
+++ b/usr.sbin/dvmrpd/dvmrp.h
@@ -0,0 +1,129 @@
+/* $OpenBSD: dvmrp.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* DVMRP protocol definitions */
+
+#ifndef _DVMRP_H_
+#define _DVMRP_H_
+
+/* misc */
+#define IPPROTO_DVMRP 2
+#define AllDVMRPRouters "224.0.0.4"
+
+#define DEFAULT_PROBE_INTERVAL 10
+#define NBR_TMOUT 35
+#define MIN_FLASH_UPDATE_INTERVAL 5
+#define ROUTE_REPORT_INTERVAL 60
+#define ROUTE_EXPIRATION_TIME 140
+#define ROUTE_HOLD_DOWN 2 * ROUTE_REPORT_INTERVAL
+#define MAX_PRUNE_LIFETIME 2 * 3600 /* 2 hours */
+#define PRUNE_RETRANS_TIME 3 /* with exp. back off */
+#define GRAFT_RETRANS_TIME 5 /* with exp. back off */
+
+#define DEFAULT_NBR_TMOUT 86400 /* 24 hours */
+ /* remove neighbor completely */
+
+#define DEFAULT_CACHE_LIFETIME 300
+
+#define DEFAULT_METRIC 1
+#define MIN_METRIC 1
+#define MAX_METRIC 31
+#define INFINITY_METRIC 31
+
+#define LAST_MASK 0x80 /* route reports */
+#define METRIC_MASK ~LAST_MASK /* route reports */
+
+#define DVMRP_MAJOR_VERSION 3
+#define DVMRP_MINOR_VERSION 255
+
+/* DVMRP packet types */
+#define PKT_TYPE_DVMRP 0x13
+
+#define DVMRP_CODE_PROBE 0x01
+#define DVMRP_CODE_REPORT 0x02
+#define DVMRP_CODE_ASK_NBRS 0x03 /* obsolete */
+#define DVMRP_CODE_NBRS 0x04 /* obsolete */
+#define DVMRP_CODE_ASK_NBRS2 0x05
+#define DVMRP_CODE_NBRS2 0x06
+#define DVMRP_CODE_PRUNE 0x07
+#define DVMRP_CODE_GRAFT 0x08
+#define DVMRP_CODE_GRAFT_ACK 0x09
+
+/* DVMRP command types */
+#define DVMRP_CMD_NULL 0
+#define DVMRP_CMD_AF_INDICATOR 2
+#define DVMRP_CMD_SUBNETMASK 3
+#define DVMRP_CMD_METRIC 4
+#define DVMRP_CMD_FLAGS0 5
+#define DVMRP_CMD_INFINITY 6
+#define DVMRP_CMD_DEST_ADDR 7
+#define DVMRP_CMD_REQ_DEST_ADDR 8
+#define DVMRP_CMD_NON_MEM_REPORT 9
+#define DVMRP_CMD_NON_MEM_CANCEL 10
+
+/* DVMRP capabilities */
+#define DVMRP_CAP_LEAF 0x01
+#define DVMRP_CAP_PRUNE 0x02
+#define DVMRP_CAP_GENID 0x04
+#define DVMRP_CAP_MTRACE 0x08
+#define DVMRP_CAP_SNMP 0x10
+#define DVMRP_CAP_NETMASK 0x20
+#define DVMRP_CAP_DEFAULT (DVMRP_CAP_PRUNE | DVMRP_CAP_GENID | \
+ DVMRP_CAP_MTRACE)
+
+/* DVMRP header */
+struct dvmrp_hdr {
+ u_int8_t type;
+ u_int8_t code;
+ u_int16_t chksum;
+ u_int8_t dummy;
+ u_int8_t capabilities;
+ u_int8_t minor_version;
+ u_int8_t major_version;
+};
+
+/* Prune header */
+struct prune_hdr {
+ u_int32_t src_host_addr;
+ u_int32_t group_addr;
+ u_int32_t lifetime;
+ u_int32_t src_netmask;
+};
+
+/* Graft and Graft Ack header */
+struct graft_hdr {
+ u_int32_t src_host_addr;
+ u_int32_t group_addr;
+ u_int32_t src_netmask;
+};
+
+struct igmpmsg {
+ u_int32_t unused1;
+ u_int32_t unused2;
+ u_int8_t im_msgtype; /* what type of message */
+#define IGMPMSG_NOCACHE 1 /* no MFC in the kernel */
+#define IGMPMSG_WRONGVIF 2 /* packet came from wrong interface */
+#define IGMPMSG_WHOLEPKT 3 /* PIM pkt for user level encap. */
+#define IGMPMSG_BW_UPCALL 4 /* BW monitoring upcall */
+ u_int8_t im_mbz; /* must be zero */
+ u_int8_t im_vif; /* vif rec'd on */
+ u_int8_t unused3;
+ struct in_addr im_src, im_dst;
+};
+
+#endif /* !_DVMRP_H_ */
diff --git a/usr.sbin/dvmrpd/dvmrpd.8 b/usr.sbin/dvmrpd/dvmrpd.8
new file mode 100644
index 00000000000..0a5e629b505
--- /dev/null
+++ b/usr.sbin/dvmrpd/dvmrpd.8
@@ -0,0 +1,78 @@
+.\" $OpenBSD: dvmrpd.8,v 1.1 2006/06/01 14:12:20 norby Exp $
+.\"
+.\" Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd February 27, 2006
+.Dt DVMRPD 8
+.Os
+.Sh NAME
+.Nm dvmrpd
+.Nd "Distance Vector Multicast Routing Protocol daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnv
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is an Distance Vector Multicast Routing Protocol
+.Pq DVMRP
+daemon which manages multicast routing tables.
+Its main purpose is to keep multicast routing tables in sync.
+.Pp
+A running
+.Nm
+can be controlled with the
+.Xr dvmrpctl 8
+utility.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground and log to
+.Em stderr .
+.It Fl f Ar file
+Specify an alternative configuration file.
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
+.It Fl v
+Produce more verbose output.
+.El
+.Sh FILES
+.Bl -tag -width "/var/run/dvmrpd.sockXX" -compact
+.It /etc/dvmrpd.conf
+Default
+.Nm
+configuration file.
+.It /var/run/dvmrpd.sock
+Unix-domain socket used for communication with
+.Xr dvmrpctl 8 .
+.El
+.Sh SEE ALSO
+.Xr dvmrpd.conf 5 ,
+.Xr dvmrpctl 8
+.Rs
+.%R "draft-ietf-idmr-dvmrp-v3-11"
+.%T "DVMRP Version 3"
+.%D August 2000
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 4.0 .
diff --git a/usr.sbin/dvmrpd/dvmrpd.c b/usr.sbin/dvmrpd/dvmrpd.c
new file mode 100644
index 00000000000..62a1fe8ddca
--- /dev/null
+++ b/usr.sbin/dvmrpd/dvmrpd.c
@@ -0,0 +1,497 @@
+/* $OpenBSD: dvmrpd.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <event.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "dvmrpe.h"
+#include "control.h"
+#include "log.h"
+#include "rde.h"
+
+void main_sig_handler(int, short, void *);
+__dead void usage(void);
+void dvmrpd_shutdown(void);
+int check_child(pid_t, const char *);
+
+void main_dispatch_dvmrpe(int, short, void *);
+void main_dispatch_rde(int, short, void *);
+void main_imsg_compose_dvmrpe(int, pid_t, void *, u_int16_t);
+void main_imsg_compose_rde(int, pid_t, void *, u_int16_t);
+
+int check_file_secrecy(int, const char *);
+
+int pipe_parent2dvmrpe[2];
+int pipe_parent2rde[2];
+int pipe_dvmrpe2rde[2];
+
+struct dvmrpd_conf *conf = NULL;
+struct imsgbuf *ibuf_dvmrpe;
+struct imsgbuf *ibuf_rde;
+
+pid_t dvmrpe_pid;
+pid_t rde_pid;
+
+void
+main_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * signal handler rules don't apply, libevent decouples for us
+ */
+ int die = 0;
+
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ die = 1;
+ /* FALLTHROUGH */
+ case SIGCHLD:
+ if (check_child(dvmrpe_pid, "dvmrp engine")) {
+ dvmrpe_pid = 0;
+ die = 1;
+ }
+ if (check_child(rde_pid, "route decision engine")) {
+ rde_pid = 0;
+ die = 1;
+ }
+ if (die)
+ dvmrpd_shutdown();
+ break;
+ case SIGHUP:
+ /* reconfigure */
+ /* ... */
+ break;
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-dnv] [-f file]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ char *conffile;
+ int ch, opts = 0;
+ int debug = 0;
+ int ipmforwarding;
+ int mib[4];
+ size_t len;
+
+ conffile = CONF_FILE;
+ dvmrpd_process = PROC_MAIN;
+
+ /* start logging */
+ log_init(1);
+
+ while ((ch = getopt(argc, argv, "df:nv")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'n':
+ opts |= DVMRPD_OPT_NOACTION;
+ break;
+ case 'v':
+ if (opts & DVMRPD_OPT_VERBOSE)
+ opts |= DVMRPD_OPT_VERBOSE2;
+ opts |= DVMRPD_OPT_VERBOSE;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ log_init(debug);
+
+ /* multicast IP forwarding must be enabled */
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET;
+ mib[2] = IPPROTO_IP;
+ mib[3] = IPCTL_MFORWARDING;
+ len = sizeof(ipmforwarding);
+ if (sysctl(mib, 4, &ipmforwarding, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+
+ if (!ipmforwarding)
+ errx(1, "multicast IP forwarding not enabled");
+
+ /* fetch interfaces early */
+ kif_init();
+
+ /* parse config file */
+ if ((conf = parse_config(conffile, opts)) == NULL )
+ exit(1);
+
+ if (conf->opts & DVMRPD_OPT_NOACTION) {
+ if (conf->opts & DVMRPD_OPT_VERBOSE)
+ print_config(conf);
+ else
+ fprintf(stderr, "configuration OK\n");
+ exit(0);
+ }
+
+ /* check for root privileges */
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ /* check for dvmrpd user */
+ if (getpwnam(DVMRPD_USER) == NULL)
+ errx(1, "unknown user %s", DVMRPD_USER);
+
+ endpwent();
+
+ if (!debug)
+ daemon(1, 0);
+
+ log_info("startup");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
+ pipe_parent2dvmrpe) == -1)
+ fatal("socketpair");
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2rde) == -1)
+ fatal("socketpair");
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_dvmrpe2rde) == -1)
+ fatal("socketpair");
+ session_socket_blockmode(pipe_parent2dvmrpe[0], BM_NONBLOCK);
+ session_socket_blockmode(pipe_parent2dvmrpe[1], BM_NONBLOCK);
+ session_socket_blockmode(pipe_parent2rde[0], BM_NONBLOCK);
+ session_socket_blockmode(pipe_parent2rde[1], BM_NONBLOCK);
+ session_socket_blockmode(pipe_dvmrpe2rde[0], BM_NONBLOCK);
+ session_socket_blockmode(pipe_dvmrpe2rde[1], BM_NONBLOCK);
+
+ /* start children */
+ rde_pid = rde(conf, pipe_parent2rde, pipe_dvmrpe2rde,
+ pipe_parent2dvmrpe);
+ dvmrpe_pid = dvmrpe(conf, pipe_parent2dvmrpe, pipe_dvmrpe2rde,
+ pipe_parent2rde);
+
+ /* create the raw ip socket */
+ if ((conf->mroute_socket = socket(AF_INET, SOCK_RAW,
+ IPPROTO_IGMP)) == -1)
+ fatal("error creating raw socket");
+
+ if_set_recvbuf(conf->mroute_socket);
+
+ if (mrt_init(conf->mroute_socket))
+ fatal("multicast routing not enabled in kernel");
+
+ /* show who we are */
+ setproctitle("parent");
+
+ event_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGINT, main_sig_handler, NULL);
+ signal_set(&ev_sighup, SIGTERM, main_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+
+ /* setup pipes to children */
+ close(pipe_parent2dvmrpe[1]);
+ close(pipe_parent2rde[1]);
+ close(pipe_dvmrpe2rde[0]);
+ close(pipe_dvmrpe2rde[1]);
+
+ if ((ibuf_dvmrpe = malloc(sizeof(struct imsgbuf))) == NULL ||
+ (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_dvmrpe, pipe_parent2dvmrpe[0], main_dispatch_dvmrpe);
+ imsg_init(ibuf_rde, pipe_parent2rde[0], main_dispatch_rde);
+
+ /* setup event handler */
+ ibuf_dvmrpe->events = EV_READ;
+ event_set(&ibuf_dvmrpe->ev, ibuf_dvmrpe->fd, ibuf_dvmrpe->events,
+ ibuf_dvmrpe->handler, ibuf_dvmrpe);
+ event_add(&ibuf_dvmrpe->ev, NULL);
+
+ ibuf_rde->events = EV_READ;
+ event_set(&ibuf_rde->ev, ibuf_rde->fd, ibuf_rde->events,
+ ibuf_rde->handler, ibuf_rde);
+ event_add(&ibuf_rde->ev, NULL);
+
+ if (kmr_init(!(conf->flags & DVMRPD_FLAG_NO_FIB_UPDATE)) == -1)
+ dvmrpd_shutdown();
+
+ event_set(&conf->ev, conf->mroute_socket, EV_READ|EV_PERSIST,
+ kmr_recv_msg, conf);
+ event_add(&conf->ev, NULL);
+
+ event_dispatch();
+
+ dvmrpd_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+void
+dvmrpd_shutdown(void)
+{
+ struct iface *iface;
+ pid_t pid;
+
+ if (dvmrpe_pid)
+ kill(dvmrpe_pid, SIGTERM);
+
+ if (rde_pid)
+ kill(rde_pid, SIGTERM);
+
+ control_cleanup();
+ kmr_shutdown();
+ kr_shutdown();
+
+ LIST_FOREACH(iface, &conf->iface_list, entry) {
+ if_del(iface);
+ }
+
+ mrt_done(conf->mroute_socket);
+
+ do {
+ if ((pid = wait(NULL)) == -1 &&
+ errno != EINTR && errno != ECHILD)
+ fatal("wait");
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ msgbuf_clear(&ibuf_dvmrpe->w);
+ free(ibuf_dvmrpe);
+ msgbuf_clear(&ibuf_rde->w);
+ free(ibuf_rde);
+
+ log_info("terminating");
+ exit(0);
+}
+
+int
+check_child(pid_t pid, const char *pname)
+{
+ int status;
+
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ if (WIFEXITED(status)) {
+ log_warnx("lost child: %s exited", pname);
+ return (1);
+ }
+ if (WIFSIGNALED(status)) {
+ log_warnx("lost child: %s terminated; signal %d",
+ pname, WTERMSIG(status));
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* imsg handling */
+void
+main_dispatch_dvmrpe(int fd, short event, void *bula)
+{
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ int n;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_RELOAD:
+ log_debug("main_dispatch_dvmrpe: IMSG_CTL_RELOAD");
+ /* reconfig */
+ break;
+ case IMSG_CTL_MFC_COUPLE:
+ kmr_mfc_couple();
+ break;
+ case IMSG_CTL_MFC_DECOUPLE:
+ kmr_mfc_decouple();
+ break;
+ default:
+ log_debug("main_dispatch_dvmrpe: error handling "
+ "imsg %d", imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+main_dispatch_rde(int fd, short event, void *bula)
+{
+ struct mfc mfc;
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ int n;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_MFC_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc))
+ fatalx("invalid size of RDE request");
+ memcpy(&mfc, imsg.data, sizeof(mfc));
+
+ /* add to MFC */
+ mrt_add_mfc(conf->mroute_socket, &mfc);
+ break;
+ case IMSG_MFC_DEL:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc))
+ fatalx("invalid size of RDE request");
+ memcpy(&mfc, imsg.data, sizeof(mfc));
+
+ /* remove from MFC */
+ mrt_del_mfc(conf->mroute_socket, &mfc);
+ break;
+ default:
+ log_debug("main_dispatch_rde: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+main_imsg_compose_dvmrpe(int type, pid_t pid, void *data, u_int16_t datalen)
+{
+ imsg_compose(ibuf_dvmrpe, type, 0, pid, data, datalen);
+}
+
+void
+main_imsg_compose_rde(int type, pid_t pid, void *data, u_int16_t datalen)
+{
+ imsg_compose(ibuf_rde, type, 0, pid, data, datalen);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ log_warn("cannot stat %s", fname);
+ return (-1);
+ }
+
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ log_warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+
+ if (st.st_mode & (S_IRWXG | S_IRWXO)) {
+ log_warnx("%s: group/world readable/writeable", fname);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* this needs to be added here so that dvmrpctl can be used without libevent */
+void
+imsg_event_add(struct imsgbuf *ibuf)
+{
+ ibuf->events = EV_READ;
+ if (ibuf->w.queued)
+ ibuf->events |= EV_WRITE;
+
+ event_del(&ibuf->ev);
+ event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf);
+ event_add(&ibuf->ev, NULL);
+}
diff --git a/usr.sbin/dvmrpd/dvmrpd.conf.5 b/usr.sbin/dvmrpd/dvmrpd.conf.5
new file mode 100644
index 00000000000..8843d29c7fb
--- /dev/null
+++ b/usr.sbin/dvmrpd/dvmrpd.conf.5
@@ -0,0 +1,167 @@
+.\" $OpenBSD: dvmrpd.conf.5,v 1.1 2006/06/01 14:12:20 norby Exp $
+.\"
+.\" Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+.\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
+.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+.\" Copyright (c) 2002 Daniel Hartmeier <dhartmei@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd February 27, 2006
+.Dt DVMRPD.CONF 5
+.Os
+.Sh NAME
+.Nm dvmrpd.conf
+.Nd Distance Vector Multicast Routing Protocol daemon configuration file
+.Sh DESCRIPTION
+The
+.Xr dvmrpd 8
+daemon implements the Distance Vector Multicast Routing Protocol as described
+in draft-ietf-idmr-dvmrp-v3-11 DVMRP Version 3.
+.Sh SECTIONS
+The
+.Nm
+config file is divided into three main sections.
+.Bl -tag -width xxxx
+.It Sy Macros
+User-defined variables may be defined and used later, simplifying the
+configuration file.
+.It Sy Global Configuration
+Global settings for
+.Xr dvmrpd 8 .
+A number of global settings can be overruled in specific areas or interfaces.
+.It Sy Groups
+Groups are used to group similar interfaces, simplifying configuration.
+.El
+.Sh MACROS
+Much like
+.Xr cpp 1
+or
+.Xr m4 1 ,
+macros can be defined that will later be expanded in context.
+Macro names must start with a letter, and may contain letters, digits,
+and underscores.
+Macro names may not be reserved words (for example,
+.Ic area ,
+.Ic interface ,
+or
+.Ic hello-interval ) .
+Macros are not expanded inside quotes.
+.Pp
+For example:
+.Bd -literal -offset indent
+hi="60"
+group {
+ interface em0 {
+ query-interval $hi
+ }
+}
+.Ed
+.Pp
+The same can be accomplished by specifying the query-interval
+globally or within the group declaration.
+.Sh GLOBAL CONFIGURATION
+All interface related settings can be configured globally, per group and per
+interface.
+The only settings that can be set globally and not overruled are listed below.
+.Bl -tag -width Ds
+.It Xo
+.Ic mfc-update
+.Pq Ic yes Ns \&| Ns Ic no
+.Xc
+If set to
+.Ic yes ,
+the kernel Multicast Forwarding Cache is updated with information from the
+daemon.
+The default is
+.Ic yes .
+.El
+.Sh GROUPS
+Groups can be used to easily configure similar interfaces.
+All interface specific parameters can
+be configured per group, overruling the global settings.
+.Bl -tag -width Ds
+.It Ic group
+Specify a group section, grouping one or more interfaces.
+.Bd -literal -offset indent
+group {
+ robustness 4
+
+ interface em0
+ interface em1
+}
+.Ed
+.El
+.Sh INTERFACES
+Each interface can have several parameters configured individually, otherwise
+they are inherited.
+.Bd -literal -offset indent
+interface em0 {
+}
+.Ed
+.Pp
+Interface specific parameters are listed below.
+.Bl -tag -width Ds
+.It Ic igmp-version Ar number
+Do not use a a newer version than specified. Valid range 1-2.
+The default version used is 2.
+.It Ic last-member-query-count Ar count
+The default value is equal to the default robustness variable;
+valid range is 1\-255.
+.It Ic last-member-query-interval Ar seconds
+The default value is 10; valid range is 1\-65535.
+.It Ic metric Ar cost
+Set the interface metric a.k.a. cost. The default value is 1; valid range is
+1-31.
+.It Ic passive
+Prevent transmission and reception of IGMP and DVMRP packets on this interface.
+.It Ic query-interval Ar seconds
+Set the query interval.
+The default value is 125 seconds; valid range is 1\-65535 seconds.
+.It Ic query-response-interval Ar seconds
+Set the query-response-interval, note that this must be less
+than the query-interval.
+The default value is 100 seconds; valid range is 1\-65535 seconds.
+.It Ic robustness Ar factor
+Set the robustness factor, note that this value might impact other variables.
+The default value is 2; valid range is 1\-4.
+.It Ic startup-query-count Ar count
+Set the startup-query-interval, used during startup to speed up the process of
+building the IGMP table for an interface.
+The default value is equal to the default robustness variable; valid range is
+the same as the robustness variable.
+.It Ic startup-query-interval Ar seconds
+Used during startup, see the startup-query-interval.
+The default value is query-interval / 4; valid range is the same as the
+query-interval.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/dvmrpd.conf" -compact
+.It Pa /etc/dvmrpd.conf
+.Xr dvmrpd 8
+configuration file
+.El
+.Sh SEE ALSO
+.Xr dvmrpctl 8 ,
+.Xr dvmrpd 8 ,
+.Xr rc.conf.local 8
+.Rs
+.%R "draft-ietf-idmr-dvmrp-v3-11"
+.%T "DVMRP Version 3"
+.%D August 2000
+.Re
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 4.0 .
diff --git a/usr.sbin/dvmrpd/dvmrpd.h b/usr.sbin/dvmrpd/dvmrpd.h
new file mode 100644
index 00000000000..bb23678559d
--- /dev/null
+++ b/usr.sbin/dvmrpd/dvmrpd.h
@@ -0,0 +1,447 @@
+/* $OpenBSD: dvmrpd.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DVMRPD_H_
+#define _DVMRPD_H_
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <event.h>
+
+#define CONF_FILE "/etc/dvmrpd.conf"
+#define DVMRPD_SOCKET "/var/run/dvmrpd.sock"
+#define DVMRPD_USER "_dvmrpd"
+
+#define NBR_HASHSIZE 128
+
+#define NBR_IDSELF 1
+#define NBR_CNTSTART (NBR_IDSELF + 1)
+
+#define READ_BUF_SIZE 65535
+#define RT_BUF_SIZE 16384
+
+#define DVMRPD_FLAG_NO_FIB_UPDATE 0x0001
+
+#define F_DVMRPD_INSERTED 0x0001
+#define F_KERNEL 0x0002
+#define F_CONNECTED 0x0004
+#define F_DOWN 0x0010
+#define F_STATIC 0x0020
+#define F_LONGER 0x0040
+
+#define MAXVIFS 32 /* XXX */
+
+/* buffer */
+struct buf {
+ TAILQ_ENTRY(buf) entry;
+ u_char *buf;
+ size_t size;
+ size_t max;
+ size_t wpos;
+ size_t rpos;
+ int fd;
+};
+
+struct msgbuf {
+ u_int32_t queued;
+ int fd;
+ TAILQ_HEAD(, buf) bufs;
+};
+
+#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE 8192
+
+struct buf_read {
+ u_char buf[READ_BUF_SIZE];
+ u_char *rptr;
+ size_t wpos;
+};
+
+struct imsgbuf {
+ TAILQ_HEAD(, imsg_fd) fds;
+ struct buf_read r;
+ struct msgbuf w;
+ struct event ev;
+ void (*handler)(int, short, void *);
+ int fd;
+ pid_t pid;
+ short events;
+};
+
+enum imsg_type {
+ IMSG_NONE,
+ IMSG_CTL_RELOAD,
+ IMSG_CTL_SHOW_IFACE,
+ IMSG_CTL_SHOW_IGMP,
+ IMSG_CTL_SHOW_NBR,
+ IMSG_CTL_SHOW_RIB,
+ IMSG_CTL_SHOW_MFC,
+ IMSG_CTL_MFC_COUPLE,
+ IMSG_CTL_MFC_DECOUPLE,
+ IMSG_CTL_KROUTE,
+ IMSG_CTL_KROUTE_ADDR,
+ IMSG_CTL_IFINFO,
+ IMSG_CTL_SHOW_SUM,
+ IMSG_CTL_END,
+ IMSG_IFINFO,
+ IMSG_ROUTE_REPORT,
+ IMSG_FULL_ROUTE_REPORT,
+ IMSG_FULL_ROUTE_REPORT_END,
+ IMSG_MFC_ADD,
+ IMSG_MFC_DEL
+};
+
+struct imsg_hdr {
+ enum imsg_type type;
+ u_int16_t len;
+ u_int32_t peerid;
+ pid_t pid;
+};
+
+struct imsg {
+ struct imsg_hdr hdr;
+ void *data;
+};
+
+struct imsg_fd {
+ TAILQ_ENTRY(imsg_fd) entry;
+ int fd;
+};
+
+/* interface states */
+#define IF_STA_DOWN 0x01
+#define IF_STA_QUERIER 0x02
+#define IF_STA_NONQUERIER 0x04
+#define IF_STA_ANY 0x07
+#define IF_STA_ACTIVE (~IF_STA_DOWN)
+
+/* interface events */
+enum iface_event {
+ IF_EVT_NOTHING,
+ IF_EVT_UP,
+ IF_EVT_QTMOUT, /* query timer expired */
+ IF_EVT_QRECVD, /* query received, check for lower IP */
+ IF_EVT_QPRSNTTMOUT, /* other querier present timeout */
+ IF_EVT_DOWN
+};
+
+/* interface actions */
+enum iface_action {
+ IF_ACT_NOTHING,
+ IF_ACT_STRT,
+ IF_ACT_QPRSNT,
+ IF_ACT_RST
+};
+
+/* interface types */
+enum iface_type {
+ IF_TYPE_POINTOPOINT,
+ IF_TYPE_BROADCAST
+};
+
+/* neighbor states */
+#define NBR_STA_DOWN 0x01
+#define NBR_STA_1_WAY 0x02
+#define NBR_STA_2_WAY 0x04
+#define NBR_STA_ACTIVE (~NBR_STA_DOWN)
+#define NBR_STA_ANY 0xff
+
+struct group {
+ TAILQ_ENTRY(group) entry;
+ struct event dead_timer;
+ struct event v1_host_timer;
+ struct event retrans_timer;
+
+ struct in_addr addr;
+
+ struct iface *iface;
+
+ time_t uptime;
+ int state;
+};
+
+struct mfc {
+ struct in_addr origin;
+ struct in_addr group;
+ u_short ifindex;
+ u_int8_t ttls[MAXVIFS];
+};
+
+TAILQ_HEAD(rr_head, rr_entry);
+
+struct iface {
+ LIST_ENTRY(iface) entry;
+ struct event probe_timer;
+ struct event query_timer;
+ struct event querier_present_timer;
+ time_t uptime;
+ LIST_HEAD(, nbr) nbr_list;
+ TAILQ_HEAD(, group) group_list;
+ struct rr_head rr_list;
+
+ char name[IF_NAMESIZE];
+ struct in_addr addr;
+ struct in_addr dst;
+ struct in_addr mask;
+ struct in_addr querier; /* designated querier */
+
+ u_int32_t gen_id;
+ u_int32_t group_cnt;
+
+ u_int32_t baudrate;
+ u_int32_t probe_interval;
+
+ u_int32_t query_interval;
+ u_int32_t query_resp_interval;
+ u_int32_t recv_query_resp_interval;
+ u_int32_t group_member_interval;
+ u_int32_t querier_present_interval;
+ u_int32_t startup_query_interval;
+ u_int32_t startup_query_cnt;
+ u_int32_t last_member_query_interval;
+ u_int32_t last_member_query_cnt;
+ u_int32_t last_member_query_time;
+ u_int32_t v1_querier_present_tmout;
+ u_int32_t v1_host_present_interval;
+ u_int32_t startup_query_counter; /* actual counter */
+ u_int32_t dead_interval;
+
+ unsigned int ifindex; /* ifindex and vif */
+ int fd;
+ int state;
+ int mtu;
+ u_int16_t flags;
+ u_int16_t metric;
+ enum iface_type type;
+
+ u_int8_t robustness;
+ u_int8_t linkstate;
+ u_int8_t media_type;
+ u_int8_t passive;
+ u_int8_t igmp_version;
+};
+
+/* dvmrp_conf */
+enum {
+ PROC_MAIN,
+ PROC_DVMRP_ENGINE,
+ PROC_RDE_ENGINE
+} dvmrpd_process;
+
+struct dvmrpd_conf {
+ struct event ev;
+ struct event report_timer;
+ u_int32_t gen_id;
+ u_int32_t opts;
+#define DVMRPD_OPT_VERBOSE 0x00000001
+#define DVMRPD_OPT_VERBOSE2 0x00000002
+#define DVMRPD_OPT_NOACTION 0x00000004
+ int maxdepth;
+ LIST_HEAD(, iface) iface_list;
+ int dvmrp_socket;
+ int mroute_socket;
+ int flags;
+};
+
+/* kroute */
+struct kroute {
+ struct in_addr prefix;
+ struct in_addr nexthop;
+ u_int16_t flags;
+ u_short ifindex;
+ u_int8_t prefixlen;
+};
+
+struct kif {
+ char ifname[IF_NAMESIZE];
+ u_long baudrate;
+ int flags;
+ int mtu;
+ u_short ifindex;
+ u_int8_t media_type;
+ u_int8_t link_state;
+ u_int8_t nh_reachable; /* for nexthop verification */
+};
+
+/* control data structures */
+struct ctl_iface {
+ char name[IF_NAMESIZE];
+ struct in_addr addr;
+ struct in_addr mask;
+ struct in_addr querier;
+
+ time_t probe_timer;
+ time_t query_timer;
+ time_t querier_present_timer;
+ time_t uptime;
+
+ u_int32_t gen_id;
+ u_int32_t group_cnt;
+ u_int32_t baudrate;
+ u_int32_t probe_interval;
+ u_int32_t query_interval;
+ u_int32_t query_resp_interval;
+ u_int32_t recv_query_resp_interval;
+ u_int32_t group_member_interval;
+ u_int32_t querier_present_interval;
+ u_int32_t startup_query_interval;
+ u_int32_t startup_query_cnt;
+ u_int32_t last_member_query_interval;
+ u_int32_t last_member_query_cnt;
+ u_int32_t last_member_query_time;
+ u_int32_t v1_querier_present_tmout;
+ u_int32_t v1_host_present_interval;
+ u_int32_t dead_interval;
+
+ unsigned int ifindex;
+ int state;
+ int mtu;
+ int nbr_cnt;
+ int adj_cnt;
+
+ u_int16_t flags;
+ u_int16_t metric;
+ enum iface_type type;
+ u_int8_t robustness;
+ u_int8_t linkstate;
+ u_int8_t mediatype;
+ u_int8_t passive;
+ u_int8_t igmp_version;
+};
+
+struct ctl_group {
+ time_t dead_timer;
+ time_t v1_host_timer;
+ time_t retrans_timer;
+ time_t uptime;
+ struct in_addr addr;
+ int state;
+};
+
+struct ctl_nbr {
+ char name[IF_NAMESIZE];
+ struct in_addr id;
+ struct in_addr addr;
+ time_t dead_timer;
+ time_t uptime;
+ int state;
+};
+
+struct ctl_rt {
+ struct in_addr prefix;
+ struct in_addr nexthop;
+ struct in_addr area;
+ struct in_addr adv_rtr;
+ time_t uptime;
+ time_t expire;
+ u_int32_t cost;
+ u_int8_t flags;
+ u_int8_t prefixlen;
+};
+
+struct ctl_mfc {
+ u_int8_t ttls[MAXVIFS]; /* outgoing vif(s) */
+ struct in_addr origin;
+ struct in_addr group;
+ time_t uptime;
+ time_t expire;
+ u_short ifindex; /* incoming vif */
+};
+
+struct ctl_sum {
+ struct in_addr rtr_id;
+ u_int32_t hold_time;
+};
+
+/* buffer.c */
+struct buf *buf_open(size_t);
+struct buf *buf_dynamic(size_t, size_t);
+int buf_add(struct buf *, void *, size_t);
+void *buf_reserve(struct buf *, size_t);
+void *buf_seek(struct buf *, size_t, size_t);
+int buf_close(struct msgbuf *, struct buf *);
+void buf_free(struct buf *);
+void msgbuf_init(struct msgbuf *);
+void msgbuf_clear(struct msgbuf *);
+int msgbuf_write(struct msgbuf *);
+
+/* dvmrpd.c */
+void main_imsg_compose_dvmrpe(int, pid_t, void *, u_int16_t);
+
+/* parse.y */
+struct dvmrpd_conf *parse_config(char *, int);
+int cmdline_symset(char *);
+
+/* imsg.c */
+void imsg_init(struct imsgbuf *, int, void (*)(int, short, void *));
+ssize_t imsg_read(struct imsgbuf *);
+ssize_t imsg_get(struct imsgbuf *, struct imsg *);
+int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t,
+ void *, u_int16_t);
+struct buf *imsg_create(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t,
+ u_int16_t);
+int imsg_add(struct buf *, void *, u_int16_t);
+int imsg_close(struct imsgbuf *, struct buf *);
+void imsg_free(struct imsg *);
+void imsg_event_add(struct imsgbuf *); /* needs to be provided externally */
+
+/* in_cksum.c */
+u_int16_t in_cksum(void *, size_t);
+
+/* kroute.c */
+int kif_init(void);
+void kif_clear(void);
+int kr_init(int);
+void kr_shutdown(void);
+void kr_dispatch_msg(int, short, void *);
+void kr_ifinfo(char *);
+struct kif *kif_findname(char *);
+
+u_int8_t prefixlen_classful(in_addr_t);
+u_int8_t mask2prefixlen(in_addr_t);
+in_addr_t prefixlen2mask(u_int8_t);
+
+/* kmroute.c */
+int kmr_init(int);
+void kmr_shutdown(void);
+void kmr_recv_msg(int, short, void *);
+void kmr_mfc_couple(void);
+void kmr_mfc_decouple(void);
+void kmroute_clear(void);
+int mrt_init(int);
+int mrt_done(int);
+int mrt_add_vif(int, struct iface *);
+void mrt_del_vif(int, struct iface *);
+int mrt_add_mfc(int, struct mfc *);
+int mrt_del_mfc(int, struct mfc *);
+
+/* log.h */
+const char *nbr_state_name(int);
+const char *if_state_name(int);
+const char *if_type_name(enum iface_type);
+const char *group_state_name(int);
+
+/* printconf.c */
+void print_config(struct dvmrpd_conf *);
+
+#endif /* _DVMRPD_H_ */
diff --git a/usr.sbin/dvmrpd/dvmrpe.c b/usr.sbin/dvmrpd/dvmrpe.c
new file mode 100644
index 00000000000..025d9d11d65
--- /dev/null
+++ b/usr.sbin/dvmrpd/dvmrpe.c
@@ -0,0 +1,411 @@
+/* $OpenBSD: dvmrpe.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <event.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "igmp.h"
+#include "dvmrp.h"
+#include "dvmrpd.h"
+#include "dvmrpe.h"
+#include "control.h"
+#include "log.h"
+
+void dvmrpe_sig_handler(int, short, void *);
+void dvmrpe_shutdown(void);
+
+volatile sig_atomic_t dvmrpe_quit = 0;
+struct dvmrpd_conf *deconf = NULL;
+struct imsgbuf *ibuf_main;
+struct imsgbuf *ibuf_rde;
+struct ctl_conn *ctl_conn;
+
+void
+dvmrpe_sig_handler(int sig, short event, void *bula)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ dvmrpe_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+/* dvmrp engine */
+pid_t
+dvmrpe(struct dvmrpd_conf *xconf, int pipe_parent2dvmrpe[2],
+ int pipe_dvmrpe2rde[2], int pipe_parent2rde[2])
+{
+ struct iface *iface = NULL;
+ struct passwd *pw;
+ struct event ev_sigint, ev_sigterm;
+ pid_t pid;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ /* NOTREACHED */
+ case 0:
+ break;
+ default:
+
+ return (pid);
+ }
+
+ /* create the raw ip socket */
+ if ((xconf->dvmrp_socket = socket(AF_INET, SOCK_RAW,
+ IPPROTO_IGMP)) == -1)
+ fatal("error creating raw socket");
+
+ /* create dvmrpd control socket outside chroot */
+ if (control_init() == -1)
+ fatalx("control socket setup failed");
+
+ /* set some defaults */
+ if (if_set_mcast_ttl(xconf->dvmrp_socket,
+ IP_DEFAULT_MULTICAST_TTL) == -1)
+ fatal("if_set_mcast_ttl");
+
+ if (if_set_mcast_loop(xconf->dvmrp_socket) == -1)
+ fatal("if_set_mcast_loop");
+
+ if (if_set_tos(xconf->dvmrp_socket, IPTOS_PREC_INTERNETCONTROL) == -1)
+ fatal("if_set_tos");
+
+ if_set_recvbuf(xconf->dvmrp_socket);
+
+ deconf = xconf;
+
+ if ((pw = getpwnam(DVMRPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("dvmrp engine");
+ dvmrpd_process = PROC_DVMRP_ENGINE;
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
+ seteuid(pw->pw_uid) || setuid(pw->pw_uid)) {
+ fatal("can't drop privileges");
+ }
+
+ event_init();
+ nbr_init(NBR_HASHSIZE);
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, dvmrpe_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, dvmrpe_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* setup pipes */
+ close(pipe_parent2dvmrpe[0]);
+ close(pipe_dvmrpe2rde[1]);
+ close(pipe_parent2rde[0]);
+ close(pipe_parent2rde[1]);
+
+ if ((ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL ||
+ (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_rde, pipe_dvmrpe2rde[0], dvmrpe_dispatch_rde);
+ imsg_init(ibuf_main, pipe_parent2dvmrpe[1], dvmrpe_dispatch_main);
+
+ /* setup event handler */
+ ibuf_rde->events = EV_READ;
+ event_set(&ibuf_rde->ev, ibuf_rde->fd, ibuf_rde->events,
+ ibuf_rde->handler, ibuf_rde);
+ event_add(&ibuf_rde->ev, NULL);
+
+ ibuf_main->events = EV_READ;
+ event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events,
+ ibuf_main->handler, ibuf_main);
+ event_add(&ibuf_main->ev, NULL);
+
+ event_set(&deconf->ev, deconf->dvmrp_socket, EV_READ|EV_PERSIST,
+ recv_packet, deconf);
+ event_add(&deconf->ev, NULL);
+
+ /* listen on dvmrpd control socket */
+ TAILQ_INIT(&ctl_conns);
+ control_listen();
+
+ if ((pkt_ptr = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("dvmrpe");
+
+ /* start interfaces */
+ LIST_FOREACH(iface, &deconf->iface_list, entry) {
+ if_init(xconf, iface);
+ if (if_fsm(iface, IF_EVT_UP)) {
+ log_debug("error starting interface %s", iface->name);
+ }
+ }
+
+ evtimer_set(&deconf->report_timer, report_timer, deconf);
+ start_report_timer();
+
+ event_dispatch();
+
+ dvmrpe_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+void
+dvmrpe_shutdown(void)
+{
+ struct iface *iface;
+
+ /* stop all interfaces and delete them */
+ LIST_FOREACH(iface, &deconf->iface_list, entry) {
+ if (if_fsm(iface, IF_EVT_DOWN)) {
+ log_debug("error stopping interface %s",
+ iface->name);
+ }
+ if_del(iface);
+ }
+
+ /* clean up */
+ msgbuf_write(&ibuf_rde->w);
+ msgbuf_clear(&ibuf_rde->w);
+ free(ibuf_rde);
+ msgbuf_write(&ibuf_main->w);
+ msgbuf_clear(&ibuf_main->w);
+ free(ibuf_main);
+ free(pkt_ptr);
+
+ log_info("dvmrp engine exiting");
+ _exit(0);
+}
+
+/* imesg */
+int
+dvmrpe_imsg_compose_parent(int type, pid_t pid, void *data, u_int16_t datalen)
+{
+ return (imsg_compose(ibuf_main, type, 0, pid, data, datalen));
+}
+
+int
+dvmrpe_imsg_compose_rde(int type, u_int32_t peerid, pid_t pid,
+ void *data, u_int16_t datalen)
+{
+ return (imsg_compose(ibuf_rde, type, peerid, pid, data, datalen));
+}
+
+void
+dvmrpe_dispatch_main(int fd, short event, void *bula)
+{
+ struct imsg imsg;
+ struct imsgbuf *ibuf = bula;
+ int n;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("dvmrpe_dispatch_main: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ default:
+ log_debug("dvmrpe_dispatch_main: error handling "
+ "imsg %d", imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+dvmrpe_dispatch_rde(int fd, short event, void *bula)
+{
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ struct nbr *nbr;
+ struct iface *iface;
+ struct route_report *rr;
+ int n;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("dvmrpe_dispatch_rde: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_SHOW_RIB:
+ case IMSG_CTL_SHOW_SUM:
+ case IMSG_CTL_SHOW_MFC:
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ break;
+ case IMSG_FULL_ROUTE_REPORT:
+ /* add route reports to list */
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr))
+ fatalx("invalid size of RDE request");
+
+ if ((rr = calloc(1, sizeof(*rr))) == NULL)
+ fatal("dvmrpe_dispatch_rde");
+
+ memcpy(rr, imsg.data, sizeof(*rr));
+
+ /* general update, per interface */
+ if (imsg.hdr.peerid == 0) {
+ /* add to interface list */
+ LIST_FOREACH(iface, &deconf->iface_list,
+ entry) {
+ if (!if_nbr_list_empty(iface))
+ rr_list_add(&iface->rr_list,
+ rr);
+ }
+ break;
+ }
+
+ /* add to neighbor list */
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ rr_list_add(&nbr->rr_list, rr);
+ break;
+ case IMSG_FULL_ROUTE_REPORT_END:
+ /* transmit route report */
+ if (imsg.hdr.peerid == 0) {
+ /*
+ * send general route report on all
+ * interfaces with neighbors.
+ */
+ LIST_FOREACH(iface, &deconf->iface_list,
+ entry) {
+ rr_list_send(&iface->rr_list,
+ iface, NULL);
+ }
+ break;
+ }
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ rr_list_send(&nbr->rr_list, NULL, nbr);
+ break;
+ default:
+ log_debug("dvmrpe_dispatch_rde: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+dvmrpe_iface_ctl(struct ctl_conn *c, unsigned int idx)
+{
+ struct iface *iface;
+ struct ctl_iface *ictl;
+
+ LIST_FOREACH(iface, &deconf->iface_list, entry)
+ if (idx == 0 || idx == iface->ifindex) {
+ ictl = if_to_ctl(iface);
+ imsg_compose(&c->ibuf, IMSG_CTL_SHOW_IFACE,
+ 0, 0, ictl, sizeof(struct ctl_iface));
+ }
+}
+
+void
+dvmrpe_iface_igmp_ctl(struct ctl_conn *c, unsigned int idx)
+{
+ struct iface *iface;
+ struct ctl_iface *ictl;
+
+ LIST_FOREACH(iface, &deconf->iface_list, entry)
+ if (idx == 0 || idx == iface->ifindex) {
+ ictl = if_to_ctl(iface);
+ imsg_compose(&c->ibuf, IMSG_CTL_SHOW_IFACE,
+ 0, 0, ictl, sizeof(struct ctl_iface));
+ group_list_dump(iface, c);
+
+ }
+}
+
+void
+dvmrpe_nbr_ctl(struct ctl_conn *c)
+{
+ struct iface *iface;
+ struct nbr *nbr;
+ struct ctl_nbr *nctl;
+
+ LIST_FOREACH(iface, &deconf->iface_list, entry)
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ nctl = nbr_to_ctl(nbr);
+ imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NBR, 0, 0, nctl,
+ sizeof(struct ctl_nbr));
+ }
+
+ imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, NULL, 0);
+}
diff --git a/usr.sbin/dvmrpd/dvmrpe.h b/usr.sbin/dvmrpd/dvmrpe.h
new file mode 100644
index 00000000000..f4fead67baa
--- /dev/null
+++ b/usr.sbin/dvmrpd/dvmrpe.h
@@ -0,0 +1,212 @@
+/* $OpenBSD: dvmrpe.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DVMRPE_H_
+#define _DVMRPE_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns;
+
+/* neighbor events */
+enum nbr_event {
+ NBR_EVT_NOTHING,
+ NBR_EVT_PROBE_RCVD,
+ NBR_EVT_1_WAY_RCVD,
+ NBR_EVT_2_WAY_RCVD,
+ NBR_EVT_KILL_NBR,
+ NBR_EVT_ITIMER,
+ NBR_EVT_LL_DOWN
+};
+
+/* neighbor actions */
+enum nbr_action {
+ NBR_ACT_NOTHING,
+ NBR_ACT_RST_ITIMER,
+ NBR_ACT_STRT_ITIMER,
+ NBR_ACT_RESET,
+ NBR_ACT_DEL,
+ NBR_ACT_CLR_LST
+};
+
+struct nbr {
+ LIST_ENTRY(nbr) entry, hash;
+ struct event inactivity_timer;
+
+ struct rr_head rr_list;
+
+ struct in_addr addr;
+ struct in_addr id;
+
+ struct iface *iface;
+
+ u_int32_t peerid; /* unique ID in DB */
+ u_int32_t gen_id;
+
+ time_t uptime;
+
+ int state;
+ u_int8_t link_state;
+ u_int8_t capabilities;
+ u_int8_t compat; /* mrouted compat */
+};
+
+struct route_report {
+ struct in_addr net;
+ struct in_addr mask;
+ struct in_addr nexthop;
+ struct in_addr adv_rtr;
+ u_short ifindex;
+ u_int8_t metric;
+
+};
+
+struct rr_entry {
+ TAILQ_ENTRY(rr_entry) entry;
+ struct route_report *re;
+};
+
+/* ask_nbrs2.c */
+int send_ask_nbrs2(struct iface *, struct in_addr, void *, int);
+void recv_ask_nbrs2(struct nbr *, char *, u_int16_t);
+
+/* dvmrpe.c */
+pid_t dvmrpe(struct dvmrpd_conf *, int[2], int[2], int[2]);
+void dvmrpe_dispatch_main(int, short, void *);
+void dvmrpe_dispatch_rde(int, short, void *);
+int dvmrpe_imsg_compose_parent(int, pid_t, void *, u_int16_t);
+int dvmrpe_imsg_compose_rde(int, u_int32_t, pid_t, void *, u_int16_t);
+
+void dvmrpe_iface_ctl(struct ctl_conn *, unsigned int);
+void dvmrpe_iface_igmp_ctl(struct ctl_conn *, unsigned int);
+void dvmrpe_nbr_ctl(struct ctl_conn *);
+
+/* graft.c */
+int send_graft(struct iface *, struct in_addr, void *, int);
+void recv_graft(struct nbr *, char *, u_int16_t);
+
+/* graft_ack.c */
+int send_graft_ack(struct iface *, struct in_addr, void *, int);
+void recv_graft_ack(struct nbr *, char *, u_int16_t);
+
+/* group.c */
+struct ctl_group *group_to_ctl(struct group *);
+
+/* igmp.c */
+int group_fsm(struct group *, enum group_event);
+int send_igmp_query(struct iface *, struct group *group);
+void recv_igmp_query(struct iface *, struct in_addr, char *, u_int16_t);
+void recv_igmp_report(struct iface *, struct in_addr, char *, u_int16_t,
+ u_int8_t);
+void recv_igmp_leave(struct iface *, struct in_addr, char *, u_int16_t);
+
+struct group *group_list_add(struct iface *, u_int32_t);
+void group_list_remove(struct iface *, struct group *);
+struct group *group_list_find(struct iface *, u_int32_t);
+void group_list_clr(struct iface *);
+int group_list_empty(struct iface *);
+void group_list_dump(struct iface *, struct ctl_conn *);
+
+const char *group_event_name(int);
+const char *group_action_name(int);
+
+/* interface.c */
+int if_fsm(struct iface *, enum iface_event);
+struct iface *if_new(struct kif *);
+int if_del(struct iface *);
+int if_nbr_list_empty(struct iface *);
+void if_init(struct dvmrpd_conf *, struct iface *);
+
+const char *if_event_name(int);
+const char *if_action_name(int);
+
+int if_set_mcast_ttl(int, u_int8_t);
+int if_set_tos(int, int);
+int if_set_mcast_loop(int);
+void if_set_recvbuf(int);
+
+int if_join_group(struct iface *, struct in_addr *);
+int if_leave_group(struct iface *, struct in_addr *);
+int if_set_mcast(struct iface *);
+
+struct ctl_iface *if_to_ctl(struct iface *);
+
+/* nbrs2.c */
+int send_nbrs2(struct iface *, struct in_addr, void *, int);
+void recv_nbrs2(struct nbr *, char *, u_int16_t);
+
+/* neighbor.c */
+void nbr_init(u_int32_t);
+struct nbr *nbr_new(u_int32_t, struct iface *, int);
+int nbr_del(struct nbr *);
+
+struct nbr *nbr_find_ip(struct iface *, u_int32_t);
+struct nbr *nbr_find_peerid(u_int32_t);
+
+int nbr_fsm(struct nbr *, enum nbr_event);
+
+int nbr_start_itimer(struct nbr *);
+int nbr_stop_itimer(struct nbr *);
+int nbr_reset_itimer(struct nbr *);
+
+void nbr_itimer(int, short, void *);
+
+int nbr_act_start(struct nbr *);
+int nbr_act_reset_itimer(struct nbr *);
+int nbr_act_start_itimer(struct nbr *);
+int nbr_act_delete(struct nbr *);
+int nbr_act_clear_lists(struct nbr *);
+
+const char *nbr_event_name(int);
+const char *nbr_action_name(int);
+
+struct ctl_nbr *nbr_to_ctl(struct nbr *);
+
+/* packet.c */
+int gen_dvmrp_hdr(struct buf *, struct iface *, u_int8_t);
+int send_packet(struct iface *, void *, size_t, struct sockaddr_in *);
+void recv_packet(int, short, void *);
+
+char *pkt_ptr; /* packet buffer */
+
+/* probe.c */
+int send_probe(struct iface *);
+void recv_probe(struct iface *, struct in_addr, u_int32_t, u_int8_t,
+ char *, u_int16_t);
+
+/* prune.c */
+int send_prune(struct nbr *, void *, int);
+void recv_prune(struct nbr *, char *, u_int16_t);
+
+/* report.c */
+int send_report(struct iface *, struct in_addr, void *, int);
+void recv_report(struct nbr *, char *, u_int16_t);
+
+void report_timer(int, short, void *);
+int start_report_timer(void);
+int stop_report_timer(void);
+
+void rr_list_add(struct rr_head *, struct route_report *);
+void rr_list_clr(struct rr_head *);
+void rr_list_send(struct rr_head *, struct iface *, struct nbr *);
+
+#endif /* _DVMRPE_H_ */
diff --git a/usr.sbin/dvmrpd/graft.c b/usr.sbin/dvmrpd/graft.c
new file mode 100644
index 00000000000..1931de3e3dc
--- /dev/null
+++ b/usr.sbin/dvmrpd/graft.c
@@ -0,0 +1,80 @@
+/* $OpenBSD: graft.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <strings.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+/* DVMRP graft packet handling */
+int
+send_graft(struct iface *iface, struct in_addr addr, void *data, int len)
+{
+ struct sockaddr_in dst;
+ struct buf *buf;
+ struct dvmrp_hdr *dvmrp_hdr;
+ int ret = 0;
+
+ log_debug("send_graft: interface %s addr %s",
+ iface->name, inet_ntoa(addr));
+
+ if (iface->passive)
+ return (0);
+
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_graft");
+
+ /* DVMRP header */
+ if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT))
+ goto fail;
+
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr.s_addr = addr.s_addr;
+
+ /* update chksum */
+ dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr));
+ dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_graft");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_graft(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ log_debug("recv_graft: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return;
+}
diff --git a/usr.sbin/dvmrpd/graft_ack.c b/usr.sbin/dvmrpd/graft_ack.c
new file mode 100644
index 00000000000..682293273da
--- /dev/null
+++ b/usr.sbin/dvmrpd/graft_ack.c
@@ -0,0 +1,80 @@
+/* $OpenBSD: graft_ack.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <strings.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+/* DVMRP graft ack packet handling */
+int
+send_graft_ack(struct iface *iface, struct in_addr addr, void *data, int len)
+{
+ struct sockaddr_in dst;
+ struct buf *buf;
+ struct dvmrp_hdr *dvmrp_hdr;
+ int ret = 0;
+
+ log_debug("send_graft_ack: interface %s addr %s",
+ iface->name, inet_ntoa(addr));
+
+ if (iface->passive)
+ return (0);
+
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_graft_ack");
+
+ /* DVMRP header */
+ if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT_ACK))
+ goto fail;
+
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr.s_addr = addr.s_addr;
+
+ /* update chksum */
+ dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr));
+ dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_graft_ack");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_graft_ack(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ log_debug("recv_graft_ack: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return;
+}
diff --git a/usr.sbin/dvmrpd/group.c b/usr.sbin/dvmrpd/group.c
new file mode 100644
index 00000000000..99bb35b7fbd
--- /dev/null
+++ b/usr.sbin/dvmrpd/group.c
@@ -0,0 +1,497 @@
+/* $OpenBSD: group.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <event.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "dvmrpe.h"
+#include "log.h"
+#include "control.h"
+
+void dead_timer(int, short, void *);
+int start_dead_timer(struct group *);
+int start_dead_timer_all(struct group *);
+int stop_dead_timer(struct group *);
+
+void v1_host_timer(int, short, void *);
+int start_v1_host_timer(struct group *);
+int stop_v1_host_timer(struct group *);
+
+void retrans_timer(int, short, void *);
+int start_retrans_timer(struct group *);
+int stop_retrans_timer(struct group *);
+
+extern struct dvmrpd_conf *deconf;
+
+#define MAX_ACTIONS 4
+
+struct {
+ int state;
+ enum group_event event;
+ enum group_action action[MAX_ACTIONS];
+ int new_state;
+} grp_fsm[] = {
+ /* current state event that happened action(s) to take resulting state */
+ /* querier fsm */
+ {GRP_STA_NO_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_ADD_ROUTE,
+ GRP_ACT_START_TMR,
+ GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
+ {GRP_STA_NO_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_ADD_ROUTE,
+ GRP_ACT_START_TMR,
+ GRP_ACT_START_V1_HOST_TMR,
+ GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
+
+ {GRP_STA_MEMB_PRSNT, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_ROUTE,
+ GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT},
+ {GRP_STA_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR,
+ GRP_ACT_END}, 0},
+ {GRP_STA_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR,
+ GRP_ACT_START_V1_HOST_TMR,
+ GRP_ACT_END}, GRP_STA_V1_MEMB_PRSNT},
+ {GRP_STA_MEMB_PRSNT, GRP_EVT_LEAVE_RCVD, {GRP_ACT_START_TMR_ALL,
+ GRP_ACT_START_RETRANS_TMR,
+ GRP_ACT_SEND_GRP_QUERY,
+ GRP_ACT_END}, GRP_STA_CHECK_MEMB},
+
+ {GRP_STA_CHECK_MEMB, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR,
+ GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
+ {GRP_STA_CHECK_MEMB, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_ROUTE,
+ GRP_ACT_CLR_RETRANS_TMR,
+ GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT},
+ {GRP_STA_CHECK_MEMB, GRP_EVT_RETRANS_TMR_EXP,{GRP_ACT_SEND_GRP_QUERY,
+ GRP_ACT_START_RETRANS_TMR,
+ GRP_ACT_END}, 0},
+ {GRP_STA_CHECK_MEMB, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR,
+ GRP_ACT_START_V1_HOST_TMR,
+ GRP_ACT_END}, GRP_STA_V1_MEMB_PRSNT},
+
+ {GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V1_HOST_TMR_EXP,{GRP_ACT_NOTHING,
+ GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
+ {GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR,
+ GRP_ACT_START_V1_HOST_TMR,
+ GRP_ACT_END}, 0},
+ {GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR,
+ GRP_ACT_END}, 0},
+ {GRP_STA_V1_MEMB_PRSNT, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_ROUTE,
+ GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT},
+
+ /* non querier fsm */
+ {GRP_STA_NO_MEMB_PRSNT, GRP_EVT_REPORT_RCVD, {GRP_ACT_ADD_ROUTE,
+ GRP_ACT_START_TMR,
+ GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
+ {GRP_STA_MEMB_PRSNT, GRP_EVT_REPORT_RCVD, {GRP_ACT_START_TMR,
+ GRP_ACT_END}, 0},
+ {GRP_STA_MEMB_PRSNT, GRP_EVT_QUERY_RCVD, {GRP_ACT_START_TMR_ALL,
+ GRP_ACT_END}, GRP_STA_CHECK_MEMB},
+
+ {GRP_STA_CHECK_MEMB, GRP_EVT_REPORT_RCVD, {GRP_ACT_START_TMR,
+ GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
+ {-1, GRP_EVT_NOTHING, {GRP_ACT_NOTHING,
+ GRP_ACT_END}, 0},
+};
+
+const char * const group_action_names[] = {
+ "END MARKER",
+ "START TIMER",
+ "START ALL TIMER",
+ "START RETRANSMISSION TIMER",
+ "START V1 HOST TIMER",
+ "SEND GROUP QUERY",
+ "ADD ROUTE",
+ "DEL ROUTE",
+ "CLEAR RETRANSMISSION TIMER",
+ "NOTHING"
+};
+
+static const char * const group_event_names[] = {
+ "V2 REPORT RCVD",
+ "V1 REPORT RCVD",
+ "LEAVE RCVD",
+ "TIMER EXPIRED",
+ "RETRANS TIMER EXPIRED",
+ "V1 HOST TIMER EXPIRED",
+ "REPORT RCVD",
+ "QUERY RCVD",
+ "NOTHING"
+};
+
+int
+group_fsm(struct group *group, enum group_event event)
+{
+ struct mfc mfc;
+ int old_state;
+ int new_state = 0;
+ int i, j, ret = 0;
+
+ old_state = group->state;
+
+ for (i = 0; grp_fsm[i].state != -1; i++)
+ if ((grp_fsm[i].state & old_state) &&
+ (grp_fsm[i].event == event)) {
+ new_state = grp_fsm[i].new_state;
+ break;
+ }
+
+ if (grp_fsm[i].state == -1) {
+ /* XXX event outside of the defined fsm, ignore it. */
+ log_debug("group_fsm: group %s, event '%s' not expected in "
+ "state '%s'", inet_ntoa(group->addr),
+ group_event_name(event), group_state_name(old_state));
+ return (0);
+ }
+
+ for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++) {
+ switch (grp_fsm[i].action[j]) {
+ case GRP_ACT_START_TMR:
+ ret = start_dead_timer(group);
+ break;
+ case GRP_ACT_START_TMR_ALL:
+ ret = start_dead_timer_all(group);
+ break;
+ case GRP_ACT_START_RETRANS_TMR:
+ ret = start_retrans_timer(group);
+ break;
+ case GRP_ACT_START_V1_HOST_TMR:
+ ret = start_v1_host_timer(group);
+ break;
+ case GRP_ACT_SEND_GRP_QUERY:
+ ret = send_igmp_query(group->iface, group);
+ break;
+ case GRP_ACT_ADD_ROUTE:
+ mfc.origin.s_addr = 0;
+ mfc.group = group->addr;
+ mfc.ifindex = group->iface->ifindex; /* XXX fixme */
+ dvmrpe_imsg_compose_rde(IMSG_MFC_ADD, 0, 0, &mfc,
+ sizeof(mfc));
+ break;
+ case GRP_ACT_DEL_ROUTE:
+ mfc.origin.s_addr = 0;
+ mfc.group = group->addr;
+ mfc.ifindex = group->iface->ifindex; /* XXX fixme */
+ dvmrpe_imsg_compose_rde(IMSG_MFC_DEL, 0, 0, &mfc,
+ sizeof(mfc));
+ break;
+ case GRP_ACT_CLR_RETRANS_TMR:
+ ret = stop_retrans_timer(group);
+ break;
+ case GRP_ACT_NOTHING:
+ case GRP_ACT_END:
+ /* do nothing */
+ break;
+ }
+
+ if (ret) {
+ log_debug("group_fsm: error changing state for "
+ "group %s, event '%s', state '%s'",
+ inet_ntoa(group->addr), group_event_name(event),
+ group_state_name(old_state));
+ return (-1);
+ }
+ }
+
+ if (new_state != 0)
+ group->state = new_state;
+
+ for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++)
+ log_debug("group_fsm: event '%s' resulted in action '%s' and "
+ "changing state for group %s from '%s' to '%s'",
+ group_event_name(event),
+ group_action_name(grp_fsm[i].action[j]),
+ inet_ntoa(group->addr), group_state_name(old_state),
+ group_state_name(group->state));
+
+ return (ret);
+}
+
+/* timers */
+void
+dead_timer(int fd, short event, void *arg)
+{
+ struct group *group = arg;
+
+ log_debug("dead_timer: %s", inet_ntoa(group->addr));
+
+ group_fsm(group, GRP_EVT_TMR_EXPIRED);
+}
+
+int
+start_dead_timer(struct group *group)
+{
+ struct timeval tv;
+
+ log_debug("start_dead_timer: %s", inet_ntoa(group->addr));
+
+ /* Group Membership Interval */
+ timerclear(&tv);
+ tv.tv_sec = group->iface->robustness * group->iface->query_interval +
+ (group->iface->query_resp_interval / 2);
+
+ return (evtimer_add(&group->dead_timer, &tv));
+}
+
+int
+start_dead_timer_all(struct group *group)
+{
+ struct timeval tv;
+
+ log_debug("start_dead_timer_all: %s", inet_ntoa(group->addr));
+
+ timerclear(&tv);
+ if (group->iface->state == IF_STA_QUERIER) {
+ /* querier */
+ tv.tv_sec = group->iface->last_member_query_interval *
+ group->iface->last_member_query_cnt;
+ } else {
+ /* non querier */
+ /* XXX max response time received in packet */
+ tv.tv_sec = group->iface->recv_query_resp_interval *
+ group->iface->last_member_query_cnt;
+ }
+
+ return (evtimer_add(&group->dead_timer, &tv));
+}
+
+int
+stop_dead_timer(struct group *group)
+{
+ log_debug("stop_dead_timer: %s", inet_ntoa(group->addr));
+
+ return (evtimer_del(&group->dead_timer));
+}
+
+void
+v1_host_timer(int fd, short event, void *arg)
+{
+ struct group *group = arg;
+
+ log_debug("v1_host_timer: %s", inet_ntoa(group->addr));
+
+ group_fsm(group, GRP_EVT_V1_HOST_TMR_EXP);
+}
+
+int
+start_v1_host_timer(struct group *group)
+{
+ struct timeval tv;
+
+ log_debug("start_v1_host_timer: %s", inet_ntoa(group->addr));
+
+ /* Group Membership Interval */
+ timerclear(&tv);
+ tv.tv_sec = group->iface->robustness * group->iface->query_interval +
+ (group->iface->query_resp_interval / 2);
+
+ return (evtimer_add(&group->v1_host_timer, &tv));
+}
+
+int
+stop_v1_host_timer(struct group *group)
+{
+ log_debug("stop_v1_host_timer: %s", inet_ntoa(group->addr));
+
+ return (evtimer_del(&group->v1_host_timer));
+}
+
+void
+retrans_timer(int fd, short event, void *arg)
+{
+ struct group *group = arg;
+ struct timeval tv;
+
+ log_debug("retrans_timer: %s", inet_ntoa(group->addr));
+
+ send_igmp_query(group->iface, group);
+
+ /* reschedule retrans_timer */
+ if (group->state == GRP_STA_CHECK_MEMB) {
+ timerclear(&tv);
+ tv.tv_sec = group->iface->last_member_query_interval;
+ evtimer_add(&group->retrans_timer, &tv);
+ }
+}
+
+int
+start_retrans_timer(struct group *group)
+{
+ struct timeval tv;
+
+ log_debug("start_retrans_timer: %s", inet_ntoa(group->addr));
+
+ timerclear(&tv);
+ tv.tv_sec = group->iface->last_member_query_interval;
+
+ return (evtimer_add(&group->retrans_timer, &tv));
+}
+
+int
+stop_retrans_timer(struct group *group)
+{
+ log_debug("stop_retrans_timer: %s", inet_ntoa(group->addr));
+
+ return (evtimer_del(&group->retrans_timer));
+}
+
+/* group list */
+struct group *
+group_list_add(struct iface *iface, u_int32_t group)
+{
+ struct group *ge;
+ struct timeval now;
+
+ /* validate group id */
+ if (!IN_MULTICAST(htonl(group)))
+ fatalx("group_list_add: invalid group");
+
+ if ((ge = group_list_find(iface, group)) != NULL) {
+ return (ge);
+ }
+
+ if ((ge = calloc(1, sizeof(*ge))) == NULL)
+ fatal("group_list_add");
+
+ ge->addr.s_addr = group;
+ ge->state = GRP_STA_NO_MEMB_PRSNT;
+ evtimer_set(&ge->dead_timer, dead_timer, ge);
+ evtimer_set(&ge->v1_host_timer, v1_host_timer, ge);
+ evtimer_set(&ge->retrans_timer, retrans_timer, ge);
+
+ gettimeofday(&now, NULL);
+ ge->uptime = now.tv_sec;
+
+ TAILQ_INSERT_TAIL(&iface->group_list, ge, entry);
+ iface->group_cnt++;
+
+ ge->iface = iface;
+
+ log_debug("group_list_add: interface %s, group %s", iface->name,
+ inet_ntoa(ge->addr));
+
+ return (ge);
+}
+
+void
+group_list_remove(struct iface *iface, struct group *group)
+{
+ log_debug("group_list_remove: interface %s, group %s", iface->name,
+ inet_ntoa(group->addr));
+
+ /* stop timers */
+ stop_dead_timer(group);
+ start_v1_host_timer(group);
+ stop_retrans_timer(group);
+
+ TAILQ_REMOVE(&iface->group_list, group, entry);
+ free(group);
+ iface->group_cnt--;
+}
+
+struct group *
+group_list_find(struct iface *iface, u_int32_t group)
+{
+ struct group *ge = NULL;
+
+ /* validate group id */
+ if (!IN_MULTICAST(htonl(group)))
+ fatalx("group_list_find: invalid group");
+
+ TAILQ_FOREACH(ge, &iface->group_list, entry) {
+ if (ge->addr.s_addr == group)
+ return (ge);
+ }
+
+ return (ge);
+}
+
+void
+group_list_clr(struct iface *iface)
+{
+ struct group *ge;
+
+ while ((ge = TAILQ_FIRST(&iface->group_list)) != NULL) {
+ TAILQ_REMOVE(&iface->group_list, ge, entry);
+ free(ge);
+ }
+ iface->group_cnt = 0;
+}
+
+int
+group_list_empty(struct iface *iface)
+{
+ return (TAILQ_EMPTY(&iface->group_list));
+}
+
+void
+group_list_dump(struct iface *iface, struct ctl_conn *c)
+{
+ struct group *ge;
+ struct ctl_group *gctl;
+
+ TAILQ_FOREACH(ge, &iface->group_list, entry) {
+ gctl = group_to_ctl(ge);
+ imsg_compose(&c->ibuf, IMSG_CTL_SHOW_IGMP, 0, 0, gctl,
+ sizeof(struct ctl_group));
+ }
+}
+
+/* names */
+const char *
+group_event_name(int event)
+{
+ return (group_event_names[event]);
+}
+
+const char *
+group_action_name(int action)
+{
+ return (group_action_names[action]);
+}
+
+struct ctl_group *
+group_to_ctl(struct group *group)
+{
+ static struct ctl_group gctl;
+ struct timeval tv, now, res;
+
+ memcpy(&gctl.addr, &group->addr, sizeof(gctl.addr));
+
+ gctl.state = group->state;
+
+ gettimeofday(&now, NULL);
+ if (evtimer_pending(&group->dead_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ gctl.dead_timer = res.tv_sec;
+ } else
+ gctl.dead_timer = 0;
+
+ if (group->state != GRP_STA_NO_MEMB_PRSNT) {
+ gctl.uptime = now.tv_sec - group->uptime;
+ } else
+ gctl.uptime = 0;
+
+ return (&gctl);
+}
diff --git a/usr.sbin/dvmrpd/igmp.c b/usr.sbin/dvmrpd/igmp.c
new file mode 100644
index 00000000000..da0f3e3d0b7
--- /dev/null
+++ b/usr.sbin/dvmrpd/igmp.c
@@ -0,0 +1,242 @@
+/* $OpenBSD: igmp.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <event.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+int igmp_chksum(struct igmp_hdr *);
+
+/* IGMP packet handling */
+int
+send_igmp_query(struct iface *iface, struct group *group)
+{
+ struct igmp_hdr igmp_hdr;
+ struct sockaddr_in dst;
+ struct buf *buf;
+ int ret = 0;
+
+ log_debug("send_igmp_query: interface %s", iface->name);
+
+ if (iface->passive)
+ return (0);
+
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_igmp_query");
+
+ /* IGMP header */
+ bzero(&igmp_hdr, sizeof(igmp_hdr));
+ igmp_hdr.type = PKT_TYPE_MEMBER_QUERY;
+
+ if (group == NULL) {
+ /* general query - version is configured */
+ igmp_hdr.grp_addr = 0;
+
+ switch (iface->igmp_version) {
+ case 1:
+ break;
+ case 2:
+ igmp_hdr.max_resp_time = iface->query_resp_interval;
+ break;
+ default:
+ fatal("send_igmp_query: invalid igmp version");
+ }
+ } else {
+ /* group specific query - only version 2 */
+ igmp_hdr.grp_addr = group->addr.s_addr;
+ igmp_hdr.max_resp_time = iface->last_member_query_interval;
+ }
+
+ /* update chksum */
+ igmp_hdr.chksum = in_cksum(&igmp_hdr, sizeof(igmp_hdr));
+
+ buf_add(buf, &igmp_hdr, sizeof(igmp_hdr));
+
+ /* set destination address */
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ inet_aton(AllSystems, &dst.sin_addr);
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+ buf_free(buf);
+ return (ret);
+}
+
+void
+recv_igmp_query(struct iface *iface, struct in_addr src, char *buf,
+ u_int16_t len)
+{
+ struct igmp_hdr igmp_hdr;
+ struct group *group;
+
+ log_debug("recv_igmp_query: interface %s", iface->name);
+
+ if (len < sizeof(igmp_hdr)) {
+ log_debug("recv_igmp_query: invalid IGMP report, interface %s",
+ iface->name);
+ return;
+ }
+
+ memcpy(&igmp_hdr, buf, sizeof(igmp_hdr));
+ iface->recv_query_resp_interval = igmp_hdr.max_resp_time;
+
+ /* verify chksum */
+ if (igmp_chksum(&igmp_hdr) == -1) {
+ log_debug("recv_igmp_query: invalid chksum, interface %s",
+ iface->name);
+ return;
+ }
+
+ if (src.s_addr < iface->addr.s_addr && igmp_hdr.grp_addr == 0) {
+ /* we received a general query and we lost the election */
+ if_fsm(iface, IF_EVT_QRECVD);
+ /* remember who is querier */
+ iface->querier = src;
+ return;
+ }
+
+ if (iface->state == IF_STA_NONQUERIER && igmp_hdr.grp_addr != 0) {
+ /* validate group id */
+ if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) {
+ log_debug("recv_igmp_query: invalid group, "
+ "interface %s", iface->name);
+ return;
+ }
+
+ if ((group = group_list_add(iface, igmp_hdr.grp_addr))
+ != NULL)
+ group_fsm(group, GRP_EVT_QUERY_RCVD);
+ }
+}
+
+void
+recv_igmp_report(struct iface *iface, struct in_addr src, char *buf,
+ u_int16_t len, u_int8_t type)
+{
+ struct igmp_hdr igmp_hdr;
+ struct group *group;
+
+ log_debug("recv_igmp_report: interface %s", iface->name);
+
+ if (len < sizeof(igmp_hdr)) {
+ log_debug("recv_igmp_report: invalid IGMP report, interface %s",
+ iface->name);
+ return;
+ }
+
+ memcpy(&igmp_hdr, buf, sizeof(igmp_hdr));
+
+ /* verify chksum */
+ if (igmp_chksum(&igmp_hdr) == -1) {
+ log_debug("recv_igmp_report: invalid chksum, interface %s",
+ iface->name);
+ return;
+ }
+
+ /* validate group id */
+ if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) {
+ log_debug("recv_igmp_report: invalid group, interface %s",
+ iface->name);
+ return;
+ }
+
+ if ((group = group_list_add(iface, igmp_hdr.grp_addr)) == NULL)
+ return;
+
+ if (iface->state == IF_STA_QUERIER) {
+ /* querier */
+ switch (type) {
+ case PKT_TYPE_MEMBER_REPORTv1:
+ group_fsm(group, GRP_EVT_V1_REPORT_RCVD);
+ break;
+ case PKT_TYPE_MEMBER_REPORTv2:
+ group_fsm(group, GRP_EVT_V2_REPORT_RCVD);
+ break;
+ default:
+ fatalx("recv_igmp_report: unknown IGMP report type");
+ }
+ } else {
+ /* non querier */
+ group_fsm(group, GRP_EVT_REPORT_RCVD);
+ }
+}
+
+void
+recv_igmp_leave(struct iface *iface, struct in_addr src, char *buf,
+ u_int16_t len)
+{
+ struct igmp_hdr igmp_hdr;
+ struct group *group;
+
+ log_debug("recv_igmp_leave: interface %s", iface->name);
+
+ if (iface->state != IF_STA_QUERIER)
+ return;
+
+ if (len < sizeof(igmp_hdr)) {
+ log_debug("recv_igmp_leave: invalid IGMP leave, interface %s",
+ iface->name);
+ return;
+ }
+
+ memcpy(&igmp_hdr, buf, sizeof(igmp_hdr));
+
+ /* verify chksum */
+ if (igmp_chksum(&igmp_hdr) == -1) {
+ log_debug("recv_igmp_leave: invalid chksum, interface %s",
+ iface->name);
+ return;
+ }
+
+ /* validate group id */
+ if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) {
+ log_debug("recv_igmp_leave: invalid group, interface %s",
+ iface->name);
+ return;
+ }
+
+ if ((group = group_list_find(iface, igmp_hdr.grp_addr)) != NULL) {
+ group_fsm(group, GRP_EVT_LEAVE_RCVD);
+ }
+}
+
+int
+igmp_chksum(struct igmp_hdr *igmp_hdr)
+{
+ u_int16_t chksum;
+
+ chksum = igmp_hdr->chksum;
+ igmp_hdr->chksum = 0;
+
+ if (chksum != in_cksum(igmp_hdr, sizeof(*igmp_hdr)))
+ return (-1);
+
+ return (0);
+}
diff --git a/usr.sbin/dvmrpd/igmp.h b/usr.sbin/dvmrpd/igmp.h
new file mode 100644
index 00000000000..81165ee67c0
--- /dev/null
+++ b/usr.sbin/dvmrpd/igmp.h
@@ -0,0 +1,111 @@
+/* $OpenBSD: igmp.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* IGMP protocol definitions */
+
+#ifndef _IGMP_H_
+#define _IGMP_H_
+
+/* misc */
+#define AllSystems "224.0.0.1"
+#define AllRouters "224.0.0.2"
+#define AllIGMPv3Routers "224.0.0.22"
+
+#define DEFAULT_ROBUSTNESS 2
+#define MIN_ROBUSTNESS 1
+#define MAX_ROBUSTNESS 4
+
+#define DEFAULT_QUERY_INTERVAL 125
+#define MIN_QUERY_INTERVAL 1
+#define MAX_QUERY_INTERVAL 65535
+
+/* must be less than QUERY_INTERVAL */
+#define DEFAULT_QUERY_RESP_INTERVAL 100 /* 10 seconds */
+#define MIN_QUERY_RESP_INTERVAL 1
+#define MAX_QUERY_RESP_INTERVAL 65535
+
+#define DEFAULT_STARTUP_QUERY_INTERVAL DEFAULT_QUERY_INTERVAL / 4
+#define MIN_STARTUP_QUERY_INTERVAL MIN_QUERY_INTERVAL
+#define MAX_STARTUP_QUERY_INTERVAL MAX_QUERY_INTERVAL
+
+#define DEFAULT_STARTUP_QUERY_CNT DEFAULT_ROBUSTNESS
+#define MIN_STARTUP_QUERY_CNT MIN_ROBUSTNESS /* XXX */
+#define MAX_STARTUP_QUERY_CNT MAX_ROBUSTNESS /* XXX */
+
+#define DEFAULT_LAST_MEMBER_QUERY_INTERVAL 10 /* 1 second */
+#define MIN_LAST_MEMBER_QUERY_INTERVAL 1
+#define MAX_LAST_MEMBER_QUERY_INTERVAL 65535
+
+#define DEFAULT_LAST_MEMBER_QUERY_CNT DEFAULT_ROBUSTNESS
+#define MIN_LAST_MEMBER_QUERY_CNT 1
+#define MAX_LAST_MEMBER_QUERY_CNT 255
+
+/* IGMP packet types */
+#define PKT_TYPE_MEMBER_QUERY 0x11
+#define PKT_TYPE_MEMBER_REPORTv1 0x12
+#define PKT_TYPE_MEMBER_REPORTv2 0x16
+#define PKT_TYPE_LEAVE_GROUPv2 0x17
+#define PKT_TYPE_MEMBER_REPORTv3 0x22
+
+#define DEFAULT_IGMP_VERSION 2
+#define MIN_IGMP_VERSION 1
+#define MAX_IGMP_VERSION 2
+
+/* IGMP header */
+struct igmp_hdr {
+ u_int8_t type;
+ u_int8_t max_resp_time;
+ u_int16_t chksum;
+ u_int32_t grp_addr;
+};
+
+/* group states */
+#define GRP_STA_NO_MEMB_PRSNT 0x01
+#define GRP_STA_MEMB_PRSNT 0x02
+#define GRP_STA_V1_MEMB_PRSNT 0x04
+#define GRP_STA_CHECK_MEMB 0x08
+#define GRP_STA_ANY 0x0e
+
+/* group events */
+enum group_event {
+ GRP_EVT_V2_REPORT_RCVD,
+ GRP_EVT_V1_REPORT_RCVD,
+ GRP_EVT_LEAVE_RCVD,
+ GRP_EVT_TMR_EXPIRED,
+ GRP_EVT_RETRANS_TMR_EXP,
+ GRP_EVT_V1_HOST_TMR_EXP,
+ GRP_EVT_REPORT_RCVD,
+ GRP_EVT_QUERY_RCVD,
+ GRP_EVT_NOTHING
+};
+
+/* group actions */
+enum group_action {
+ GRP_ACT_END,
+ GRP_ACT_START_TMR,
+ GRP_ACT_START_TMR_ALL,
+ GRP_ACT_START_RETRANS_TMR,
+ GRP_ACT_START_V1_HOST_TMR,
+ GRP_ACT_SEND_GRP_QUERY,
+ GRP_ACT_ADD_ROUTE,
+ GRP_ACT_DEL_ROUTE,
+ GRP_ACT_CLR_RETRANS_TMR,
+ GRP_ACT_NOTHING
+};
+
+#endif /* _IGMP_H_ */
diff --git a/usr.sbin/dvmrpd/imsg.c b/usr.sbin/dvmrpd/imsg.c
new file mode 100644
index 00000000000..da001cec783
--- /dev/null
+++ b/usr.sbin/dvmrpd/imsg.c
@@ -0,0 +1,178 @@
+/* $OpenBSD: imsg.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dvmrpd.h"
+#include "log.h"
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd, void (*handler)(int, short, void *))
+{
+ msgbuf_init(&ibuf->w);
+ bzero(&ibuf->r, sizeof(ibuf->r));
+ ibuf->fd = fd;
+ ibuf->w.fd = fd;
+ ibuf->pid = getpid();
+ ibuf->handler = handler;
+ TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+ ssize_t n;
+
+ if ((n = recv(ibuf->fd, ibuf->r.buf + ibuf->r.wpos,
+ sizeof(ibuf->r.buf) - ibuf->r.wpos, 0)) == -1) {
+ if (errno != EINTR && errno != EAGAIN) {
+ log_warn("imsg_read: pipe read error");
+ return (-1);
+ }
+ return (0);
+ }
+
+ ibuf->r.wpos += n;
+
+ return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+ size_t av, left, datalen;
+
+ av = ibuf->r.wpos;
+
+ if (IMSG_HEADER_SIZE > av)
+ return (0);
+
+ memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+ if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+ imsg->hdr.len > MAX_IMSGSIZE) {
+ log_warnx("imsg_get: imsg hdr len %u out of bounds, type=%u",
+ imsg->hdr.len, imsg->hdr.type);
+ return (-1);
+ }
+ if (imsg->hdr.len > av)
+ return (0);
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+ if ((imsg->data = malloc(datalen)) == NULL) {
+ log_warn("imsg_get");
+ return (-1);
+ }
+ memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+ if (imsg->hdr.len < av) {
+ left = av - imsg->hdr.len;
+ memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+ ibuf->r.wpos = left;
+ } else
+ ibuf->r.wpos = 0;
+
+ return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid,
+ pid_t pid, void *data, u_int16_t datalen)
+{
+ struct buf *wbuf;
+ int n;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ if (imsg_add(wbuf, data, datalen) == -1)
+ return (-1);
+
+ if ((n = imsg_close(ibuf, wbuf)) < 0)
+ return (-1);
+
+ return (n);
+}
+
+/* ARGSUSED */
+struct buf *
+imsg_create(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid,
+ pid_t pid, u_int16_t datalen)
+{
+ struct buf *wbuf;
+ struct imsg_hdr hdr;
+
+ if (datalen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
+ log_warnx("imsg_create: len %u > MAX_IMSGSIZE; "
+ "type %u peerid %lu", datalen + IMSG_HEADER_SIZE,
+ type, peerid);
+ return (NULL);
+ }
+
+ hdr.len = (u_int16_t)(datalen + IMSG_HEADER_SIZE);
+ hdr.type = type;
+ hdr.peerid = peerid;
+ if ((hdr.pid = pid) == 0)
+ hdr.pid = ibuf->pid;
+ if ((wbuf = buf_open(hdr.len)) == NULL) {
+ log_warn("imsg_create: buf_open");
+ return (NULL);
+ }
+ if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+ return (NULL);
+
+ return (wbuf);
+}
+
+int
+imsg_add(struct buf *msg, void *data, u_int16_t datalen)
+{
+ if (datalen)
+ if (buf_add(msg, data, datalen) == -1) {
+ log_warnx("imsg_add: buf_add error");
+ buf_free(msg);
+ return (-1);
+ }
+ return (datalen);
+}
+
+int
+imsg_close(struct imsgbuf *ibuf, struct buf *msg)
+{
+ int n;
+
+ if ((n = buf_close(&ibuf->w, msg)) < 0) {
+ log_warnx("imsg_close: buf_close error");
+ buf_free(msg);
+ return (-1);
+ }
+ imsg_event_add(ibuf);
+
+ return (n);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+ free(imsg->data);
+}
diff --git a/usr.sbin/dvmrpd/in_cksum.c b/usr.sbin/dvmrpd/in_cksum.c
new file mode 100644
index 00000000000..67a2145a73f
--- /dev/null
+++ b/usr.sbin/dvmrpd/in_cksum.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: in_cksum.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+/* $NetBSD: in_cksum.c,v 1.3 1995/04/22 13:53:48 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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 University of
+ * California, Lawrence Berkeley Laboratory and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * 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.
+ *
+ * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL)
+ */
+
+#include <sys/types.h>
+
+#include "dvmrpd.h"
+#include "log.h"
+
+/*
+ * Checksum routine for Internet Protocol family headers.
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ * In particular, it should not be this one.
+ */
+u_int16_t
+in_cksum(void *p, size_t l)
+{
+ unsigned int sum = 0;
+ int len, oddbyte = 0, v = 0;
+ u_char *cp = p;
+
+ /* ensure that < 2^16 bytes being summed */
+ if (l >= (1 << 16))
+ fatalx("in_cksum: packet to big");
+ len = (int)l;
+
+ while (len > 0) {
+ if (oddbyte) {
+ sum += v + *cp++;
+ len--;
+ }
+ if (((long)cp & 1) == 0) {
+ while ((len -= 2) >= 0) {
+ sum += *(u_short *)cp;
+ cp += 2;
+ }
+ } else {
+ while ((len -= 2) >= 0) {
+ sum += *cp++ << 8;
+ sum += *cp++;
+ }
+ }
+ if ((oddbyte = len & 1) != 0)
+ v = *cp << 8;
+ }
+ if (oddbyte)
+ sum += v;
+ sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */
+ sum += sum >> 16; /* add potential last carry */
+ return (0xffff & ~sum);
+}
diff --git a/usr.sbin/dvmrpd/interface.c b/usr.sbin/dvmrpd/interface.c
new file mode 100644
index 00000000000..1f37aedb139
--- /dev/null
+++ b/usr.sbin/dvmrpd/interface.c
@@ -0,0 +1,688 @@
+/* $OpenBSD: interface.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <event.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+void if_probe_timer(int, short, void *);
+int if_start_probe_timer(struct iface *);
+int if_stop_probe_timer(struct iface *);
+void if_query_timer(int, short, void *);
+int if_start_query_timer(struct iface *);
+int if_stop_query_timer(struct iface *);
+void if_querier_present_timer(int, short, void *);
+int if_start_querier_present_timer(struct iface *);
+int if_stop_querier_present_timer(struct iface *);
+int if_reset_querier_present_timer(struct iface *);
+int if_act_start(struct iface *);
+int if_act_query_seen(struct iface *);
+int if_act_reset(struct iface *);
+
+struct {
+ int state;
+ enum iface_event event;
+ enum iface_action action;
+ int new_state;
+} iface_fsm[] = {
+ /* current state event that happened action to take resulting state */
+ {IF_STA_DOWN, IF_EVT_UP, IF_ACT_STRT, 0},
+ {IF_STA_ACTIVE, IF_EVT_QRECVD, IF_ACT_QPRSNT, 0},
+ {IF_STA_NONQUERIER, IF_EVT_QPRSNTTMOUT, IF_ACT_STRT, 0},
+ {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN},
+ {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0},
+};
+
+const char * const if_action_names[] = {
+ "NOTHING",
+ "START",
+ "QPRSNT",
+ "RESET"
+};
+
+static const char * const if_event_names[] = {
+ "NOTHING",
+ "UP",
+ "QTMOUT",
+ "QRECVD",
+ "QPRSNTTMOUT",
+ "DOWN"
+};
+
+int
+if_fsm(struct iface *iface, enum iface_event event)
+{
+ int old_state;
+ int new_state = 0;
+ int i, ret = 0;
+
+ old_state = iface->state;
+
+ for (i = 0; iface_fsm[i].state != -1; i++)
+ if ((iface_fsm[i].state & old_state) &&
+ (iface_fsm[i].event == event)) {
+ new_state = iface_fsm[i].new_state;
+ break;
+ }
+
+ if (iface_fsm[i].state == -1) {
+ /* XXX event outside of the defined fsm, ignore it. */
+ log_debug("fsm_if: interface %s, "
+ "event '%s' not expected in state '%s'", iface->name,
+ if_event_name(event), if_state_name(old_state));
+ return (0);
+ }
+
+ switch (iface_fsm[i].action) {
+ case IF_ACT_STRT:
+ ret = if_act_start(iface);
+ break;
+ case IF_ACT_QPRSNT:
+ ret = if_act_query_seen(iface);
+ break;
+ case IF_ACT_RST:
+ ret = if_act_reset(iface);
+ break;
+ case IF_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ if (ret) {
+ log_debug("fsm_if: error changing state for interface %s, "
+ "event '%s', state '%s'", iface->name, if_event_name(event),
+ if_state_name(old_state));
+ return (-1);
+ }
+
+ if (new_state != 0)
+ iface->state = new_state;
+
+ log_debug("fsm_if: event '%s' resulted in action '%s' and changing "
+ "state for interface %s from '%s' to '%s'",
+ if_event_name(event), if_action_name(iface_fsm[i].action),
+ iface->name, if_state_name(old_state), if_state_name(iface->state));
+
+ return (ret);
+}
+
+struct iface *
+if_new(struct kif *kif)
+{
+ struct sockaddr_in *sain;
+ struct iface *iface;
+ struct ifreq *ifr;
+ int s;
+
+ if ((iface = calloc(1, sizeof(*iface))) == NULL)
+ err(1, "if_new: calloc");
+
+ iface->state = IF_STA_DOWN;
+ iface->passive = 1;
+
+ LIST_INIT(&iface->nbr_list);
+ TAILQ_INIT(&iface->group_list);
+ strlcpy(iface->name, kif->ifname, sizeof(iface->name));
+
+ if ((ifr = calloc(1, sizeof(*ifr))) == NULL)
+ err(1, "if_new: calloc");
+
+ /* set up ifreq */
+ strlcpy(ifr->ifr_name, kif->ifname, sizeof(ifr->ifr_name));
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ err(1, "if_new: socket");
+
+ /* get type */
+ if ((kif->flags & IFF_POINTOPOINT))
+ iface->type = IF_TYPE_POINTOPOINT;
+ if ((kif->flags & IFF_BROADCAST) &&
+ (kif->flags & IFF_MULTICAST))
+ iface->type = IF_TYPE_BROADCAST;
+
+ /* get mtu, index and flags */
+ iface->mtu = kif->mtu;
+ iface->ifindex = kif->ifindex;
+ iface->flags = kif->flags;
+ iface->linkstate = kif->link_state;
+ iface->media_type = kif->media_type;
+
+ /* get address */
+ if (ioctl(s, SIOCGIFADDR, (caddr_t)ifr) < 0)
+ err(1, "if_new: cannot get address");
+ sain = (struct sockaddr_in *) &ifr->ifr_addr;
+ iface->addr = sain->sin_addr;
+
+ /* get mask */
+ if (ioctl(s, SIOCGIFNETMASK, (caddr_t)ifr) < 0)
+ err(1, "if_new: cannot get mask");
+ sain = (struct sockaddr_in *) &ifr->ifr_addr;
+ iface->mask = sain->sin_addr;
+
+ /* get p2p dst address */
+ if (iface->type == IF_TYPE_POINTOPOINT) {
+ if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)ifr) < 0)
+ err(1, "if_new: cannot get dst addr");
+ sain = (struct sockaddr_in *) &ifr->ifr_addr;
+ iface->dst = sain->sin_addr;
+ }
+
+ free(ifr);
+ close(s);
+
+ return (iface);
+}
+
+void
+if_init(struct dvmrpd_conf *xconf, struct iface *iface)
+{
+ /* set event handlers for interface */
+ evtimer_set(&iface->probe_timer, if_probe_timer, iface);
+ evtimer_set(&iface->query_timer, if_query_timer, iface);
+ evtimer_set(&iface->querier_present_timer, if_querier_present_timer,
+ iface);
+
+ TAILQ_INIT(&iface->rr_list);
+
+ iface->fd = xconf->dvmrp_socket;
+ iface->gen_id = xconf->gen_id;
+}
+
+int
+if_del(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+
+ /* clear lists etc */
+ while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) {
+ LIST_REMOVE(nbr, entry);
+ nbr_del(nbr);
+ }
+ group_list_clr(iface);
+
+ return (-1);
+}
+
+int
+if_nbr_list_empty(struct iface *iface)
+{
+ return (LIST_EMPTY(&iface->nbr_list));
+}
+
+/* timers */
+void
+if_probe_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+ struct timeval tv;
+
+ send_probe(iface);
+
+ /* reschedule probe_timer */
+ if (!iface->passive) {
+ timerclear(&tv);
+ tv.tv_sec = iface->probe_interval;
+ evtimer_add(&iface->probe_timer, &tv);
+ }
+}
+
+int
+if_start_probe_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ return (evtimer_add(&iface->probe_timer, &tv));
+}
+
+int
+if_stop_probe_timer(struct iface *iface)
+{
+ return (evtimer_del(&iface->probe_timer));
+}
+
+void
+if_query_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+ struct timeval tv;
+
+ /* send a general query */
+ send_igmp_query(iface, NULL);
+
+ /* reschedule query_timer */
+ if (!iface->passive) {
+ timerclear(&tv);
+ if (iface->startup_query_counter != 0) {
+ tv.tv_sec = iface->startup_query_interval;
+ iface->startup_query_counter--;
+ } else
+ tv.tv_sec = iface->query_interval;
+
+ evtimer_add(&iface->query_timer, &tv);
+ }
+}
+
+int
+if_start_query_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ return (evtimer_add(&iface->query_timer, &tv));
+}
+
+int
+if_stop_query_timer(struct iface *iface)
+{
+ return (evtimer_del(&iface->query_timer));
+}
+
+void
+if_querier_present_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+
+ if_fsm(iface, IF_EVT_QPRSNTTMOUT);
+}
+
+int
+if_start_querier_present_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ /* Other Querier Present Interval */
+ timerclear(&tv);
+ tv.tv_sec = iface->robustness * iface->query_interval +
+ (iface->query_resp_interval / 2);
+
+ return (evtimer_add(&iface->querier_present_timer, &tv));
+}
+
+int
+if_stop_querier_present_timer(struct iface *iface)
+{
+ return (evtimer_del(&iface->querier_present_timer));
+}
+
+int
+if_reset_querier_present_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ /* Other Querier Present Interval */
+ timerclear(&tv);
+ tv.tv_sec = iface->robustness * iface->query_interval +
+ (iface->query_resp_interval / 2);
+
+ return (evtimer_add(&iface->querier_present_timer, &tv));
+}
+
+/* actions */
+int
+if_act_start(struct iface *iface)
+{
+ struct in_addr addr;
+ struct timeval now;
+
+ if (iface->passive) {
+ log_debug("if_act_start: cannot start passive interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ if (!((iface->flags & IFF_UP) &&
+ (iface->linkstate != LINK_STATE_DOWN))) {
+ log_debug("if_act_start: interface %s link down",
+ iface->name);
+ return (0);
+ }
+
+ gettimeofday(&now, NULL);
+ iface->uptime = now.tv_sec;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ inet_aton(AllSystems, &addr);
+ if (if_join_group(iface, &addr)) {
+ log_warnx("if_act_start: error joining group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ return (-1);
+ }
+ inet_aton(AllRouters, &addr);
+ if (if_join_group(iface, &addr)) {
+ log_warnx("if_act_start: error joining group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ return (-1);
+ }
+ inet_aton(AllDVMRPRouters, &addr);
+ if (if_join_group(iface, &addr)) {
+ log_warnx("if_act_start: error joining group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ return (-1);
+ }
+
+ iface->state = IF_STA_QUERIER;
+ if_start_query_timer(iface);
+ if_start_probe_timer(iface);
+ iface->startup_query_counter = iface->startup_query_cnt;
+ break;
+ default:
+ fatalx("if_act_start: unknown type");
+ }
+
+ return (0);
+}
+
+int
+if_act_query_seen(struct iface *iface)
+{
+ log_debug("if_act_query_seen: interface %s", iface->name);
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ /* XXX iface p2p */
+ break;
+ case IF_TYPE_BROADCAST:
+ iface->state = IF_STA_NONQUERIER;
+ if_stop_query_timer(iface);
+ if_reset_querier_present_timer(iface);
+ break;
+ default:
+ fatalx("if_act_querier_seen: unknown type");
+ }
+
+ return (0);
+}
+
+int
+if_act_reset(struct iface *iface)
+{
+ struct in_addr addr;
+ struct nbr *nbr;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ inet_aton(AllSystems, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: error leaving group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ return (-1);
+ }
+ inet_aton(AllRouters, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: error leaving group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ return (-1);
+ }
+ inet_aton(AllDVMRPRouters, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: error leaving group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ return (-1);
+ }
+
+ iface->state = IF_STA_DOWN;
+ iface->gen_id++;
+ if_stop_query_timer(iface);
+ if_stop_querier_present_timer(iface);
+ /* XXX clear nbr list? */
+ break;
+ default:
+ fatalx("if_act_reset: unknown type");
+ }
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) {
+ log_debug("if_act_reset: error killing neighbor %s",
+ inet_ntoa(nbr->id));
+ }
+ }
+
+ group_list_clr(iface); /* XXX clear group list? */
+
+ return (0);
+}
+
+const char *
+if_event_name(int event)
+{
+ return (if_event_names[event]);
+}
+
+const char *
+if_action_name(int action)
+{
+ return (if_action_names[action]);
+}
+
+/* misc */
+int
+if_set_mcast_ttl(int fd, u_int8_t ttl)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ log_warn("if_set_mcast_ttl: error setting "
+ "IP_MULTICAST_TTL to %d", ttl);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_tos(int fd, int tos)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS,
+ (int *)&tos, sizeof(tos)) < 0) {
+ log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+if_set_recvbuf(int fd)
+{
+ int bsize;
+
+ bsize = 65535;
+ while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
+ sizeof(bsize)) == -1)
+ bsize /= 2;
+}
+
+int
+if_join_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ mreq.imr_multiaddr.s_addr = addr->s_addr;
+ mreq.imr_interface.s_addr = iface->addr.s_addr;
+
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_debug("if_join_group: error IP_ADD_MEMBERSHIP, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+ break;
+ default:
+ fatalx("if_join_group: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_leave_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ mreq.imr_multiaddr.s_addr = addr->s_addr;
+ mreq.imr_interface.s_addr = iface->addr.s_addr;
+
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_debug("if_leave_group: error IP_DROP_MEMBERSHIP, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+ break;
+ default:
+ fatalx("if_leave_group: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_set_mcast(struct iface *iface)
+{
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&iface->addr.s_addr,
+ sizeof(iface->addr.s_addr)) < 0) {
+ log_debug("if_set_mcast: error setting "
+ "IP_MULTICAST_IF, interface %s", iface->name);
+ return (-1);
+ }
+ break;
+ default:
+ fatalx("if_set_mcast: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_set_mcast_loop(int fd)
+{
+ u_int8_t loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0) {
+ log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
+ return (-1);
+ }
+
+ return (0);
+}
+
+struct ctl_iface *
+if_to_ctl(struct iface *iface)
+{
+ static struct ctl_iface ictl;
+ struct timeval tv, now, res;
+
+ memcpy(ictl.name, iface->name, sizeof(ictl.name));
+ memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
+ memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
+ memcpy(&ictl.querier, &iface->querier, sizeof(ictl.querier));
+
+ ictl.ifindex = iface->ifindex;
+ ictl.state = iface->state;
+ ictl.mtu = iface->mtu;
+ ictl.nbr_cnt = 0; /* XXX */
+ ictl.adj_cnt = 0; /* XXX */
+
+ ictl.gen_id = iface->gen_id;
+ ictl.group_cnt = iface->group_cnt;
+ ictl.baudrate = iface->baudrate;
+ ictl.probe_interval = iface->probe_interval;
+ ictl.query_interval = iface->query_interval;
+ ictl.query_resp_interval = iface->query_resp_interval;
+ ictl.recv_query_resp_interval = iface->recv_query_resp_interval;
+ ictl.group_member_interval = iface->group_member_interval;
+ ictl.querier_present_interval = iface->querier_present_interval;
+ ictl.startup_query_interval = iface->startup_query_interval;
+ ictl.startup_query_cnt = iface->startup_query_cnt;
+ ictl.last_member_query_interval = iface->last_member_query_interval;
+ ictl.last_member_query_cnt = iface->last_member_query_cnt;
+ ictl.last_member_query_time = iface->last_member_query_time;
+ ictl.v1_querier_present_tmout = iface->v1_querier_present_tmout;
+ ictl.v1_host_present_interval = iface->v1_host_present_interval;
+ ictl.dead_interval = iface->dead_interval;
+
+ ictl.baudrate = iface->baudrate;
+ ictl.flags = iface->flags;
+ ictl.metric = iface->metric;
+ ictl.type = iface->type;
+ ictl.robustness = iface->robustness;
+ ictl.linkstate = iface->linkstate;
+ ictl.passive = iface->passive;
+ ictl.igmp_version = iface->igmp_version;
+ ictl.mediatype = iface->media_type;
+
+ gettimeofday(&now, NULL);
+ if (evtimer_pending(&iface->probe_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ ictl.probe_timer = res.tv_sec;
+ } else
+ ictl.probe_timer = -1;
+
+ if (evtimer_pending(&iface->query_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ ictl.query_timer = res.tv_sec;
+ } else
+ ictl.query_timer = -1;
+
+ if (evtimer_pending(&iface->querier_present_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ ictl.querier_present_timer = res.tv_sec;
+ } else
+ ictl.querier_present_timer = -1;
+
+ if (iface->state != IF_STA_DOWN) {
+ ictl.uptime = now.tv_sec - iface->uptime;
+ } else
+ ictl.uptime = 0;
+
+ return (&ictl);
+}
diff --git a/usr.sbin/dvmrpd/kmroute.c b/usr.sbin/dvmrpd/kmroute.c
new file mode 100644
index 00000000000..911319bc995
--- /dev/null
+++ b/usr.sbin/dvmrpd/kmroute.c
@@ -0,0 +1,256 @@
+/* $OpenBSD: kmroute.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/ip_mroute.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "dvmrpe.h"
+#include "log.h"
+
+extern struct dvmrpd_conf *conf;
+char *mroute_ptr; /* packet buffer */
+
+void main_imsg_compose_rde(int, pid_t, void *, u_int16_t);
+
+int
+kmr_init(int fd)
+{
+ struct iface *iface;
+ struct route_report rr;
+
+ LIST_FOREACH(iface, &conf->iface_list, entry) {
+ log_debug("kmr_init: interface %s", iface->name);
+
+ rr.net.s_addr = iface->addr.s_addr & iface->mask.s_addr;
+ rr.mask = iface->mask;
+ rr.nexthop.s_addr = 0;
+ rr.metric = iface->metric;
+ rr.ifindex = iface->ifindex;
+ main_imsg_compose_rde(IMSG_ROUTE_REPORT, -1, &rr, sizeof(rr));
+
+ mrt_add_vif(conf->mroute_socket, iface);
+ }
+
+ if ((mroute_ptr = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("kmr_init");
+
+ return (0);
+}
+
+void
+kmr_shutdown(void)
+{
+ struct iface *iface;
+
+ kmr_mfc_decouple();
+ kmroute_clear();
+
+ LIST_FOREACH(iface, &conf->iface_list, entry) {
+ log_debug("kmr_shutdown: interface %s", iface->name);
+
+ mrt_del_vif(conf->mroute_socket, iface);
+ }
+
+ free(mroute_ptr);
+}
+
+void
+kmr_recv_msg(int fd, short event, void *bula)
+{
+ struct mfc mfc;
+ struct igmpmsg kernel_msg;
+ char *buf;
+ ssize_t r;
+
+ if (event != EV_READ)
+ return;
+
+ /* setup buffer */
+ buf = mroute_ptr;
+
+ if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) {
+ if (errno != EAGAIN && errno != EINTR)
+ log_debug("kmr_recv_msg: error receiving packet");
+ return;
+ }
+
+ memcpy(&kernel_msg, buf, sizeof(kernel_msg));
+
+ /* we are only interested in kernel messages */
+ if (kernel_msg.im_mbz != 0)
+ return;
+
+ switch (kernel_msg.im_msgtype) {
+ case IGMPMSG_NOCACHE:
+ /* verify that dst is a multicast group */
+ if (!IN_MULTICAST(ntohl(kernel_msg.im_dst.s_addr))) {
+ log_debug("kmr_recv_msg: kernel providing garbage!");
+ return;
+ }
+
+ /* send MFC entry to RDE */
+ mfc.origin = kernel_msg.im_src;
+ mfc.group = kernel_msg.im_dst;
+ mfc.ifindex = kernel_msg.im_vif;
+ main_imsg_compose_rde(IMSG_MFC_ADD, 0, &mfc, sizeof(mfc));
+ break;
+ case IGMPMSG_WRONGVIF:
+ case IGMPMSG_WHOLEPKT:
+ case IGMPMSG_BW_UPCALL:
+ default:
+ log_debug("kmr_recv_msg: unhandled msg type %d!",
+ kernel_msg.im_msgtype);
+ }
+}
+
+void
+kmr_mfc_couple(void)
+{
+ log_info("kernel multicast forwarding cache coupled");
+}
+
+void
+kmr_mfc_decouple(void)
+{
+ log_info("kernel multicast forwarding cache decoupled");
+}
+
+void
+kmroute_clear(void)
+{
+
+}
+
+int
+mrt_init(int fd)
+{
+ int flag = 1;
+
+ if (setsockopt(fd, IPPROTO_IP, MRT_INIT, &flag,
+ sizeof(flag)) < 0) {
+ log_warn("mrt_init: error setting MRT_INIT");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+mrt_done(int fd)
+{
+ int flag = 0;
+
+ if (setsockopt(fd, IPPROTO_IP, MRT_DONE, &flag,
+ sizeof(flag)) < 0) {
+ log_warn("mrt_done: error setting MRT_DONE");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+mrt_add_vif(int fd, struct iface *iface)
+{
+ struct vifctl vc;
+
+ vc.vifc_vifi = iface->ifindex;
+ vc.vifc_flags = 0;
+ vc.vifc_threshold = 1;
+ vc.vifc_rate_limit = 0;
+ vc.vifc_lcl_addr.s_addr = iface->addr.s_addr;
+ vc.vifc_rmt_addr.s_addr = 0;
+
+ if (setsockopt(fd, IPPROTO_IP, MRT_ADD_VIF, &vc,
+ sizeof(vc)) < 0) {
+ log_warn("mrt_add_vif: error adding VIF");
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+mrt_del_vif(int fd, struct iface *iface)
+{
+ vifi_t vifi;
+
+ vifi = iface->ifindex;
+
+ if (setsockopt(fd, IPPROTO_IP, MRT_DEL_VIF, &vifi,
+ sizeof(vifi)) < 0)
+ log_warn("mrt_del_vif: error deleting VIF");
+}
+
+int
+mrt_add_mfc(int fd, struct mfc *mfc)
+{
+ struct mfcctl mc;
+ int i;
+
+ log_debug("mrt_add_mfc: interface %d, group %s", mfc->ifindex,
+ inet_ntoa(mfc->group));
+
+ mc.mfcc_origin = mfc->origin;
+ mc.mfcc_mcastgrp = mfc->group;
+ mc.mfcc_parent = mfc->ifindex;
+
+ for (i = 0; i < MAXVIFS; i++) {
+ mc.mfcc_ttls[i] = mfc->ttls[i];
+ }
+
+ if (setsockopt(fd, IPPROTO_IP, MRT_ADD_MFC, &mc, sizeof(mc))
+ < 0) {
+ log_warnx("mrt_add_mfc: error adding group %s to interface %d",
+ inet_ntoa(mfc->group), mfc->ifindex);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+mrt_del_mfc(int fd, struct mfc *mfc)
+{
+ struct mfcctl mc;
+
+ log_debug("mrt_del_mfc: group %s", inet_ntoa(mfc->group));
+
+ mc.mfcc_origin = mfc->origin;
+ mc.mfcc_mcastgrp = mfc->group;
+
+ if (setsockopt(fd, IPPROTO_IP, MRT_DEL_MFC, &mc, sizeof(mc))
+ < 0) {
+ log_warnx("mrt_del_mfc: error deleting group %s ",
+ inet_ntoa(mfc->group));
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/dvmrpd/kroute.c b/usr.sbin/dvmrpd/kroute.c
new file mode 100644
index 00000000000..51e9f7571ee
--- /dev/null
+++ b/usr.sbin/dvmrpd/kroute.c
@@ -0,0 +1,398 @@
+/* $OpenBSD: kroute.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/tree.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dvmrp.h"
+#include "dvmrpd.h"
+#include "log.h"
+
+struct {
+ u_int32_t rtseq;
+ pid_t pid;
+ struct event ev;
+} kr_state;
+
+struct kif_node {
+ RB_ENTRY(kif_node) entry;
+ struct kif k;
+};
+
+int kif_compare(struct kif_node *, struct kif_node *);
+
+struct kif_node *kif_find(int);
+int kif_insert(struct kif_node *);
+int kif_remove(struct kif_node *);
+void kif_clear(void);
+
+in_addr_t prefixlen2mask(u_int8_t);
+void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
+void if_change(u_short, int, struct if_data *);
+void if_announce(void *);
+
+int fetchifs(int);
+
+RB_HEAD(kif_tree, kif_node) kit;
+RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
+RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
+
+int
+kif_init(void)
+{
+ RB_INIT(&kit);
+
+ if (fetchifs(0) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+kr_init(int fs)
+{
+ int opt, fd;
+
+
+ if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
+ log_warn("kr_init: socket");
+ return (-1);
+ }
+
+ /* not interested in my own messages */
+ if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, &opt, sizeof(opt)) == -1)
+ log_warn("kr_init: setsockopt"); /* not fatal */
+
+ kr_state.pid = getpid();
+ kr_state.rtseq = 1;
+
+ event_set(&kr_state.ev, fd, EV_READ | EV_PERSIST,
+ kr_dispatch_msg, NULL);
+ event_add(&kr_state.ev, NULL);
+
+ return (0);
+}
+
+void
+kr_shutdown(void)
+{
+ kif_clear();
+}
+
+void
+kr_ifinfo(char *ifname)
+{
+ struct kif_node *kif;
+
+ RB_FOREACH(kif, kif_tree, &kit)
+ if (!strcmp(ifname, kif->k.ifname)) {
+ main_imsg_compose_dvmrpe(IMSG_CTL_IFINFO, 0,
+ &kif->k, sizeof(kif->k));
+ return;
+ }
+}
+
+/* rb-tree compare */
+int
+kif_compare(struct kif_node *a, struct kif_node *b)
+{
+ return (b->k.ifindex - a->k.ifindex);
+}
+
+struct kif_node *
+kif_find(int ifindex)
+{
+ struct kif_node s;
+
+ bzero(&s, sizeof(s));
+ s.k.ifindex = ifindex;
+
+ return (RB_FIND(kif_tree, &kit, &s));
+}
+
+struct kif *
+kif_findname(char *ifname)
+{
+ struct kif_node *kif;
+
+ RB_FOREACH(kif, kif_tree, &kit)
+ if (!strcmp(ifname, kif->k.ifname))
+ return (&kif->k);
+
+ return (NULL);
+}
+
+int
+kif_insert(struct kif_node *kif)
+{
+ if (RB_INSERT(kif_tree, &kit, kif) != NULL) {
+ log_warnx("RB_INSERT(kif_tree, &kit, kif)");
+ free(kif);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+kif_remove(struct kif_node *kif)
+{
+ if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
+ log_warnx("RB_REMOVE(kif_tree, &kit, kif)");
+ return (-1);
+ }
+
+ free(kif);
+ return (0);
+}
+
+void
+kif_clear(void)
+{
+ struct kif_node *kif;
+
+ while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
+ kif_remove(kif);
+}
+
+/* misc */
+u_int8_t
+prefixlen_classful(in_addr_t ina)
+{
+ /* it hurt to write this. */
+
+ if (ina >= 0xf0000000U) /* class E */
+ return (32);
+ else if (ina >= 0xe0000000U) /* class D */
+ return (4);
+ else if (ina >= 0xc0000000U) /* class C */
+ return (24);
+ else if (ina >= 0x80000000U) /* class B */
+ return (16);
+ else /* class A */
+ return (8);
+}
+
+u_int8_t
+mask2prefixlen(in_addr_t ina)
+{
+ if (ina == 0)
+ return (0);
+ else
+ return (33 - ffs(ntohl(ina)));
+}
+
+in_addr_t
+prefixlen2mask(u_int8_t prefixlen)
+{
+ if (prefixlen == 0)
+ return (0);
+
+ return (0xffffffff << (32 - prefixlen));
+}
+
+void
+if_change(u_short ifindex, int flags, struct if_data *ifd)
+{
+ struct kif_node *kif;
+ u_int8_t reachable;
+
+ if ((kif = kif_find(ifindex)) == NULL) {
+ log_warnx("interface with index %u not found",
+ ifindex);
+ return;
+ }
+
+ kif->k.flags = flags;
+ kif->k.link_state = ifd->ifi_link_state;
+ kif->k.media_type = ifd->ifi_type;
+ kif->k.baudrate = ifd->ifi_baudrate;
+
+ if ((reachable = (flags & IFF_UP) &&
+ (ifd->ifi_link_state != LINK_STATE_DOWN)) == kif->k.nh_reachable)
+ return; /* nothing changed wrt nexthop validity */
+
+ kif->k.nh_reachable = reachable;
+ main_imsg_compose_dvmrpe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k));
+}
+
+void
+if_announce(void *msg)
+{
+ struct if_announcemsghdr *ifan;
+ struct kif_node *kif;
+
+ ifan = msg;
+
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
+ log_warn("if_announce");
+ return;
+ }
+
+ kif->k.ifindex = ifan->ifan_index;
+ strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname));
+ kif_insert(kif);
+ break;
+ case IFAN_DEPARTURE:
+ kif = kif_find(ifan->ifan_index);
+ kif_remove(kif);
+ break;
+ }
+}
+
+/* rtsock */
+#define ROUNDUP(a, size) \
+ (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a))
+
+void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ sa = (struct sockaddr *)((char *)(sa) +
+ ROUNDUP(sa->sa_len, sizeof(long)));
+ } else
+ rti_info[i] = NULL;
+ }
+}
+
+int
+fetchifs(int ifindex)
+{
+ size_t len;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct if_msghdr ifm;
+ struct kif_node *kif;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_dl *sdl;
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = ifindex;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchif");
+ return (-1);
+ }
+ if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ free(buf);
+ return (-1);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm.ifm_msglen) {
+ memcpy(&ifm, next, sizeof(ifm));
+ sa = (struct sockaddr *)(next + sizeof(ifm));
+ get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
+
+ if (ifm.ifm_type != RTM_IFINFO)
+ continue;
+
+ if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
+ log_warn("fetchifs");
+ free(buf);
+ return (-1);
+ }
+
+ kif->k.ifindex = ifm.ifm_index;
+ kif->k.flags = ifm.ifm_flags;
+ kif->k.link_state = ifm.ifm_data.ifi_link_state;
+ kif->k.media_type = ifm.ifm_data.ifi_type;
+ kif->k.baudrate = ifm.ifm_data.ifi_baudrate;
+ kif->k.mtu = ifm.ifm_data.ifi_mtu;
+ kif->k.nh_reachable = (kif->k.flags & IFF_UP) &&
+ (ifm.ifm_data.ifi_link_state != LINK_STATE_DOWN);
+ if ((sa = rti_info[RTAX_IFP]) != NULL)
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (sdl->sdl_nlen > 0)
+ strlcpy(kif->k.ifname, sdl->sdl_data,
+ sizeof(kif->k.ifname));
+ }
+
+ kif_insert(kif);
+ }
+ free(buf);
+ return (0);
+}
+
+void
+kr_dispatch_msg(int fd, short event, void *bula)
+{
+ char buf[RT_BUF_SIZE];
+ ssize_t n;
+ char *next, *lim;
+ struct rt_msghdr *rtm;
+ struct if_msghdr ifm;
+
+ if ((n = read(fd, &buf, sizeof(buf))) == -1)
+ fatal("dispatch_rtmsg: read error");
+
+ if (n == 0)
+ fatalx("routing socket closed");
+
+ lim = buf + n;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+
+ switch (rtm->rtm_type) {
+ case RTM_IFINFO:
+ memcpy(&ifm, next, sizeof(ifm));
+ if_change(ifm.ifm_index, ifm.ifm_flags,
+ &ifm.ifm_data);
+ break;
+ case RTM_IFANNOUNCE:
+ if_announce(next);
+ break;
+ default:
+ /* ignore for now */
+ break;
+ }
+ }
+}
+
diff --git a/usr.sbin/dvmrpd/log.c b/usr.sbin/dvmrpd/log.c
new file mode 100644
index 00000000000..f10d9e044c9
--- /dev/null
+++ b/usr.sbin/dvmrpd/log.c
@@ -0,0 +1,225 @@
+/* $OpenBSD: log.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "log.h"
+
+static const char * const procnames[] = {
+ "parent",
+ "dvmrpe",
+ "rde"
+};
+
+int debug;
+
+void logit(int, const char *, ...);
+
+void
+log_init(int n_debug)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ tzset();
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (debug) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ logit(LOG_CRIT, "fatal in %s: %s", procnames[dvmrpd_process],
+ strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal in %s: %s: %s",
+ procnames[dvmrpd_process], emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal in %s: %s",
+ procnames[dvmrpd_process], emsg);
+
+ if (dvmrpd_process == PROC_MAIN)
+ exit(1);
+ else /* parent copes via SIGCHLD */
+ _exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
+
+/* names */
+const char *
+nbr_state_name(int state)
+{
+ switch (state) {
+ case NBR_STA_DOWN:
+ return ("DOWN");
+ case NBR_STA_1_WAY:
+ return ("1-WAY");
+ case NBR_STA_2_WAY:
+ return ("2-WAY");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+if_state_name(int state)
+{
+ switch (state) {
+ case IF_STA_DOWN:
+ return ("DOWN");
+ case IF_STA_QUERIER:
+ return ("QUERIER");
+ case IF_STA_NONQUERIER:
+ return ("NONQUERIER");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+group_state_name(int state)
+{
+ switch (state) {
+ case GRP_STA_NO_MEMB_PRSNT:
+ return ("NO MEMBER");
+ case GRP_STA_MEMB_PRSNT:
+ return ("MEMBER");
+ case GRP_STA_V1_MEMB_PRSNT:
+ return ("V1 MEMBER");
+ case GRP_STA_CHECK_MEMB:
+ return ("CHECKING");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+if_type_name(enum iface_type type)
+{
+ switch (type) {
+ case IF_TYPE_POINTOPOINT:
+ return ("POINTOPOINT");
+ case IF_TYPE_BROADCAST:
+ return ("BROADCAST");
+ }
+ /* NOTREACHED */
+ return ("UNKNOWN");
+}
diff --git a/usr.sbin/dvmrpd/log.h b/usr.sbin/dvmrpd/log.h
new file mode 100644
index 00000000000..9fbc18e29b6
--- /dev/null
+++ b/usr.sbin/dvmrpd/log.h
@@ -0,0 +1,33 @@
+/* $OpenBSD: log.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <stdarg.h>
+
+void log_init(int);
+void vlog(int, const char *, va_list);
+void log_warn(const char *, ...);
+void log_warnx(const char *, ...);
+void log_info(const char *, ...);
+void log_debug(const char *, ...);
+void fatal(const char *) __dead;
+void fatalx(const char *) __dead;
+
+#endif /* _LOG_H_ */
diff --git a/usr.sbin/dvmrpd/nbrs2.c b/usr.sbin/dvmrpd/nbrs2.c
new file mode 100644
index 00000000000..1ea88464dc6
--- /dev/null
+++ b/usr.sbin/dvmrpd/nbrs2.c
@@ -0,0 +1,80 @@
+/* $OpenBSD: nbrs2.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <strings.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+/* DVMRP neighbors2 packet handling */
+int
+send_nbrs2(struct iface *iface, struct in_addr addr, void *data, int len)
+{
+ struct sockaddr_in dst;
+ struct buf *buf;
+ struct dvmrp_hdr *dvmrp_hdr;
+ int ret = 0;
+
+ log_debug("send_nbrs2: interface %s addr %s",
+ iface->name, inet_ntoa(addr));
+
+ if (iface->passive)
+ return (0);
+
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_nbrs2");
+
+ /* DVMRP header */
+ if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT_ACK))
+ goto fail;
+
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr.s_addr = addr.s_addr;
+
+ /* update chksum */
+ dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr));
+ dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_nbrs2");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_nbrs2(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ log_debug("recv_nbrs2: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return;
+}
diff --git a/usr.sbin/dvmrpd/neighbor.c b/usr.sbin/dvmrpd/neighbor.c
new file mode 100644
index 00000000000..c8282c5eaa2
--- /dev/null
+++ b/usr.sbin/dvmrpd/neighbor.c
@@ -0,0 +1,427 @@
+/* $OpenBSD: neighbor.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <event.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "dvmrpe.h"
+#include "log.h"
+#include "rde.h"
+
+LIST_HEAD(nbr_head, nbr);
+
+struct nbr_table {
+ struct nbr_head *hashtbl;
+ u_int32_t hashmask;
+} nbrtable;
+
+#define NBR_HASH(x) \
+ &nbrtable.hashtbl[(x) & nbrtable.hashmask]
+
+u_int32_t peercnt = NBR_CNTSTART;
+
+struct {
+ int state;
+ enum nbr_event event;
+ enum nbr_action action;
+ int new_state;
+} nbr_fsm_tbl[] = {
+ /* current state event that happened action to take resulting state */
+ {NBR_STA_DOWN, NBR_EVT_PROBE_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_1_WAY},
+ {NBR_STA_ACTIVE, NBR_EVT_PROBE_RCVD, NBR_ACT_RST_ITIMER, 0},
+ {NBR_STA_1_WAY, NBR_EVT_2_WAY_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_2_WAY},
+ {NBR_STA_ACTIVE, NBR_EVT_1_WAY_RCVD, NBR_ACT_RESET, NBR_STA_1_WAY},
+ {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN},
+ {NBR_STA_ANY, NBR_EVT_LL_DOWN, NBR_ACT_DEL, NBR_STA_DOWN},
+ {NBR_STA_ANY, NBR_EVT_ITIMER, NBR_ACT_DEL, NBR_STA_DOWN},
+ {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0},
+};
+
+const char * const nbr_event_names[] = {
+ "NOTHING",
+ "PROBE RCVD",
+ "1-WAY RCVD",
+ "2-WAY RCVD",
+ "KILL NBR",
+ "ITIMER",
+ "LL DOWN"
+};
+
+const char * const nbr_action_names[] = {
+ "NOTHING",
+ "RESET ITIMER",
+ "START ITIMER",
+ "RESET",
+ "DELETE",
+ "CLEAR LISTS"
+};
+
+int
+nbr_fsm(struct nbr *nbr, enum nbr_event event)
+{
+ struct timeval now;
+ int old_state;
+ int new_state = 0;
+ int i, ret = 0;
+
+ old_state = nbr->state;
+ for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
+ if ((nbr_fsm_tbl[i].state & old_state) &&
+ (nbr_fsm_tbl[i].event == event)) {
+ new_state = nbr_fsm_tbl[i].new_state;
+ break;
+ }
+
+ if (nbr_fsm_tbl[i].state == -1) {
+ /* XXX event outside of the defined fsm, ignore it. */
+ log_warnx("nbr_fsm: neighbor ID %s, "
+ "event '%s' not expected in state '%s'",
+ inet_ntoa(nbr->id), nbr_event_name(event),
+ nbr_state_name(old_state));
+ return (0);
+ }
+
+ switch (nbr_fsm_tbl[i].action) {
+ case NBR_ACT_RST_ITIMER:
+ ret = nbr_act_reset_itimer(nbr);
+ break;
+ case NBR_ACT_STRT_ITIMER:
+ ret = nbr_act_start_itimer(nbr);
+ break;
+ case NBR_ACT_RESET:
+ /* XXX nbr action reset */
+ break;
+ case NBR_ACT_DEL:
+ ret = nbr_act_delete(nbr);
+ break;
+ case NBR_ACT_CLR_LST:
+ ret = nbr_act_clear_lists(nbr);
+ break;
+ case NBR_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ if (ret) {
+ log_warnx("nbr_fsm: error changing state for neighbor ID %s, "
+ "event '%s', state '%s'", inet_ntoa(nbr->id),
+ nbr_event_name(event), nbr_state_name(old_state));
+ return (-1);
+ }
+
+ if (new_state != 0)
+ nbr->state = new_state;
+
+ if (old_state != nbr->state) {
+ if (old_state & NBR_STA_2_WAY || nbr->state & NBR_STA_2_WAY) {
+ /* neighbor changed from/to 2_WAY */
+
+ gettimeofday(&now, NULL);
+ nbr->uptime = now.tv_sec;
+ }
+
+ log_debug("nbr_fsm: event '%s' resulted in action '%s' and "
+ "changing state for neighbor ID %s from '%s' to '%s'",
+ nbr_event_name(event),
+ nbr_action_name(nbr_fsm_tbl[i].action),
+ inet_ntoa(nbr->id), nbr_state_name(old_state),
+ nbr_state_name(nbr->state));
+ }
+
+ return (ret);
+}
+
+void
+nbr_init(u_int32_t hashsize)
+{
+ u_int32_t hs, i;
+
+ for (hs = 1; hs < hashsize; hs <<= 1)
+ ;
+ nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head));
+ if (nbrtable.hashtbl == NULL)
+ fatal("nbr_init");
+
+ for (i = 0; i < hs; i++)
+ LIST_INIT(&nbrtable.hashtbl[i]);
+
+ nbrtable.hashmask = hs - 1;
+}
+
+struct nbr *
+nbr_new(u_int32_t nbr_id, struct iface *iface, int self)
+{
+ struct nbr_head *head;
+ struct nbr *nbr = NULL;
+
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal("nbr_new");
+
+ nbr->state = NBR_STA_DOWN;
+ nbr->id.s_addr = nbr_id;
+
+ /* get next unused peerid */
+ while (nbr_find_peerid(++peercnt))
+ ;
+ nbr->peerid = peercnt;
+ head = NBR_HASH(nbr->peerid);
+ LIST_INSERT_HEAD(head, nbr, hash);
+
+ /* add to peer list */
+ nbr->iface = iface;
+ LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry);
+
+ TAILQ_INIT(&nbr->rr_list);
+
+ /* set event structures */
+ evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr);
+
+ log_debug("nbr_new: neighbor ID %s, peerid %lu",
+ inet_ntoa(nbr->id), nbr->peerid);
+
+ return (nbr);
+}
+
+int
+nbr_del(struct nbr *nbr)
+{
+ log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id),
+ nbr->peerid);
+
+ /* clear lists */
+ rr_list_clr(&nbr->rr_list);
+
+ LIST_REMOVE(nbr, entry);
+ LIST_REMOVE(nbr, hash);
+
+ free(nbr);
+
+ return (0);
+}
+
+struct nbr *
+nbr_find_peerid(u_int32_t peerid)
+{
+ struct nbr_head *head;
+ struct nbr *nbr;
+
+ head = NBR_HASH(peerid);
+
+ LIST_FOREACH(nbr, head, hash) {
+ if (nbr->peerid == peerid)
+ return (nbr);
+ }
+
+ return (NULL);
+}
+
+struct nbr *
+nbr_find_ip(struct iface *iface, u_int32_t src_ip)
+{
+ struct nbr *nbr = NULL;
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->id.s_addr == src_ip) {
+ return (nbr);
+ }
+ }
+
+ return (NULL);
+}
+
+/* timers */
+void
+nbr_itimer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ log_debug("nbr_itimer: %s", inet_ntoa(nbr->id));
+
+ if (nbr->state == NBR_STA_DOWN) {
+ nbr_del(nbr);
+ } else
+ nbr_fsm(nbr, NBR_EVT_ITIMER);
+}
+
+int
+nbr_start_itimer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ log_debug("nbr_start_itimer: %s", inet_ntoa(nbr->id));
+
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->dead_interval;
+
+ return (evtimer_add(&nbr->inactivity_timer, &tv));
+}
+
+int
+nbr_stop_itimer(struct nbr *nbr)
+{
+ return (evtimer_del(&nbr->inactivity_timer));
+}
+
+int
+nbr_reset_itimer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->dead_interval;
+
+ return (evtimer_add(&nbr->inactivity_timer, &tv));
+}
+
+/* actions */
+int
+nbr_act_start(struct nbr *nbr)
+{
+ log_debug("nbr_act_start: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return (-1);
+}
+
+int
+nbr_act_reset_itimer(struct nbr *nbr)
+{
+ if (nbr_reset_itimer(nbr)) {
+ log_warnx("nbr_act_reset_itimer: cannot schedule inactivity "
+ "timer, neighbor ID %s", inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+nbr_act_start_itimer(struct nbr *nbr)
+{
+ if (nbr_start_itimer(nbr)) {
+ log_warnx("nbr_act_start_itimer: cannot schedule inactivity "
+ "timer, neighbor ID %s",
+ inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ if (nbr->state == NBR_STA_1_WAY) {
+ /* new nbr, send entire route table, unicast */
+ log_debug("nbr_act_start_itimer: nbr %s, send route table",
+ inet_ntoa(nbr->id));
+
+ dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, nbr->peerid, 0,
+ NULL, 0);
+ }
+
+ return (0);
+}
+
+int
+nbr_act_delete(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ log_debug("nbr_act_delete: neighbor ID %s", inet_ntoa(nbr->id));
+
+ /* stop timers */
+ if (nbr_stop_itimer(nbr)) {
+ log_warnx("nbr_act_delete: error removing inactivity timer, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ /* schedule kill timer */
+ timerclear(&tv);
+ tv.tv_sec = DEFAULT_NBR_TMOUT;
+
+ if (evtimer_add(&nbr->inactivity_timer, &tv)) {
+ log_warnx("nbr_act_delete: error scheduling neighbor ID %s "
+ "for removal", inet_ntoa(nbr->id));
+ }
+
+ return (nbr_act_clear_lists(nbr));
+}
+
+int
+nbr_act_clear_lists(struct nbr *nbr)
+{
+ log_debug("nbr_act_clear_lists: neighbor ID %s", inet_ntoa(nbr->id));
+
+ /* XXX clear lists */
+
+ return (0);
+}
+
+/* names */
+const char *
+nbr_event_name(int event)
+{
+ return (nbr_event_names[event]);
+}
+
+const char *
+nbr_action_name(int action)
+{
+ return (nbr_action_names[action]);
+}
+
+struct ctl_nbr *
+nbr_to_ctl(struct nbr *nbr)
+{
+ static struct ctl_nbr nctl;
+ struct timeval tv, now, res;
+
+ memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name));
+ memcpy(&nctl.id, &nbr->id, sizeof(nctl.id));
+ memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr));
+
+ nctl.state = nbr->state;
+
+ gettimeofday(&now, NULL);
+ if (evtimer_pending(&nbr->inactivity_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ if (nbr->state & NBR_STA_DOWN)
+ nctl.dead_timer = DEFAULT_NBR_TMOUT - res.tv_sec;
+ else
+ nctl.dead_timer = res.tv_sec;
+ } else
+ nctl.dead_timer = 0;
+
+ if (nbr->state == NBR_STA_2_WAY) {
+ nctl.uptime = now.tv_sec - nbr->uptime;
+ } else
+ nctl.uptime = 0;
+
+ return (&nctl);
+}
diff --git a/usr.sbin/dvmrpd/packet.c b/usr.sbin/dvmrpd/packet.c
new file mode 100644
index 00000000000..f81a1614308
--- /dev/null
+++ b/usr.sbin/dvmrpd/packet.c
@@ -0,0 +1,288 @@
+/* $OpenBSD: packet.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_mroute.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <event.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+int ip_hdr_sanity_check(const struct ip *, u_int16_t);
+int dvmrp_hdr_sanity_check(const struct ip *, struct dvmrp_hdr *,
+ u_int16_t, const struct iface *);
+struct iface *find_iface(struct dvmrpd_conf *, struct in_addr);
+
+extern struct dvmrpd_conf *deconf;
+
+int
+gen_dvmrp_hdr(struct buf *buf, struct iface *iface, u_int8_t code)
+{
+ struct dvmrp_hdr dvmrp_hdr;
+
+ bzero(&dvmrp_hdr, sizeof(dvmrp_hdr));
+ dvmrp_hdr.type = PKT_TYPE_DVMRP;
+ dvmrp_hdr.code = code;
+ dvmrp_hdr.chksum = 0; /* updated later */
+ dvmrp_hdr.capabilities = DVMRP_CAP_DEFAULT; /* XXX update */
+ dvmrp_hdr.minor_version = DVMRP_MINOR_VERSION;
+ dvmrp_hdr.major_version = DVMRP_MAJOR_VERSION;
+
+ return (buf_add(buf, &dvmrp_hdr, sizeof(dvmrp_hdr)));
+}
+
+/* send and receive packets */
+int
+send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst)
+{
+ if (iface->passive) {
+ log_warnx("send_packet: cannot send packet on passive "
+ "interface %s", iface->name);
+ return (-1);
+ }
+
+ /* set outgoing interface for multicast traffic */
+ if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr)))
+ if (if_set_mcast(iface) == -1) {
+ log_warn("send_packet: error setting multicast "
+ "interface, %s", iface->name);
+ return (-1);
+ }
+
+ if (sendto(iface->fd, pkt, len, 0,
+ (struct sockaddr *)dst, sizeof(*dst)) == -1 ) {
+ log_warn("send_packet: error sending packet on interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+recv_packet(int fd, short event, void *bula)
+{
+ struct dvmrpd_conf *xconf = bula;
+ struct ip ip_hdr;
+ struct dvmrp_hdr *dvmrp_hdr;
+ struct iface *iface;
+ struct nbr *nbr = NULL;
+ struct in_addr addr;
+ char *buf;
+ ssize_t r;
+ u_int16_t len;
+ int l;
+
+ if (event != EV_READ)
+ return;
+
+ /* setup buffer */
+ buf = pkt_ptr;
+
+ if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) {
+ if (errno != EAGAIN && errno != EINTR)
+ log_debug("recv_packet: error receiving packet");
+ return;
+ }
+
+ len = (u_int16_t)r;
+
+ /* IP header sanity checks */
+ if (len < sizeof(ip_hdr)) {
+ log_warnx("recv_packet: bad packet size");
+ return;
+ }
+
+ memcpy(&ip_hdr, buf, sizeof(ip_hdr));
+ if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1)
+ return;
+ buf += l;
+ len -= l;
+
+ /* find a matching interface */
+ if ((iface = find_iface(xconf, ip_hdr.ip_src)) == NULL) {
+ log_debug("recv_packet: cannot find valid interface, ip src %s",
+ inet_ntoa(ip_hdr.ip_src));
+ return;
+ }
+
+ /* header sanity checks */
+ if (len < sizeof(*dvmrp_hdr)) {
+ log_warnx("recv_packet: bad packet size");
+ return;
+ }
+ dvmrp_hdr = (struct dvmrp_hdr *)buf;
+
+ switch (dvmrp_hdr->type) {
+ /* DVMRP */
+ case PKT_TYPE_DVMRP:
+ if ((l = dvmrp_hdr_sanity_check(&ip_hdr, dvmrp_hdr, len,
+ iface)) == -1)
+ return;
+
+ /*
+ * mrouted compat
+ *
+ * Old mrouted versions, send route reports before establishing
+ * 2-WAY neighbor relationships.
+ */
+ if ((nbr_find_ip(iface, ip_hdr.ip_src.s_addr) == NULL) &&
+ (dvmrp_hdr->code == DVMRP_CODE_REPORT)) {
+ log_debug("recv_packet: route report from neighbor"
+ " ID %s, compat", inet_ntoa(ip_hdr.ip_src));
+ nbr = nbr_new(ip_hdr.ip_src.s_addr, iface, 0);
+ nbr_fsm(nbr, NBR_EVT_PROBE_RCVD);
+ nbr->compat = 1;
+ nbr->addr = ip_hdr.ip_src;
+ }
+
+ if ((dvmrp_hdr->type == PKT_TYPE_DVMRP) &&
+ (dvmrp_hdr->code != DVMRP_CODE_PROBE))
+ /* find neighbor */
+ if ((nbr = nbr_find_ip(iface, ip_hdr.ip_src.s_addr))
+ == NULL) {
+ log_debug("recv_packet: unknown neighbor ID");
+ return;
+ }
+
+ buf += sizeof(*dvmrp_hdr);
+ len = l - sizeof(*dvmrp_hdr);
+
+ inet_aton(AllDVMRPRouters, &addr);
+ if ((ip_hdr.ip_dst.s_addr != addr.s_addr) &&
+ (ip_hdr.ip_dst.s_addr != iface->addr.s_addr)) {
+ log_debug("recv_packet: interface %s, invalid"
+ " destination IP address %s", iface->name,
+ inet_ntoa(ip_hdr.ip_dst));
+ break;
+ }
+
+ switch (dvmrp_hdr->code) {
+ case DVMRP_CODE_PROBE:
+ recv_probe(iface, ip_hdr.ip_src, ip_hdr.ip_src.s_addr,
+ dvmrp_hdr->capabilities, buf, len);
+ break;
+ case DVMRP_CODE_REPORT:
+ recv_report(nbr, buf, len);
+ break;
+ case DVMRP_CODE_ASK_NBRS2:
+ recv_ask_nbrs2(nbr, buf,len);
+ break;
+ case DVMRP_CODE_NBRS2:
+ recv_nbrs2(nbr, buf,len);
+ break;
+ case DVMRP_CODE_PRUNE:
+ recv_prune(nbr, buf, len);
+ break;
+ case DVMRP_CODE_GRAFT:
+ recv_graft(nbr, buf,len);
+ break;
+ case DVMRP_CODE_GRAFT_ACK:
+ recv_graft_ack(nbr, buf,len);
+ break;
+ default:
+ log_debug("recv_packet: unknown DVMRP packet type, "
+ "interface %s", iface->name);
+ }
+ break;
+ /* IGMP */
+ case PKT_TYPE_MEMBER_QUERY:
+ recv_igmp_query(iface, ip_hdr.ip_src, buf, len);
+ break;
+ case PKT_TYPE_MEMBER_REPORTv1:
+ case PKT_TYPE_MEMBER_REPORTv2:
+ recv_igmp_report(iface, ip_hdr.ip_src, buf, len,
+ dvmrp_hdr->type);
+ break;
+ case PKT_TYPE_LEAVE_GROUPv2:
+ recv_igmp_leave(iface, ip_hdr.ip_src, buf, len);
+ break;
+ default:
+ log_debug("recv_packet: unknown IGMP packet type, interface %s",
+ iface->name);
+ }
+}
+
+int
+ip_hdr_sanity_check(const struct ip *ip_hdr, u_int16_t len)
+{
+ if (ntohs(ip_hdr->ip_len) != len) {
+ log_debug("recv_packet: invalid IP packet length %u",
+ ntohs(ip_hdr->ip_len));
+ return (-1);
+ }
+
+ if (ip_hdr->ip_p != IPPROTO_IGMP)
+ /* this is enforced by the socket itself */
+ fatalx("recv_packet: invalid IP proto");
+
+ return (ip_hdr->ip_hl << 2);
+}
+
+int
+dvmrp_hdr_sanity_check(const struct ip *ip_hdr, struct dvmrp_hdr *dvmrp_hdr,
+ u_int16_t len, const struct iface *iface)
+{
+ /* we only support DVMRPv3 */
+ if (dvmrp_hdr->major_version != DVMRP_MAJOR_VERSION) {
+ log_debug("recv_packet: invalid DVMRP version");
+ return (-1);
+ }
+
+ /* XXX enforce minor version as well, but not yet */
+
+ /* XXX chksum */
+
+ return (len);
+}
+
+struct iface *
+find_iface(struct dvmrpd_conf *xconf, struct in_addr src)
+{
+ struct iface *iface = NULL;
+
+ /* returned interface needs to be active */
+ LIST_FOREACH(iface, &xconf->iface_list, entry) {
+ if (iface->fd > 0 &&
+ (iface->type == IF_TYPE_POINTOPOINT) &&
+ (iface->dst.s_addr == src.s_addr) &&
+ !iface->passive)
+ return (iface);
+
+ if (iface->fd > 0 && (iface->addr.s_addr &
+ iface->mask.s_addr) == (src.s_addr &
+ iface->mask.s_addr) && !iface->passive)
+ return (iface);
+ }
+
+ return (NULL);
+}
diff --git a/usr.sbin/dvmrpd/parse.y b/usr.sbin/dvmrpd/parse.y
new file mode 100644
index 00000000000..faa778d80c6
--- /dev/null
+++ b/usr.sbin/dvmrpd/parse.y
@@ -0,0 +1,786 @@
+/* $OpenBSD: parse.y,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "igmp.h"
+#include "dvmrp.h"
+#include "dvmrpd.h"
+#include "dvmrpe.h"
+#include "log.h"
+
+static struct dvmrpd_conf *conf;
+static FILE *fin = NULL;
+static int lineno = 1;
+static int errors = 0;
+static int pdebug = 1;
+char *infile;
+char *start_state;
+
+struct iface *iface = NULL;
+
+int yyerror(const char *, ...);
+int yyparse(void);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(FILE *);
+int lungetc(int);
+int findeol(void);
+int yylex(void);
+void clear_config(struct dvmrpd_conf *xconf);
+int check_file_secrecy(int fd, const char *fname);
+
+static struct {
+ u_int32_t probe_interval;
+ u_int32_t query_interval;
+ u_int32_t query_resp_interval;
+ u_int32_t startup_query_interval;
+ u_int32_t startup_query_cnt;
+ u_int32_t last_member_query_interval;
+ u_int32_t last_member_query_cnt;
+ u_int32_t dead_interval;
+ u_int16_t metric;
+ u_int8_t robustness;
+ u_int8_t igmp_version;
+} *defs, *grdefs, globaldefs, groupdefs, ifacedefs;
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entries;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+
+int symset(const char *, const char *, int);
+char *symget(const char *);
+int atoul(char *, u_long *);
+struct iface *conf_get_if(struct kif *);
+struct iface *new_group(void);
+
+typedef struct {
+ union {
+ u_int32_t number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token INTERFACE FIBUPDATE
+%token GROUP
+%token METRIC PASSIVE
+%token ROBUSTNESS QUERYINTERVAL QUERYRESPINTERVAL
+%token STARTUPQUERYINTERVAL STARTUPQUERYCNT
+%token LASTMEMBERQUERYINTERVAL LASTMEMBERQUERYCNT
+%token IGMPVERSION
+%token ERROR
+%token <v.string> STRING
+%type <v.number> number yesno
+%type <v.string> string
+
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar conf_main '\n'
+ | grammar varset '\n'
+ | grammar interface '\n'
+ | grammar group '\n'
+ | grammar error '\n' { errors++; }
+ ;
+
+number : STRING {
+ u_long ulval;
+
+ if (atoul($1, &ulval) == -1) {
+ yyerror("%s is not a number", $1);
+ free($1);
+ YYERROR;
+ } else
+ $$ = ulval;
+ free($1);
+ }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+ free($1);
+ free($2);
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+yesno : STRING {
+ if (!strcmp($1, "yes"))
+ $$ = 1;
+ else if (!strcmp($1, "no"))
+ $$ = 0;
+ else {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+varset : STRING '=' string {
+ if (conf->opts & DVMRPD_OPT_VERBOSE)
+ printf("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+conf_main : FIBUPDATE yesno {
+ if ($2 == 0)
+ conf->flags |= DVMRPD_FLAG_NO_FIB_UPDATE;
+ else
+ conf->flags &= ~DVMRPD_FLAG_NO_FIB_UPDATE;
+ }
+ | defaults
+ ;
+
+defaults : LASTMEMBERQUERYCNT number {
+ if ($2 < MIN_LAST_MEMBER_QUERY_CNT ||
+ $2 > MAX_LAST_MEMBER_QUERY_CNT) {
+ yyerror("last-member-query-count out of "
+ "range (%d-%d)",
+ MIN_LAST_MEMBER_QUERY_CNT,
+ MAX_LAST_MEMBER_QUERY_CNT);
+ YYERROR;
+ }
+ defs->last_member_query_cnt = $2;
+ }
+ | LASTMEMBERQUERYINTERVAL number {
+ if ($2 < MIN_LAST_MEMBER_QUERY_INTERVAL ||
+ $2 > MAX_LAST_MEMBER_QUERY_INTERVAL) {
+ yyerror("last-member-query-interval out of "
+ "range (%d-%d)",
+ MIN_LAST_MEMBER_QUERY_INTERVAL,
+ MAX_LAST_MEMBER_QUERY_INTERVAL);
+ YYERROR;
+ }
+ defs->last_member_query_interval = $2;
+ }
+ | METRIC number {
+ if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
+ yyerror("metric out of range (%d-%d)",
+ MIN_METRIC, MAX_METRIC);
+ YYERROR;
+ }
+ defs->metric = $2;
+ }
+ | QUERYINTERVAL number {
+ if ($2 < MIN_QUERY_INTERVAL ||
+ $2 > MAX_QUERY_INTERVAL) {
+ yyerror("query-interval out of range (%d-%d)",
+ MIN_QUERY_INTERVAL, MAX_QUERY_INTERVAL);
+ YYERROR;
+ }
+ defs->query_interval = $2;
+ }
+ | QUERYRESPINTERVAL number {
+ if ($2 < MIN_QUERY_RESP_INTERVAL ||
+ $2 > MAX_QUERY_RESP_INTERVAL) {
+ yyerror("query-response-interval out of "
+ "range (%d-%d)",
+ MIN_QUERY_RESP_INTERVAL,
+ MAX_QUERY_RESP_INTERVAL);
+ YYERROR;
+ }
+ defs->query_resp_interval = $2;
+ }
+ | ROBUSTNESS number {
+ if ($2 < MIN_ROBUSTNESS || $2 > MAX_ROBUSTNESS) {
+ yyerror("robustness out of range (%d-%d)",
+ MIN_ROBUSTNESS, MAX_ROBUSTNESS);
+ YYERROR;
+ }
+ defs->robustness = $2;
+ }
+ | STARTUPQUERYCNT number {
+ if ($2 < MIN_STARTUP_QUERY_CNT ||
+ $2 > MAX_STARTUP_QUERY_CNT) {
+ yyerror("startup-query-count out of "
+ "range (%d-%d)",
+ MIN_STARTUP_QUERY_CNT,
+ MAX_STARTUP_QUERY_CNT);
+ YYERROR;
+ }
+ defs->startup_query_cnt = $2;
+ }
+ | STARTUPQUERYINTERVAL number {
+ if ($2 < MIN_STARTUP_QUERY_INTERVAL ||
+ $2 > MAX_STARTUP_QUERY_INTERVAL) {
+ yyerror("startup-query-interval out of "
+ "range (%d-%d)",
+ MIN_STARTUP_QUERY_INTERVAL,
+ MAX_STARTUP_QUERY_INTERVAL);
+ YYERROR;
+ }
+ defs->startup_query_interval = $2;
+ }
+ | IGMPVERSION number {
+ if ($2 < MIN_IGMP_VERSION ||
+ $2 > MAX_IGMP_VERSION) {
+ yyerror("igmp-version out of range (%d-%d)",
+ MIN_IGMP_VERSION, MAX_IGMP_VERSION);
+ YYERROR;
+ }
+ defs->igmp_version = $2;
+ }
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl /* one newline or more */
+ ;
+
+interface : INTERFACE STRING {
+ struct kif *kif;
+
+ if ((kif = kif_findname($2)) == NULL) {
+ yyerror("unknown interface %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ iface = conf_get_if(kif);
+ if (iface == NULL)
+ YYERROR;
+ LIST_INSERT_HEAD(&conf->iface_list, iface, entry);
+
+ memcpy(&ifacedefs, defs, sizeof(ifacedefs));
+ defs = &ifacedefs;
+ } interface_block {
+ iface->probe_interval = defs->probe_interval;
+ iface->query_interval = defs->query_interval;
+ iface->query_resp_interval = defs->query_resp_interval;
+ iface->startup_query_interval =
+ defs->startup_query_interval;
+ iface->startup_query_cnt = defs->startup_query_cnt;
+ iface->last_member_query_interval =
+ defs->last_member_query_interval;
+ iface->last_member_query_cnt =
+ defs->last_member_query_cnt;
+ iface->dead_interval = defs->dead_interval;
+ iface->metric = defs->metric;
+ iface->robustness = defs->robustness;
+ iface->igmp_version = defs->igmp_version;
+ if (grdefs)
+ defs = grdefs;
+ else
+ defs = &globaldefs;
+ iface = NULL;
+ }
+ ;
+
+interface_block : '{' optnl interfaceopts_l '}'
+ | '{' optnl '}'
+ |
+ ;
+
+interfaceopts_l : interfaceopts_l interfaceoptsl
+ | interfaceoptsl
+ ;
+
+interfaceoptsl : PASSIVE nl { iface->passive = 1; }
+ | defaults nl
+ ;
+
+group : GROUP optnl '{' optnl {
+ memcpy(&groupdefs, defs, sizeof(groupdefs));
+ grdefs = defs = &groupdefs;
+ }
+ groupopts_l '}' {
+ grdefs = NULL;
+ defs = &globaldefs;
+ }
+ ;
+
+groupopts_l : groupopts_l groupoptsl
+ | groupoptsl
+ ;
+
+groupoptsl : interface nl
+ | defaults nl
+ | error nl
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+
+ errors = 1;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", infile, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ {"fib-update", FIBUPDATE},
+ {"group", GROUP},
+ {"igmp-version", IGMPVERSION},
+ {"interface", INTERFACE},
+ {"last-member-query-count", LASTMEMBERQUERYCNT},
+ {"last-member-query-interval", LASTMEMBERQUERYINTERVAL},
+ {"metric", METRIC},
+ {"passive", PASSIVE},
+ {"query-interval", QUERYINTERVAL},
+ {"query-response-interval", QUERYRESPINTERVAL},
+ {"robustness", ROBUSTNESS},
+ {"startup-query-count", STARTUPQUERYCNT},
+ {"startup-query-interval", STARTUPQUERYINTERVAL}
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p) {
+ if (pdebug > 1)
+ fprintf(stderr, "%s: %d\n", s, p->k_val);
+ return (p->k_val);
+ } else {
+ if (pdebug > 1)
+ fprintf(stderr, "string: %s\n", s);
+ return (STRING);
+ }
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(FILE *f)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ while ((c = getc(f)) == '\\') {
+ next = getc(f);
+ if (next != '\n') {
+ if (isspace(next))
+ yyerror("whitespace after \\");
+ ungetc(next, f);
+ break;
+ }
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == '\t' || c == ' ') {
+ /* Compress blanks to a single space. */
+ do {
+ c = getc(f);
+ } while (c == '\t' || c == ' ');
+ ungetc(c, f);
+ c = ' ';
+ }
+
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(fin);
+ if (c == '\n') {
+ lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int endc, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(fin)) == ' ')
+ ; /* nothing */
+
+ yylval.lineno = lineno;
+ if (c == '#')
+ while ((c = lgetc(fin)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ endc = c;
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+ if (c == endc) {
+ *p = '\0';
+ break;
+ }
+ if (c == '\n') {
+ lineno++;
+ continue;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ errx(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+struct dvmrpd_conf *
+parse_config(char *filename, int opts)
+{
+ struct sym *sym, *next;
+ struct timeval now;
+
+ if ((conf = calloc(1, sizeof(struct dvmrpd_conf))) == NULL) {
+ errx(1, "parse_config calloc");
+ return (NULL);
+ }
+
+ defs = &globaldefs;
+ defs->probe_interval = DEFAULT_PROBE_INTERVAL;
+ defs->last_member_query_cnt = DEFAULT_LAST_MEMBER_QUERY_CNT;
+ defs->last_member_query_interval = DEFAULT_LAST_MEMBER_QUERY_INTERVAL;
+ defs->metric = DEFAULT_METRIC;
+ defs->query_interval = DEFAULT_QUERY_INTERVAL;
+ defs->query_resp_interval = DEFAULT_QUERY_RESP_INTERVAL;
+ defs->robustness = DEFAULT_ROBUSTNESS;
+ defs->startup_query_cnt = DEFAULT_STARTUP_QUERY_CNT;
+ defs->startup_query_interval = DEFAULT_STARTUP_QUERY_INTERVAL;
+ defs->igmp_version = DEFAULT_IGMP_VERSION;
+ defs->dead_interval = NBR_TMOUT;
+
+ if ((fin = fopen(filename, "r")) == NULL) {
+ warn("%s", filename);
+ free(conf);
+ return (NULL);
+ }
+ infile = filename;
+
+ /* Generation ID must be non decreasing */
+ gettimeofday(&now, NULL);
+ conf->gen_id = now.tv_sec;
+ conf->opts = opts;
+
+ if (check_file_secrecy(fileno(fin), filename)) {
+ fclose(fin);
+ free(conf);
+ return (NULL);
+ }
+
+ yyparse();
+
+ fclose(fin);
+
+ /* Free macros and check which have not been used. */
+ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+ next = TAILQ_NEXT(sym, entries);
+ if ((conf->opts & DVMRPD_OPT_VERBOSE2) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not "
+ "used\n", sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
+ }
+ }
+
+ if (errors) {
+ clear_config(conf);
+ return (NULL);
+ }
+
+ return (conf);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entries))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entries);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ errx(1, "cmdline_symset: malloc");
+
+ strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entries)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+int
+atoul(char *s, u_long *ulvalp)
+{
+ u_long ulval;
+ char *ep;
+
+ errno = 0;
+ ulval = strtoul(s, &ep, 0);
+ if (s[0] == '\0' || *ep != '\0')
+ return (-1);
+ if (errno == ERANGE && ulval == ULONG_MAX)
+ return (-1);
+ *ulvalp = ulval;
+ return (0);
+}
+
+struct iface *
+conf_get_if(struct kif *kif)
+{
+ struct iface *i;
+
+ LIST_FOREACH(i, &conf->iface_list, entry)
+ if (i->ifindex == kif->ifindex) {
+ yyerror("interface %s already configured",
+ kif->ifname);
+ return (NULL);
+ }
+
+ i = if_new(kif);
+ i->passive = 0;
+ i->recv_query_resp_interval = DEFAULT_QUERY_RESP_INTERVAL;
+
+ return (i);
+}
+
+void
+clear_config(struct dvmrpd_conf *xconf)
+{
+ /* XXX clear conf */
+ /* ... */
+}
diff --git a/usr.sbin/dvmrpd/printconf.c b/usr.sbin/dvmrpd/printconf.c
new file mode 100644
index 00000000000..bfc4bd56c07
--- /dev/null
+++ b/usr.sbin/dvmrpd/printconf.c
@@ -0,0 +1,79 @@
+/* $OpenBSD: printconf.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+
+#include "igmp.h"
+#include "dvmrp.h"
+#include "dvmrpd.h"
+#include "dvmrpe.h"
+
+void print_mainconf(struct dvmrpd_conf *);
+void print_iface(struct iface *);
+
+void
+print_mainconf(struct dvmrpd_conf *conf)
+{
+ if (conf->flags |= DVMRPD_FLAG_NO_FIB_UPDATE)
+ printf("fib-update yes\n");
+ else
+ printf("fib-update no\n");
+}
+
+void
+print_iface(struct iface *iface)
+{
+ printf("interface %s {\n", iface->name);
+
+ if (iface->passive)
+ printf("\tpassive\n");
+
+ printf("\tmetric %d\n", iface->metric);
+ printf("\tquery-interval %d\n", iface->query_interval);
+ printf("\tquery-response-interval %d\n", iface->query_resp_interval);
+ printf("\trobustness %d\n", iface->robustness);
+ printf("\tstartup-query-count %d\n", iface->startup_query_cnt);
+ printf("\tlast-member-query-count %d\n", iface->last_member_query_cnt);
+ printf("\tlast-member-query-interval %d\n",
+ iface->last_member_query_interval);
+ printf("\tigmp-version %d\n", iface->igmp_version);
+
+ printf("}\n");
+}
+
+void
+print_config(struct dvmrpd_conf *conf)
+{
+ struct iface *iface;
+
+ printf("\n");
+ print_mainconf(conf);
+ printf("\n");
+
+ LIST_FOREACH(iface, &conf->iface_list, entry) {
+ print_iface(iface);
+ }
+ printf("\n");
+
+}
diff --git a/usr.sbin/dvmrpd/probe.c b/usr.sbin/dvmrpd/probe.c
new file mode 100644
index 00000000000..93e3dccb607
--- /dev/null
+++ b/usr.sbin/dvmrpd/probe.c
@@ -0,0 +1,139 @@
+/* $OpenBSD: probe.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <event.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+extern struct dvmrpd_conf *deconf;
+
+/* DVMRP probe packet handling */
+int
+send_probe(struct iface *iface)
+{
+ struct sockaddr_in dst;
+ struct buf *buf;
+ struct dvmrp_hdr *dvmrp_hdr;
+ struct nbr *nbr;
+ int ret = 0;
+
+ if (iface->passive)
+ return (0);
+
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_probe");
+
+ /* DVMRP header */
+ if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_PROBE))
+ goto fail;
+
+ /* generation ID */
+ buf_add(buf, &iface->gen_id, sizeof(iface->gen_id));
+
+ /* generate neighbor list */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->state > NBR_STA_DOWN)
+ buf_add(buf, &nbr->id, sizeof(nbr->id));
+ }
+
+ /* set destination address */
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ inet_aton(AllDVMRPRouters, &dst.sin_addr);
+
+ /* update chksum */
+ dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr));
+ dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_probe");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_probe(struct iface *iface, struct in_addr src, u_int32_t src_ip,
+ u_int8_t capabilities, char *buf, u_int16_t len)
+{
+ struct nbr *nbr = NULL;
+ u_int32_t gen_id;
+ u_int32_t nbr_id;
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->id.s_addr == src_ip)
+ break;
+ }
+
+ memcpy(&gen_id, buf, sizeof(gen_id));
+ len -= sizeof(gen_id);
+ buf += sizeof(gen_id);
+
+ if (!nbr) {
+ nbr = nbr_new(src_ip, iface, 0);
+ nbr->gen_id = gen_id;
+ nbr->capabilities = capabilities;
+ nbr->addr = src;
+ }
+
+ nbr_fsm(nbr, NBR_EVT_PROBE_RCVD);
+
+ if ((nbr->gen_id != gen_id) || (nbr->capabilities != capabilities)) {
+ if (!nbr->compat)
+ nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD);
+ nbr->gen_id = gen_id;
+ nbr->capabilities = capabilities;
+
+ /* XXX handle nbr change! */
+ }
+
+ while (len >= sizeof(nbr_id)) {
+ memcpy(&nbr_id, buf, sizeof(nbr_id));
+ if (nbr_id == iface->addr.s_addr) {
+ /* seen myself */
+ if (nbr->state < NBR_STA_2_WAY)
+ nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
+ break;
+ }
+ buf += sizeof(nbr_id);
+ len -= sizeof(nbr_id);
+ }
+
+ if (len == 0) {
+ nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD);
+ return;
+ }
+
+ /* XXX len correct?? */
+
+ return;
+}
diff --git a/usr.sbin/dvmrpd/prune.c b/usr.sbin/dvmrpd/prune.c
new file mode 100644
index 00000000000..697603432f5
--- /dev/null
+++ b/usr.sbin/dvmrpd/prune.c
@@ -0,0 +1,80 @@
+/* $OpenBSD: prune.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <strings.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+/* DVMRP prune packet handling */
+int
+send_prune(struct nbr *nbr, void *data, int len)
+{
+ struct sockaddr_in dst;
+ struct buf *buf;
+ struct dvmrp_hdr *dvmrp_hdr;
+ int ret = 0;
+
+ log_debug("send_prune: interface %s nbr %s", nbr->iface->name,
+ inet_ntoa(nbr->addr));
+
+ if (nbr->iface->passive)
+ return (0);
+
+ if ((buf = buf_open(nbr->iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_prune");
+
+ /* DVMRP header */
+ if (gen_dvmrp_hdr(buf, nbr->iface, DVMRP_CODE_PRUNE))
+ goto fail;
+
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr = nbr->addr;
+
+ /* update chksum */
+ dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr));
+ dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);
+
+ ret = send_packet(nbr->iface, buf->buf, buf->wpos, &dst);
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_prune");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_prune(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ log_debug("recv_prune: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return;
+}
diff --git a/usr.sbin/dvmrpd/rde.c b/usr.sbin/dvmrpd/rde.c
new file mode 100644
index 00000000000..ad359830b34
--- /dev/null
+++ b/usr.sbin/dvmrpd/rde.c
@@ -0,0 +1,281 @@
+/* $OpenBSD: rde.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <event.h>
+
+#include "igmp.h"
+#include "dvmrp.h"
+#include "dvmrpd.h"
+#include "dvmrpe.h"
+#include "log.h"
+#include "rde.h"
+
+void rde_sig_handler(int sig, short, void *);
+void rde_shutdown(void);
+void rde_dispatch_imsg(int, short, void *);
+
+volatile sig_atomic_t rde_quit = 0;
+struct dvmrpd_conf *rdeconf = NULL;
+struct imsgbuf *ibuf_dvmrpe;
+struct imsgbuf *ibuf_main;
+
+void
+rde_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * signal handler rules don't apply, libevent decouples for us
+ */
+
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ rde_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+/* route decision engine */
+pid_t
+rde(struct dvmrpd_conf *xconf, int pipe_parent2rde[2], int pipe_dvmrpe2rde[2],
+ int pipe_parent2dvmrpe[2])
+{
+ struct passwd *pw;
+ struct event ev_sigint, ev_sigterm;
+ pid_t pid;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ /* NOTREACHED */
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ rdeconf = xconf;
+
+ if ((pw = getpwnam(DVMRPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("route decision engine");
+ dvmrpd_process = PROC_RDE_ENGINE;
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
+ seteuid(pw->pw_uid) || setuid(pw->pw_uid)) {
+ fatal("can't drop privileges");
+ }
+
+ endpwent();
+
+ event_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, rde_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, rde_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+
+ /* setup pipes */
+ close(pipe_dvmrpe2rde[0]);
+ close(pipe_parent2rde[0]);
+ close(pipe_parent2dvmrpe[0]);
+ close(pipe_parent2dvmrpe[1]);
+
+ if ((ibuf_dvmrpe = malloc(sizeof(struct imsgbuf))) == NULL ||
+ (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_dvmrpe, pipe_dvmrpe2rde[1], rde_dispatch_imsg);
+ imsg_init(ibuf_main, pipe_parent2rde[1], rde_dispatch_imsg);
+
+ /* setup event handler */
+ ibuf_dvmrpe->events = EV_READ;
+ event_set(&ibuf_dvmrpe->ev, ibuf_dvmrpe->fd, ibuf_dvmrpe->events,
+ ibuf_dvmrpe->handler, ibuf_dvmrpe);
+ event_add(&ibuf_dvmrpe->ev, NULL);
+
+ ibuf_main->events = EV_READ;
+ event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events,
+ ibuf_main->handler, ibuf_main);
+ event_add(&ibuf_main->ev, NULL);
+
+ rt_init();
+ mfc_init();
+
+ event_dispatch();
+
+ rde_shutdown();
+ /* NOTREACHED */
+
+ return (0);
+}
+
+void
+rde_shutdown(void)
+{
+ struct iface *iface;
+
+ rt_clear();
+ mfc_clear();
+
+ LIST_FOREACH(iface, &rdeconf->iface_list, entry) {
+ if_del(iface);
+ }
+
+ msgbuf_clear(&ibuf_dvmrpe->w);
+ free(ibuf_dvmrpe);
+ msgbuf_clear(&ibuf_main->w);
+ free(ibuf_main);
+ free(rdeconf);
+
+ log_info("route decision engine exiting");
+ _exit(0);
+}
+
+/* imesg */
+int
+rde_imsg_compose_parent(int type, pid_t pid, void *data, u_int16_t datalen)
+{
+ return (imsg_compose(ibuf_main, type, 0, pid, data, datalen));
+}
+
+int
+rde_imsg_compose_dvmrpe(int type, u_int32_t peerid, pid_t pid, void *data,
+ u_int16_t datalen)
+{
+ return (imsg_compose(ibuf_dvmrpe, type, peerid, pid, data, datalen));
+}
+
+void
+rde_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct mfc mfc;
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ struct route_report rr;
+ int n, connected = 0;
+ int i;
+ struct iface *iface;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("rde_dispatch_imsg: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_SHOW_RIB:
+ rt_dump(imsg.hdr.pid);
+ imsg_compose(ibuf_dvmrpe, IMSG_CTL_END, 0, imsg.hdr.pid,
+ NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_MFC:
+ mfc_dump(imsg.hdr.pid);
+ imsg_compose(ibuf_dvmrpe, IMSG_CTL_END, 0, imsg.hdr.pid,
+ NULL, 0);
+ break;
+ case IMSG_ROUTE_REPORT:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr))
+ fatalx("invalid size of OE request");
+ memcpy(&rr, imsg.data, sizeof(rr));
+
+ /* directly connected networks from parent */
+ if (imsg.hdr.peerid == 0) {
+ connected = 1;
+ }
+ rt_update(rr.net, mask2prefixlen(rr.mask.s_addr),
+ rr.nexthop, rr.metric, rr.adv_rtr, rr.ifindex, 0,
+ connected);
+ break;
+ case IMSG_FULL_ROUTE_REPORT:
+ rt_snap(imsg.hdr.peerid);
+ rde_imsg_compose_dvmrpe(IMSG_FULL_ROUTE_REPORT_END,
+ imsg.hdr.peerid, 0, NULL, 0);
+ break;
+ case IMSG_MFC_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc))
+ fatalx("invalid size of OE request");
+ memcpy(&mfc, imsg.data, sizeof(mfc));
+#if 1
+ for (i = 0; i < MAXVIFS; i++)
+ mfc.ttls[i] = 0;
+
+ LIST_FOREACH(iface, &rdeconf->iface_list, entry) {
+ if (mfc.ifindex != iface->ifindex)
+ mfc.ttls[iface->ifindex] = 1;
+ }
+
+ mfc_update(&mfc);
+#endif
+ break;
+ case IMSG_MFC_DEL:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc))
+ fatalx("invalid size of OE request");
+ memcpy(&mfc, imsg.data, sizeof(mfc));
+#if 1
+ mfc_delete(&mfc);
+#endif
+ break;
+ default:
+ log_debug("rde_dispatch_msg: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
diff --git a/usr.sbin/dvmrpd/rde.h b/usr.sbin/dvmrpd/rde.h
new file mode 100644
index 00000000000..8483a05d0cd
--- /dev/null
+++ b/usr.sbin/dvmrpd/rde.h
@@ -0,0 +1,82 @@
+/* $OpenBSD: rde.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _RDE_H_
+#define _RDE_H_
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+#include <sys/queue.h>
+#include <event.h>
+#include <limits.h>
+
+struct rt_node {
+ RB_ENTRY(rt_node) entry;
+ struct event expiration_timer;
+ struct in_addr prefix;
+ struct in_addr nexthop;
+ struct in_addr adv_rtr;
+ u_int32_t cost;
+ u_short ifindex; /* learned from this iface */
+ time_t uptime;
+ u_int8_t flags;
+ u_int8_t prefixlen;
+ u_int8_t invalid;
+ u_int8_t connected;
+};
+
+struct mfc_node {
+ RB_ENTRY(mfc_node) entry;
+ struct event expiration_timer;
+ u_int8_t ttls[MAXVIFS]; /* outgoing vif(s) */
+ struct in_addr origin;
+ struct in_addr group;
+ u_short ifindex; /* incoming vif */
+ time_t uptime;
+};
+
+/* rde.c */
+pid_t rde(struct dvmrpd_conf *, int [2], int [2], int [2]);
+int rde_imsg_compose_parent(int, pid_t, void *, u_int16_t);
+int rde_imsg_compose_dvmrpe(int, u_int32_t, pid_t, void *, u_int16_t);
+
+/* rde_mfc.c */
+void mfc_init(void);
+int mfc_compare(struct mfc_node *, struct mfc_node *);
+struct mfc_node *mfc_find(in_addr_t, in_addr_t);
+int mfc_insert(struct mfc_node *);
+int mfc_remove(struct mfc_node *);
+void mfc_clear(void);
+void mfc_dump(pid_t);
+void mfc_update(struct mfc *);
+void mfc_delete(struct mfc *);
+
+/* rde_srt.c */
+void rt_init(void);
+int rt_compare(struct rt_node *, struct rt_node *);
+struct rt_node *rt_find(in_addr_t, u_int8_t);
+int rt_insert(struct rt_node *);
+int rt_remove(struct rt_node *);
+void rt_clear(void);
+void rt_snap(u_int32_t);
+void rt_dump(pid_t);
+void rt_update(struct in_addr, u_int8_t, struct in_addr,
+ u_int32_t, struct in_addr, u_short, u_int8_t, u_int8_t);
+
+#endif /* _RDE_H_ */
diff --git a/usr.sbin/dvmrpd/rde_mfc.c b/usr.sbin/dvmrpd/rde_mfc.c
new file mode 100644
index 00000000000..b985360fa56
--- /dev/null
+++ b/usr.sbin/dvmrpd/rde_mfc.c
@@ -0,0 +1,218 @@
+/* $OpenBSD: rde_mfc.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <stdlib.h>
+
+#include "igmp.h"
+#include "dvmrp.h"
+#include "dvmrpd.h"
+#include "rde.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+/* multicast forwarding cache */
+
+void mfc_invalidate(void);
+void mfc_expire_timer(int, short, void *);
+int mfc_start_expire_timer(struct mfc_node *);
+int mfc_compare(struct mfc_node *, struct mfc_node *);
+
+RB_HEAD(mfc_tree, mfc_node) mfc;
+RB_PROTOTYPE(mfc_tree, mfc_node, entry, mfc_compare)
+RB_GENERATE(mfc_tree, mfc_node, entry, mfc_compare)
+
+extern struct dvmrpd_conf *rdeconf;
+
+/* timers */
+void
+mfc_expire_timer(int fd, short event, void *arg)
+{
+ struct mfc_node *mn = arg;
+ struct mfc nmfc;
+
+ log_debug("mfc_expire_timer: group %s", inet_ntoa(mn->group));
+
+ /* remove route entry */
+ nmfc.origin = mn->origin;
+ nmfc.group = mn->group;
+ rde_imsg_compose_parent(IMSG_MFC_DEL, 0, &nmfc, sizeof(nmfc));
+
+ event_del(&mn->expiration_timer);
+ mfc_remove(mn);
+}
+
+int
+mfc_start_expire_timer(struct mfc_node *mn)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = ROUTE_EXPIRATION_TIME;
+ return (evtimer_add(&mn->expiration_timer, &tv));
+}
+
+/* route table */
+void
+mfc_init(void)
+{
+ RB_INIT(&mfc);
+}
+
+int
+mfc_compare(struct mfc_node *a, struct mfc_node *b)
+{
+ if (ntohl(a->origin.s_addr) < ntohl(b->origin.s_addr))
+ return (-1);
+ if (ntohl(a->origin.s_addr) > ntohl(b->origin.s_addr))
+ return (1);
+ if (ntohl(a->group.s_addr) < ntohl(b->group.s_addr))
+ return (-1);
+ if (ntohl(a->group.s_addr) > ntohl(b->group.s_addr))
+ return (1);
+ return (0);
+}
+
+struct mfc_node *
+mfc_find(in_addr_t origin, in_addr_t group)
+{
+ struct mfc_node s;
+
+ s.origin.s_addr = origin;
+ s.group.s_addr = group;
+
+ return (RB_FIND(mfc_tree, &mfc, &s));
+}
+
+int
+mfc_insert(struct mfc_node *m)
+{
+ if (RB_INSERT(mfc_tree, &mfc, m) != NULL) {
+ log_warnx("mfc_insert failed for group %s",
+ inet_ntoa(m->group));
+ free(m);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+mfc_remove(struct mfc_node *m)
+{
+ if (RB_REMOVE(mfc_tree, &mfc, m) == NULL) {
+ log_warnx("mfc_remove failed for group %s",
+ inet_ntoa(m->group));
+ return (-1);
+ }
+
+ free(m);
+ return (0);
+}
+
+void
+mfc_clear(void)
+{
+ struct mfc_node *m;
+
+ while ((m = RB_MIN(mfc_tree, &mfc)) != NULL)
+ mfc_remove(m);
+}
+
+void
+mfc_dump(pid_t pid)
+{
+ static struct ctl_mfc mfcctl;
+ struct timespec now;
+ struct timeval tv, now2, res;
+ struct mfc_node *mn;
+ int i;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ RB_FOREACH(mn, mfc_tree, &mfc) {
+ mfcctl.origin.s_addr = mn->origin.s_addr;
+ mfcctl.group.s_addr = mn->group.s_addr;
+ mfcctl.uptime = now.tv_sec - mn->uptime;
+ mfcctl.ifindex = mn->ifindex;
+
+ for (i = 0; i < MAXVIFS; i ++) {
+ mfcctl.ttls[i] = mn->ttls[i];
+ }
+
+ gettimeofday(&now2, NULL);
+ if (evtimer_pending(&mn->expiration_timer, &tv)) {
+ timersub(&tv, &now2, &res);
+ mfcctl.expire = res.tv_sec;
+ } else
+ mfcctl.expire = -1;
+
+ rde_imsg_compose_dvmrpe(IMSG_CTL_SHOW_MFC, 0, pid, &mfcctl,
+ sizeof(mfcctl));
+ }
+}
+
+void
+mfc_update(struct mfc *nmfc)
+{
+ struct timespec now;
+ struct mfc_node *mn;
+ int i;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ if ((mn = mfc_find(nmfc->origin.s_addr, nmfc->group.s_addr)) == NULL) {
+ if ((mn = calloc(1, sizeof(struct mfc_node))) == NULL)
+ fatalx("mfc_update");
+
+ mn->origin.s_addr = nmfc->origin.s_addr;
+ mn->group.s_addr = nmfc->group.s_addr;
+ mn->ifindex = nmfc->ifindex;
+ mn->uptime = now.tv_sec;
+ for (i = 0; i < MAXVIFS; i++)
+ mn->ttls[i] = nmfc->ttls[i];
+
+ if (mfc_insert(mn) == 0) {
+ if (nmfc->origin.s_addr != 0)
+ rde_imsg_compose_parent(IMSG_MFC_ADD, 0, nmfc,
+ sizeof(*nmfc));
+ }
+
+ evtimer_set(&mn->expiration_timer, mfc_expire_timer, mn);
+ mfc_start_expire_timer(mn);
+ }
+}
+
+void
+mfc_delete(struct mfc *nmfc)
+{
+ struct mfc_node *mn;
+
+ if ((mn = mfc_find(nmfc->origin.s_addr, nmfc->group.s_addr)) == NULL)
+ return;
+
+ /* XXX decide if it should really be removed */
+ mfc_remove(mn);
+
+ /* XXX notify parent */
+}
diff --git a/usr.sbin/dvmrpd/rde_srt.c b/usr.sbin/dvmrpd/rde_srt.c
new file mode 100644
index 00000000000..bc288db30b5
--- /dev/null
+++ b/usr.sbin/dvmrpd/rde_srt.c
@@ -0,0 +1,246 @@
+/* $OpenBSD: rde_srt.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <stdlib.h>
+
+#include "igmp.h"
+#include "dvmrp.h"
+#include "dvmrpd.h"
+#include "rde.h"
+#include "log.h"
+#include "dvmrpe.h"
+
+/* source route tree */
+
+void rt_invalidate(void);
+void rt_expire_timer(int, short, void *);
+int rt_start_expire_timer(struct rt_node *);
+
+RB_HEAD(rt_tree, rt_node) rt;
+RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare)
+RB_GENERATE(rt_tree, rt_node, entry, rt_compare)
+
+extern struct dvmrpd_conf *rdeconf;
+
+/* timers */
+void
+rt_expire_timer(int fd, short event, void *arg)
+{
+ struct rt_node *rn = arg;
+
+ log_debug("rt_expire_timer: route %s/%d", inet_ntoa(rn->prefix),
+ rn->prefixlen);
+
+ /* XXX tell neighbors */
+
+ /* remove route entry */
+ event_del(&rn->expiration_timer);
+ rt_remove(rn);
+}
+
+int
+rt_start_expire_timer(struct rt_node *rn)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = ROUTE_EXPIRATION_TIME;
+ return (evtimer_add(&rn->expiration_timer, &tv));
+
+}
+
+/* route table */
+void
+rt_init(void)
+{
+ RB_INIT(&rt);
+}
+
+int
+rt_compare(struct rt_node *a, struct rt_node *b)
+{
+ /*
+ * sort route entries based on prefixlen since generating route
+ * reports rely on that.
+ */
+ if (a->prefixlen < b->prefixlen)
+ return (-1);
+ if (a->prefixlen > b->prefixlen)
+ return (1);
+ if (ntohl(a->prefix.s_addr) < ntohl(b->prefix.s_addr))
+ return (-1);
+ if (ntohl(a->prefix.s_addr) > ntohl(b->prefix.s_addr))
+ return (1);
+ return (0);
+}
+
+struct rt_node *
+rt_find(in_addr_t prefix, u_int8_t prefixlen)
+{
+ struct rt_node s;
+
+ s.prefix.s_addr = prefix;
+ s.prefixlen = prefixlen;
+
+ return (RB_FIND(rt_tree, &rt, &s));
+}
+
+int
+rt_insert(struct rt_node *r)
+{
+ if (RB_INSERT(rt_tree, &rt, r) != NULL) {
+ log_warnx("rt_insert failed for %s/%u", inet_ntoa(r->prefix),
+ r->prefixlen);
+ free(r);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+rt_remove(struct rt_node *r)
+{
+ if (RB_REMOVE(rt_tree, &rt, r) == NULL) {
+ log_warnx("rt_remove failed for %s/%u",
+ inet_ntoa(r->prefix), r->prefixlen);
+ return (-1);
+ }
+
+ free(r);
+ return (0);
+}
+
+void
+rt_invalidate(void)
+{
+ struct rt_node *r, *nr;
+
+ for (r = RB_MIN(rt_tree, &rt); r != NULL; r = nr) {
+ nr = RB_NEXT(rt_tree, &rt, r);
+ if (r->invalid)
+ rt_remove(r);
+ else
+ r->invalid = 1;
+ }
+}
+
+void
+rt_clear(void)
+{
+ struct rt_node *r;
+
+ while ((r = RB_MIN(rt_tree, &rt)) != NULL)
+ rt_remove(r);
+}
+
+void
+rt_snap(u_int32_t peerid)
+{
+ struct rt_node *r;
+ struct route_report rr;
+
+ RB_FOREACH(r, rt_tree, &rt) {
+ if (r->invalid)
+ continue;
+
+ rr.net = r->prefix;
+ rr.mask.s_addr = ntohl(prefixlen2mask(r->prefixlen));
+ rr.metric = r->cost;
+ rr.ifindex = r->ifindex;
+ rde_imsg_compose_dvmrpe(IMSG_FULL_ROUTE_REPORT, peerid, 0, &rr,
+ sizeof(rr));
+ }
+}
+
+void
+rt_dump(pid_t pid)
+{
+ static struct ctl_rt rtctl;
+ struct timespec now;
+ struct timeval tv, now2, res;
+ struct rt_node *r;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ RB_FOREACH(r, rt_tree, &rt) {
+ if (r->invalid)
+ continue;
+
+ rtctl.prefix.s_addr = r->prefix.s_addr;
+ rtctl.nexthop.s_addr = r->nexthop.s_addr;
+ rtctl.adv_rtr.s_addr = r->adv_rtr.s_addr;
+ rtctl.cost = r->cost;
+ rtctl.flags = r->flags;
+ rtctl.prefixlen = r->prefixlen;
+ rtctl.uptime = now.tv_sec - r->uptime;
+
+ gettimeofday(&now2, NULL);
+ if (evtimer_pending(&r->expiration_timer, &tv)) {
+ timersub(&tv, &now2, &res);
+ rtctl.expire = res.tv_sec;
+ } else
+ rtctl.expire = -1;
+
+ rde_imsg_compose_dvmrpe(IMSG_CTL_SHOW_RIB, 0, pid, &rtctl,
+ sizeof(rtctl));
+ }
+}
+
+void
+rt_update(struct in_addr prefix, u_int8_t prefixlen, struct in_addr nexthop,
+ u_int32_t cost, struct in_addr adv_rtr, u_short ifindex, u_int8_t flags,
+ u_int8_t connected)
+{
+ struct timespec now;
+ struct rt_node *rte;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ if ((rte = rt_find(prefix.s_addr, prefixlen)) == NULL) {
+ if ((rte = calloc(1, sizeof(struct rt_node))) == NULL)
+ fatalx("rt_update");
+ rte->prefix.s_addr = prefix.s_addr;
+ rte->prefixlen = prefixlen;
+ rte->nexthop.s_addr = nexthop.s_addr;
+ rte->adv_rtr.s_addr = adv_rtr.s_addr;
+ rte->cost = cost;
+ rte->ifindex = ifindex;
+ rte->flags = flags;
+ rte->invalid = 0;
+ rte->connected = connected;
+ rte->uptime = now.tv_sec;
+
+ rt_insert(rte);
+
+ evtimer_set(&rte->expiration_timer, rt_expire_timer, rte);
+
+ if (!rte->connected)
+ rt_start_expire_timer(rte);
+
+ } else {
+ if (!rte->connected)
+ rt_start_expire_timer(rte);
+ }
+}
diff --git a/usr.sbin/dvmrpd/report.c b/usr.sbin/dvmrpd/report.c
new file mode 100644
index 00000000000..6fae0421507
--- /dev/null
+++ b/usr.sbin/dvmrpd/report.c
@@ -0,0 +1,304 @@
+/* $OpenBSD: report.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <strings.h>
+
+#include "igmp.h"
+#include "dvmrpd.h"
+#include "dvmrp.h"
+#include "dvmrpe.h"
+#include "log.h"
+
+extern struct dvmrpd_conf *deconf;
+
+void rr_list_remove(struct rr_head *, struct route_report *);
+
+/* DVMRP report packet handling */
+int
+send_report(struct iface *iface, struct in_addr addr, void *data, int len)
+{
+ struct sockaddr_in dst;
+ struct buf *buf;
+ struct dvmrp_hdr *dvmrp_hdr;
+ int ret = 0;
+
+ log_debug("send_report: interface %s addr %s",
+ iface->name, inet_ntoa(addr));
+
+ if (iface->passive)
+ return (0);
+
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_report");
+
+ /* DVMRP header */
+ if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_REPORT))
+ goto fail;
+
+ buf_add(buf, data, len);
+
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr.s_addr = addr.s_addr;
+
+ /* update chksum */
+ dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr));
+ dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_report");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_report(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct route_report rr;
+ u_int32_t netid, netmask;
+ u_int8_t metric, netid_len, prefixlen;
+
+ log_debug("recv_report: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if ((nbr->state != NBR_STA_2_WAY) && (!nbr->compat)) {
+ log_warnx("recv_report: neighbor %s not in state %s",
+ inet_ntoa(nbr->id), "2-WAY");
+ return;
+ }
+
+ /* parse route report */
+ do {
+ /*
+ * get netmask
+ *
+ * The netmask in a DVMRP report is only represented by 3 bytes,
+ * to cope with that we read 4 bytes and shift 8 bits.
+ * The most significant part of the mask is always 255.
+ */
+
+ /* XXX handle special case 0.0.0.0/0 */
+
+ /* read four bytes */
+ memcpy(&netmask, buf, sizeof(netmask));
+ /* ditch one byte, since we only need three */
+ netmask = netmask << 8;
+ /* set the highest byte to 255 */
+ netmask = netmask | htonl(0xff000000);
+ buf += 3;
+ len -= 3;
+
+ netid_len = 0;
+ prefixlen = mask2prefixlen(netmask);
+ if (prefixlen > 0)
+ netid_len = 1;
+ if (prefixlen > 8)
+ netid_len = 2;
+ if (prefixlen > 16)
+ netid_len = 3;
+ if (prefixlen > 24)
+ netid_len = 4;
+
+ do {
+ /*
+ * get netid
+ *
+ * The length of the netid is depending on the above
+ * netmask.
+ * Read 4 bytes and use the netmask from above to
+ * determine the netid.
+ */
+ memcpy(&netid, buf, sizeof(netid));
+ netid = netid & netmask;
+ buf += netid_len;
+ len -= netid_len;
+
+ /* get metric */
+ memcpy(&metric, buf, sizeof(metric));
+ buf += sizeof(metric);
+ len -= sizeof(metric);
+
+ rr.net.s_addr = netid;
+ rr.mask.s_addr = netmask;
+ rr.nexthop = nbr->id;
+ /* adjusted metric */
+ rr.metric = (metric & METRIC_MASK) + nbr->iface->metric;
+
+ /* ifindex */
+ rr.ifindex = nbr->iface->ifindex;
+
+ /* send route report to RDE */
+ dvmrpe_imsg_compose_rde(IMSG_ROUTE_REPORT, nbr->peerid,
+ 0, &rr, sizeof(rr));
+
+ } while (!(metric & LAST_MASK) && (len > 0));
+ } while (len > 0);
+
+ return;
+}
+
+/* timers */
+void
+report_timer(int fd, short event, void *arg)
+{
+ struct timeval tv;
+
+ /* request full route report */
+ dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, 0, 0, NULL, 0);
+
+ /* restart report timer */
+ timerclear(&tv);
+ tv.tv_sec = ROUTE_REPORT_INTERVAL;
+ evtimer_add(&deconf->report_timer, &tv);
+}
+
+int
+start_report_timer(void)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = MIN_FLASH_UPDATE_INTERVAL; /* XXX safe?? */
+ return (evtimer_add(&deconf->report_timer, &tv));
+}
+
+int
+stop_report_timer(void)
+{
+ return (evtimer_del(&deconf->report_timer));
+}
+
+/* route report list */
+void
+rr_list_add(struct rr_head *rr_list, struct route_report *rr)
+{
+ struct rr_entry *le;
+
+ if (rr == NULL)
+ fatalx("rr_list_add: no route report");
+
+ if ((le = calloc(1, sizeof(*le))) == NULL)
+ fatal("rr_list_add");
+
+ TAILQ_INSERT_TAIL(rr_list, le, entry);
+ le->re = rr;
+}
+
+void
+rr_list_clr(struct rr_head *rr_list)
+{
+ struct rr_entry *le;
+
+ while ((le = TAILQ_FIRST(rr_list)) != NULL) {
+ TAILQ_REMOVE(rr_list, le, entry);
+ free(le->re);
+ free(le);
+ }
+}
+
+void
+rr_list_send(struct rr_head *rr_list, struct iface *xiface, struct nbr *nbr)
+{
+ struct rr_entry *le, *le2;
+ struct buf *buf;
+ struct iface *iface;
+ struct in_addr addr;
+ u_int32_t netid, netmask;
+ u_int8_t metric, netid_len, prefixlen;
+
+ /* set destination */
+ if (xiface == NULL) {
+ /* directly to a nbr */
+ iface = nbr->iface;
+ addr = nbr->addr;
+ } else {
+ /* multicast on interface */
+ iface = xiface;
+ inet_aton(AllDVMRPRouters, &addr);
+ }
+
+ while (!TAILQ_EMPTY(rr_list)) {
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("rr_list_send");
+
+ prefixlen = 0;
+ while (((le = TAILQ_FIRST(rr_list)) != NULL) &&
+ (buf->wpos < 1000)) {
+ /* netmask */
+ netmask = le->re->mask.s_addr;
+ if (prefixlen != mask2prefixlen(netmask)) {
+ prefixlen = mask2prefixlen(netmask);
+ netmask = netmask >> 8;
+ buf_add(buf, &netmask, 3);
+ }
+
+ netid_len = 0;
+ if (prefixlen > 0)
+ netid_len = 1;
+ if (prefixlen > 8)
+ netid_len = 2;
+ if (prefixlen > 16)
+ netid_len = 3;
+ if (prefixlen > 24)
+ netid_len = 4;
+
+ /* netid */
+ netid = le->re->net.s_addr;
+ buf_add(buf, &netid, netid_len);
+
+ /* metric */
+ if (iface->ifindex == le->re->ifindex)
+ /* poison reverse */
+ metric = le->re->metric + INFINITY_METRIC;
+ else
+ metric = le->re->metric;
+
+ /*
+ * determine if we need to flag last entry with current
+ * netmask.
+ */
+ le2 = TAILQ_NEXT(le, entry);
+ if (le2 != NULL) {
+ if (mask2prefixlen(le2->re->mask.s_addr) !=
+ prefixlen)
+ metric = metric | LAST_MASK;
+ } else {
+ metric = metric | LAST_MASK;
+ }
+
+ buf_add(buf, &metric, sizeof(metric));
+
+ /* XXX rr_list_remove */
+ TAILQ_REMOVE(rr_list, le, entry);
+ /* XXX free(le->re); */
+ free(le);
+ }
+ send_report(iface, addr, buf->buf, buf->wpos);
+ buf_free(buf);
+ }
+}