summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorEsben Norby <norby@cvs.openbsd.org>2006-10-18 16:11:59 +0000
committerEsben Norby <norby@cvs.openbsd.org>2006-10-18 16:11:59 +0000
commit9898323e8078c0a28e2404083e37bbb23637a390 (patch)
tree2fd1fe72c94b0d594d939719e44a82ce86dd6e10 /usr.sbin
parent489a63b5b7e02bf58b50268110b2421d48373394 (diff)
Welcome ripd
started by Michele Marchetto some time ago by using the imsg/three process framework of ospfd. He implemented most of the daemon with a little help and guidance from Claudio and I. Currently the daemon is more or less complete, with the exception of key lifetime and rollover. Not yet connected to the builds. OK claudio@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/ripd/Makefile18
-rw-r--r--usr.sbin/ripd/auth.c295
-rw-r--r--usr.sbin/ripd/buffer.c218
-rw-r--r--usr.sbin/ripd/control.c300
-rw-r--r--usr.sbin/ripd/control.h50
-rw-r--r--usr.sbin/ripd/imsg.c178
-rw-r--r--usr.sbin/ripd/interface.c482
-rw-r--r--usr.sbin/ripd/kroute.c1126
-rw-r--r--usr.sbin/ripd/log.c222
-rw-r--r--usr.sbin/ripd/log.h35
-rw-r--r--usr.sbin/ripd/neighbor.c357
-rw-r--r--usr.sbin/ripd/packet.c251
-rw-r--r--usr.sbin/ripd/parse.y763
-rw-r--r--usr.sbin/ripd/printconf.c106
-rw-r--r--usr.sbin/ripd/rde.c455
-rw-r--r--usr.sbin/ripd/rde.h64
-rw-r--r--usr.sbin/ripd/rde_rib.c258
-rw-r--r--usr.sbin/ripd/rip.h82
-rw-r--r--usr.sbin/ripd/ripd.881
-rw-r--r--usr.sbin/ripd/ripd.conf.5195
-rw-r--r--usr.sbin/ripd/ripd.h392
-rw-r--r--usr.sbin/ripd/ripe.c504
-rw-r--r--usr.sbin/ripd/ripe.h146
23 files changed, 6578 insertions, 0 deletions
diff --git a/usr.sbin/ripd/Makefile b/usr.sbin/ripd/Makefile
new file mode 100644
index 00000000000..261abf70b56
--- /dev/null
+++ b/usr.sbin/ripd/Makefile
@@ -0,0 +1,18 @@
+# $OpenBSD: Makefile,v 1.1 2006/10/18 16:11:58 norby Exp $
+
+PROG= ripd
+SRCS= auth.c buffer.c control.c imsg.c interface.c kroute.c \
+ log.c message.c neighbor.c packet.c parse.y printconf.c \
+ rde.c rde_rib.c ripe.c ripd.c
+
+MAN= ripd.8 ripd.conf.5
+
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+YFLAGS=
+LDADD+= -levent
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ripd/auth.c b/usr.sbin/ripd/auth.c
new file mode 100644
index 00000000000..ae3a20b5891
--- /dev/null
+++ b/usr.sbin/ripd/auth.c
@@ -0,0 +1,295 @@
+/* $OpenBSD: auth.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * Copyright (c) 2004, 2005 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 <limits.h>
+#include <md5.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ripd.h"
+#include "rip.h"
+#include "log.h"
+#include "ripe.h"
+
+struct auth_md *md_list_find(struct auth_md_head *, u_int8_t);
+void auth_trailer_header_gen(struct buf *);
+u_int32_t auth_get_seq_num(void);
+
+u_int32_t
+auth_get_seq_num()
+{
+ return time(NULL);
+}
+
+void
+auth_trailer_header_gen(struct buf *buf)
+{
+ u_int16_t field1 = 0xFFFF;
+ u_int16_t field2 = htons(0x01);
+
+ buf_add(buf, &field1, sizeof(field1));
+ buf_add(buf, &field2, sizeof(field2));
+}
+
+/* XXX add the support for key lifetime and rollover */
+int
+auth_validate(char **buf, u_int16_t *len, struct iface *iface, struct nbr *nbr,
+ struct nbr_failed *nbr_failed, u_int32_t *crypt_seq_num)
+{
+ MD5_CTX hash;
+ u_int8_t digest[MD5_DIGEST_LENGTH];
+ u_int8_t recv_digest[MD5_DIGEST_LENGTH];
+ char pwd[MAX_SIMPLE_AUTH_LEN];
+ struct rip_auth *auth_head;
+ struct md5_auth *a;
+ struct auth_md *md;
+ char *auth_data;
+ char *b = *buf;
+
+ *buf += RIP_HDR_LEN;
+ *len -= RIP_HDR_LEN;
+
+ auth_head = (struct rip_auth *)(*buf);
+
+ if (auth_head->auth_fixed != AUTH) {
+ if (iface->auth_type != AUTH_NONE) {
+ log_debug("auth_validate: packet carrying no"
+ " authentication");
+ return (-1);
+ }
+ return (0);
+ } else {
+ if (ntohs(auth_head->auth_type) !=
+ (u_int16_t)iface->auth_type) {
+ log_debug("auth_validate: wrong auth type");
+ return (-1);
+ }
+ }
+
+ switch (iface->auth_type) {
+ case AUTH_SIMPLE:
+ bcopy(*buf+sizeof(*auth_head), pwd, MAX_SIMPLE_AUTH_LEN);
+ if (bcmp(pwd, iface->auth_key, MAX_SIMPLE_AUTH_LEN)) {
+ log_debug("auth_validate: wrong password, "
+ "interface: %s", iface->name);
+ return (-1);
+ }
+ break;
+ case AUTH_CRYPT:
+ a = (struct md5_auth *)(*buf + sizeof(*auth_head));
+
+ if ((md = md_list_find(&iface->auth_md_list,
+ a->auth_keyid)) == NULL) {
+ log_debug("auth_validate: keyid %d not configured, "
+ "interface %s", a->auth_keyid,
+ iface->name);
+ return (-1);
+ }
+
+ if (nbr != NULL) {
+ if (ntohl(a->auth_seq) < nbr->auth_seq_num) {
+ log_debug("auth_validate: decreasing seq num, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+ } else if (nbr_failed != NULL) {
+ if (ntohl(a->auth_seq) < nbr_failed->auth_seq_num &&
+ ntohl(a->auth_seq)) {
+ log_debug("auth_validate: decreasing seq num, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+ }
+
+ /* XXX: maybe validate also the trailer header */
+ if (a->auth_length != MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN) {
+ log_debug("auth_validate: invalid key length, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+
+ auth_data = *buf;
+ auth_data += ntohs(a->auth_offset);
+
+ /* save the received digest and clear it in the packet */
+ bcopy(auth_data, recv_digest, sizeof(recv_digest));
+ bzero(auth_data, MD5_DIGEST_LENGTH);
+
+ /* insert plaintext key */
+ bzero(digest, MD5_DIGEST_LENGTH);
+ strncpy(digest, md->key, MD5_DIGEST_LENGTH);
+
+ /* calculate MD5 digest */
+ MD5Init(&hash);
+ MD5Update(&hash, b, ntohs(a->auth_offset) + RIP_HDR_LEN);
+ MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
+ MD5Final(digest, &hash);
+
+ if (bcmp(recv_digest, digest, sizeof(digest))) {
+ log_debug("auth_validate: invalid MD5 digest, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+
+ *crypt_seq_num = ntohl(a->auth_seq);
+
+ *len -= AUTH_TRLR_HDR_LEN + MD5_DIGEST_LENGTH;
+
+ break;
+ default:
+ log_debug("auth_validate: unknown auth type, interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ *buf += RIP_ENTRY_LEN;
+ *len -= RIP_ENTRY_LEN;
+
+ return (0);
+}
+
+int
+auth_gen(struct buf *buf, struct iface *iface)
+{
+ struct rip_auth auth_head;
+ struct md5_auth a;
+
+ auth_head.auth_fixed = AUTH;
+ auth_head.auth_type = htons(iface->auth_type);
+
+ buf_add(buf, &auth_head, sizeof(auth_head));
+
+ switch (iface->auth_type) {
+ case AUTH_SIMPLE:
+ return (buf_add(buf, &iface->auth_key, MAX_SIMPLE_AUTH_LEN));
+ break;
+ case AUTH_CRYPT:
+ bzero(&a, sizeof(a));
+ a.auth_keyid = iface->auth_keyid;
+ a.auth_seq = htonl(auth_get_seq_num());
+ a.auth_length = MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN;
+
+ return (buf_add(buf, &a, sizeof(a)));
+ break;
+ default:
+ /* NOTREACHED */
+ break;
+ }
+
+ return (0);
+}
+
+int
+auth_add_trailer(struct buf *buf, struct iface *iface)
+{
+ MD5_CTX hash;
+ u_int8_t digest[MD5_DIGEST_LENGTH];
+ struct auth_md *md;
+ struct md5_auth *a;
+ int pos;
+
+ pos = sizeof(struct rip_hdr) + sizeof(struct rip_auth);
+
+ /* add offset to header */
+ a = buf_seek(buf, pos, sizeof(*a));
+ a->auth_offset = htons(buf->wpos);
+
+ /* insert plaintext key */
+ if ((md = md_list_find(&iface->auth_md_list,
+ iface->auth_keyid)) == NULL) {
+ log_debug("auth_validate: keyid %d not configured, "
+ "interface %s", iface->auth_keyid, iface->name);
+ return (-1);
+ }
+
+ bzero(digest, MD5_DIGEST_LENGTH);
+ strncpy(digest, md->key, MD5_DIGEST_LENGTH);
+
+ auth_trailer_header_gen(buf);
+
+ /* calculate MD5 digest */
+ MD5Init(&hash);
+ MD5Update(&hash, buf->buf, buf->wpos);
+ MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
+ MD5Final(digest, &hash);
+
+ return (buf_add(buf, digest, MD5_DIGEST_LENGTH));
+}
+
+/* md list */
+void
+md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
+{
+ struct auth_md *md;
+
+ if ((md = md_list_find(head, keyid)) != NULL) {
+ /* update key */
+ strncpy(md->key, key, sizeof(md->key));
+ return;
+ }
+
+ if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
+ fatalx("md_list_add");
+
+ md->keyid = keyid;
+ strncpy(md->key, key, sizeof(md->key));
+ TAILQ_INSERT_TAIL(head, md, entry);
+}
+
+void
+md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
+{
+ struct auth_md *m, *md;
+
+ TAILQ_INIT(to);
+
+ TAILQ_FOREACH(m, from, entry) {
+ if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
+ fatalx("md_list_add");
+
+ md->keyid = m->keyid;
+ strncpy(md->key, m->key, sizeof(md->key));
+ TAILQ_INSERT_TAIL(to, md, entry);
+ }
+}
+
+void
+md_list_clr(struct auth_md_head *head)
+{
+ struct auth_md *m;
+
+ while ((m = TAILQ_FIRST(head)) != NULL) {
+ TAILQ_REMOVE(head, m, entry);
+ free(m);
+ }
+}
+
+struct auth_md *
+md_list_find(struct auth_md_head *head, u_int8_t keyid)
+{
+ struct auth_md *m;
+
+ TAILQ_FOREACH(m, head, entry)
+ if (m->keyid == keyid)
+ return (m);
+
+ return (NULL);
+}
diff --git a/usr.sbin/ripd/buffer.c b/usr.sbin/ripd/buffer.c
new file mode 100644
index 00000000000..645f8588ff3
--- /dev/null
+++ b/usr.sbin/ripd/buffer.c
@@ -0,0 +1,218 @@
+/* $OpenBSD: buffer.c,v 1.1 2006/10/18 16:11:58 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 "ripd.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/ripd/control.c b/usr.sbin/ripd/control.c
new file mode 100644
index 00000000000..77b3101cf41
--- /dev/null
+++ b/usr.sbin/ripd/control.c
@@ -0,0 +1,300 @@
+/* $OpenBSD: control.c,v 1.1 2006/10/18 16:11:58 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 "ripd.h"
+#include "rip.h"
+#include "ripe.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, RIPD_SOCKET, sizeof(sun.sun_path));
+
+ if (unlink(RIPD_SOCKET) == -1)
+ if (errno != ENOENT) {
+ log_warn("control_init: unlink %s", RIPD_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", RIPD_SOCKET);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(RIPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("control_init: chmod");
+ close(fd);
+ (void)unlink(RIPD_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(RIPD_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_SHOW_INTERFACE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(ifidx))
+ return;
+
+ memcpy(&ifidx, imsg.data, sizeof(ifidx));
+ ripe_iface_ctl(c, ifidx);
+ imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, NULL, 0);
+
+ break;
+ case IMSG_CTL_SHOW_RIB:
+ c->ibuf.pid = imsg.hdr.pid;
+ ripe_imsg_compose_rde(imsg.hdr.type, 0,
+ imsg.hdr.pid, imsg.data, imsg.hdr.len -
+ IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_SHOW_NBR:
+ ripe_nbr_ctl(c);
+ break;
+ case IMSG_CTL_KROUTE_ADDR:
+ case IMSG_CTL_KROUTE:
+ c->ibuf.pid = imsg.hdr.pid;
+ ripe_imsg_compose_parent(imsg.hdr.type,
+ imsg.hdr.pid, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_IFINFO:
+ c->ibuf.pid = imsg.hdr.pid;
+ ripe_imsg_compose_parent(imsg.hdr.type,
+ imsg.hdr.pid, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_FIB_COUPLE:
+ case IMSG_CTL_FIB_DECOUPLE:
+ case IMSG_CTL_RELOAD:
+ c->ibuf.pid = imsg.hdr.pid;
+ ripe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
+ 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/ripd/control.h b/usr.sbin/ripd/control.h
new file mode 100644
index 00000000000..6d372673006
--- /dev/null
+++ b/usr.sbin/ripd/control.h
@@ -0,0 +1,50 @@
+/* $OpenBSD: control.h,v 1.1 2006/10/18 16:11:58 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/ripd/imsg.c b/usr.sbin/ripd/imsg.c
new file mode 100644
index 00000000000..51157ee5ad4
--- /dev/null
+++ b/usr.sbin/ripd/imsg.c
@@ -0,0 +1,178 @@
+/* $OpenBSD: imsg.c,v 1.1 2006/10/18 16:11:58 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 "ripd.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/ripd/interface.c b/usr.sbin/ripd/interface.c
new file mode 100644
index 00000000000..be9917361ad
--- /dev/null
+++ b/usr.sbin/ripd/interface.c
@@ -0,0 +1,482 @@
+/* $OpenBSD: interface.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 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 <net/if_types.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <event.h>
+
+#include "ripd.h"
+#include "log.h"
+#include "rip.h"
+#include "rde.h"
+#include "ripe.h"
+
+extern struct ripd_conf *conf;
+
+int if_act_start(struct iface *);
+int if_act_reset(struct iface *);
+void if_keepalive_timer(int, short, void *);
+
+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_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",
+ "RESET"
+};
+
+static const char * const if_event_names[] = {
+ "NOTHING",
+ "UP",
+ "DOWN",
+};
+
+void
+if_init(struct ripd_conf *xconf, struct iface *iface)
+{
+ /* XXX as in ospfd I would like to kill that. This is a design error */
+ iface->fd = xconf->rip_socket;
+}
+
+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) {
+ /* event outside of the defined fsm, ignore it. */
+ log_debug("if_fsm: 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_RST:
+ ret = if_act_reset(iface);
+ break;
+ case IF_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ if (ret) {
+ log_debug("if_fsm: error changing state for interface %s, "
+ "event '%s', state '%s'", iface->name, if_event_name(event),
+ if_state_name(old_state));
+ return (0);
+ }
+
+ if (new_state != 0)
+ iface->state = new_state;
+
+ log_debug("if_fsm: 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_find_index(u_short ifindex)
+{
+ struct iface *iface;
+
+ LIST_FOREACH(iface, &conf->iface_list, entry) {
+ if (iface->ifindex == ifindex)
+ return (iface);
+ }
+
+ return (NULL);
+}
+
+
+/* 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 (0);
+ }
+
+ if (!((iface->flags & IFF_UP) &&
+ (iface->linkstate == LINK_STATE_UP ||
+ (iface->linkstate == LINK_STATE_UNKNOWN &&
+ iface->media_type != IFT_CARP)))) {
+ 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(ALL_RIP_ROUTERS, &addr);
+ if (if_join_group(iface, &addr)) {
+ log_warn("if_act_start: error joining group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ return (-1);
+ }
+
+ iface->state = IF_STA_ACTIVE;
+ break;
+ default:
+ fatalx("if_act_start: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_act_reset(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+ struct in_addr addr;
+
+ if (iface->passive)
+ return (0);
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ inet_aton(ALL_RIP_ROUTERS, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warn("if_act_reset: error leaving group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ }
+ break;
+ default:
+ fatalx("if_act_reset: unknown interface 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));
+ }
+ }
+
+ 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_opt(int fd)
+{
+ int yes = 1;
+
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &yes,
+ sizeof(int)) < 0) {
+ log_warn("if_set_opt: error setting IP_RECVIF");
+ 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);
+}
+
+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,
+ &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);
+}
+
+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)
+ 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)
+ return (-1);
+ break;
+ default:
+ fatalx("if_leave_group: unknown interface type");
+ }
+
+ return (0);
+}
+
+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;
+
+ LIST_INIT(&iface->nbr_list);
+ TAILQ_INIT(&iface->rp_list);
+ TAILQ_INIT(&iface->rq_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;
+ if (kif->flags & IFF_LOOPBACK) {
+ iface->type = IF_TYPE_POINTOPOINT;
+ /* XXX protect loopback from sending packets over lo? */
+ }
+
+ /* 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, 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, 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 (kif->flags & IFF_POINTOPOINT) {
+ if (ioctl(s, SIOCGIFDSTADDR, 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_del(struct iface *iface)
+{
+ struct nbr *nbr;
+
+ log_debug("if_del: interface %s", iface->name);
+
+ /* clear lists etc */
+ while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL)
+ nbr_act_del(nbr);
+
+ /* XXX rq_list, rp_list */
+
+ free(iface);
+}
+
+struct ctl_iface *
+if_to_ctl(struct iface *iface)
+{
+ static struct ctl_iface ictl;
+ struct timeval now;
+
+ memcpy(ictl.name, iface->name, sizeof(ictl.name));
+ memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
+ memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
+
+ ictl.ifindex = iface->ifindex;
+ ictl.state = iface->state;
+ ictl.mtu = iface->mtu;
+
+ ictl.baudrate = iface->baudrate;
+ ictl.flags = iface->flags;
+ ictl.metric = iface->cost;
+ ictl.type = iface->type;
+ ictl.linkstate = iface->linkstate;
+ ictl.passive = iface->passive;
+ ictl.mediatype = iface->media_type;
+
+ gettimeofday(&now, NULL);
+
+ if (iface->state != IF_STA_DOWN) {
+ ictl.uptime = now.tv_sec - iface->uptime;
+ } else
+ ictl.uptime = 0;
+
+ return (&ictl);
+}
diff --git a/usr.sbin/ripd/kroute.c b/usr.sbin/ripd/kroute.c
new file mode 100644
index 00000000000..39a18ab353b
--- /dev/null
+++ b/usr.sbin/ripd/kroute.c
@@ -0,0 +1,1126 @@
+/* $OpenBSD: kroute.c,v 1.1 2006/10/18 16:11:58 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/if_types.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 "ripd.h"
+#include "log.h"
+
+struct {
+ u_int32_t rtseq;
+ pid_t pid;
+ int fib_sync;
+ int fd;
+ struct event ev;
+} kr_state;
+
+struct kroute_node {
+ RB_ENTRY(kroute_node) entry;
+ struct kroute r;
+};
+
+struct kif_node {
+ RB_ENTRY(kif_node) entry;
+ struct kif k;
+};
+
+void kr_redistribute(int, struct kroute *);
+int kroute_compare(struct kroute_node *, struct kroute_node *);
+int kif_compare(struct kif_node *, struct kif_node *);
+
+struct kroute_node *kroute_find(in_addr_t, in_addr_t);
+int kroute_insert(struct kroute_node *);
+int kroute_remove(struct kroute_node *);
+void kroute_clear(void);
+
+struct kif_node *kif_find(int);
+int kif_insert(struct kif_node *);
+int kif_remove(struct kif_node *);
+void kif_clear(void);
+int kif_validate(int);
+
+struct kroute_node *kroute_match(in_addr_t);
+
+int protect_lo(void);
+u_int8_t prefixlen_classful(in_addr_t);
+void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
+void if_change(u_short, int, struct if_data *);
+void if_announce(void *);
+
+int send_rtmsg(int, int, struct kroute *);
+int dispatch_rtmsg(void);
+int fetchtable(void);
+int fetchifs(int);
+
+RB_HEAD(kroute_tree, kroute_node) krt;
+RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare)
+RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare)
+
+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 = 0, rcvbuf, default_rcvbuf;
+ socklen_t optlen;
+
+ kr_state.fib_sync = fs;
+
+ if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
+ log_warn("kr_init: socket");
+ return (-1);
+ }
+
+ /* not interested in my own messages */
+ if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK,
+ &opt, sizeof(opt)) == -1)
+ log_warn("kr_init: setsockopt"); /* not fatal */
+
+ /* grow receive buffer, don't wanna miss messages */
+ optlen = sizeof(default_rcvbuf);
+ if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF,
+ &default_rcvbuf, &optlen) == -1)
+ log_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF");
+ else
+ for (rcvbuf = MAX_RTSOCK_BUF;
+ rcvbuf > default_rcvbuf &&
+ setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF,
+ &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS;
+ rcvbuf /= 2)
+ ; /* nothing */
+
+ kr_state.pid = getpid();
+ kr_state.rtseq = 1;
+
+ RB_INIT(&krt);
+
+ if (fetchtable() == -1)
+ return (-1);
+
+ if (protect_lo() == -1)
+ return (-1);
+
+ event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST,
+ kr_dispatch_msg, NULL);
+ event_add(&kr_state.ev, NULL);
+
+ return (0);
+}
+
+int
+kr_change(struct kroute *kroute)
+{
+ struct kroute_node *kr;
+ int action = RTM_ADD;
+
+ if ((kr = kroute_find(kroute->prefix.s_addr, kroute->netmask.s_addr)) !=
+ NULL) {
+ if (!(kr->r.flags & F_KERNEL))
+ action = RTM_CHANGE;
+ else { /* a non-rip route already exists. not a problem */
+ if (!(kr->r.flags & (F_BGPD_INSERTED|
+ F_OSPFD_INSERTED))) {
+ kr->r.flags |= F_RIPD_INSERTED;
+ return (0);
+ }
+ /*
+ * rip route has higher pref
+ * - reset flags to the rip ones
+ * - use RTM_CHANGE
+ * - zero out ifindex (this is no longer relevant)
+ */
+ action = RTM_CHANGE;
+ kr->r.flags = kroute->flags | F_RIPD_INSERTED;
+ kr->r.ifindex = 0;
+ }
+ }
+
+ /* nexthop within 127/8 -> ignore silently */
+ if ((kroute->nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
+ htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
+ return (0);
+
+ if (send_rtmsg(kr_state.fd, action, kroute) == -1)
+ return (-1);
+
+ if (action == RTM_ADD) {
+ if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
+ log_warn("kr_change");
+ return (-1);
+ }
+ kr->r.prefix.s_addr = kroute->prefix.s_addr;
+ kr->r.netmask.s_addr = kroute->netmask.s_addr;
+ kr->r.nexthop.s_addr = kroute->nexthop.s_addr;
+ kr->r.flags = kroute->flags |= F_RIPD_INSERTED;
+
+ if (kroute_insert(kr) == -1)
+ free(kr);
+ } else
+ kr->r.nexthop.s_addr = kroute->nexthop.s_addr;
+
+ return (0);
+}
+
+int
+kr_delete(struct kroute *kroute)
+{
+ struct kroute_node *kr;
+
+ if ((kr = kroute_find(kroute->prefix.s_addr, kroute->netmask.s_addr)) ==
+ NULL)
+ return (0);
+
+ if (!(kr->r.flags & F_RIPD_INSERTED))
+ return (0);
+
+ if (kr->r.flags & F_KERNEL) {
+ /* remove F_RIPD_INSERTED flag, route still exists in kernel */
+ kr->r.flags &= ~F_RIPD_INSERTED;
+ return (0);
+ }
+
+ if (send_rtmsg(kr_state.fd, RTM_DELETE, kroute) == -1)
+ return (-1);
+
+ if (kroute_remove(kr) == -1)
+ return (-1);
+
+ return (0);
+}
+
+void
+kr_shutdown(void)
+{
+ kr_fib_decouple();
+ kroute_clear();
+ kif_clear();
+}
+
+void
+kr_fib_couple(void)
+{
+ struct kroute_node *kr;
+
+ if (kr_state.fib_sync == 1) /* already coupled */
+ return;
+
+ kr_state.fib_sync = 1;
+
+ RB_FOREACH(kr, kroute_tree, &krt)
+ if (!(kr->r.flags & F_KERNEL))
+ send_rtmsg(kr_state.fd, RTM_ADD, &kr->r);
+
+ log_info("kernel routing table coupled");
+}
+
+void
+kr_fib_decouple(void)
+{
+ struct kroute_node *kr;
+
+ if (kr_state.fib_sync == 0) /* already decoupled */
+ return;
+
+ RB_FOREACH(kr, kroute_tree, &krt)
+ if (!(kr->r.flags & F_KERNEL))
+ send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
+
+ kr_state.fib_sync = 0;
+
+ log_info("kernel routing table decoupled");
+}
+
+/* ARGSUSED */
+void
+kr_dispatch_msg(int fd, short event, void *bula)
+{
+ dispatch_rtmsg();
+}
+
+void
+kr_show_route(struct imsg *imsg)
+{
+ struct kroute_node *kr;
+ int flags;
+ struct in_addr addr;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_KROUTE:
+ if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) {
+ log_warnx("kr_show_route: wrong imsg len");
+ return;
+ }
+ memcpy(&flags, imsg->data, sizeof(flags));
+ RB_FOREACH(kr, kroute_tree, &krt)
+ if (!flags || kr->r.flags & flags) {
+ main_imsg_compose_ripe(IMSG_CTL_KROUTE,
+ imsg->hdr.pid, &kr->r, sizeof(kr->r));
+ }
+ break;
+ case IMSG_CTL_KROUTE_ADDR:
+ if (imsg->hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct in_addr)) {
+ log_warnx("kr_show_route: wrong imsg len");
+ return;
+ }
+ memcpy(&addr, imsg->data, sizeof(addr));
+ kr = NULL;
+ kr = kroute_match(addr.s_addr);
+ if (kr != NULL)
+ main_imsg_compose_ripe(IMSG_CTL_KROUTE, imsg->hdr.pid,
+ &kr->r, sizeof(kr->r));
+ break;
+ default:
+ log_debug("kr_show_route: error handling imsg");
+ break;
+ }
+
+ main_imsg_compose_ripe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0);
+}
+
+void
+kr_ifinfo(char *ifname, pid_t pid)
+{
+ struct kif_node *kif;
+
+ RB_FOREACH(kif, kif_tree, &kit)
+ if (ifname == NULL || !strcmp(ifname, kif->k.ifname)) {
+ main_imsg_compose_ripe(IMSG_CTL_IFINFO,
+ pid, &kif->k, sizeof(kif->k));
+ }
+
+ main_imsg_compose_ripe(IMSG_CTL_END, pid, NULL, 0);
+}
+
+void
+kr_redistribute(int type, struct kroute *kr)
+{
+ u_int32_t a;
+
+
+ if (type == IMSG_NETWORK_DEL) {
+dont_redistribute:
+ /* was the route redistributed? */
+ if (kr->flags & F_REDISTRIBUTED) {
+ /* remove redistributed flag */
+ kr->flags &= ~F_REDISTRIBUTED;
+ main_imsg_compose_rde(type, 0, kr,
+ sizeof(struct kroute));
+ }
+ return;
+ }
+
+ /* interface is not up and running so don't announce */
+ if (kr->flags & F_DOWN)
+ return;
+
+ /*
+ * We consider the loopback net, multicast and experimental addresses
+ * as not redistributable.
+ */
+ a = ntohl(kr->prefix.s_addr);
+ if (IN_MULTICAST(a) || IN_BADCLASS(a) ||
+ (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
+ return;
+ /*
+ * Consider networks with nexthop loopback as not redistributable.
+ */
+ if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK))
+ return;
+
+ /* Should we redistrubute this route? */
+ if (!rip_redistribute(kr))
+ goto dont_redistribute;
+
+ /* Does not matter if we resend the kr, the RDE will cope. */
+ kr->flags |= F_REDISTRIBUTED;
+ main_imsg_compose_rde(type, 0, kr, sizeof(struct kroute));
+}
+
+/* rb-tree compare */
+int
+kroute_compare(struct kroute_node *a, struct kroute_node *b)
+{
+ if (ntohl(a->r.prefix.s_addr) < ntohl(b->r.prefix.s_addr))
+ return (-1);
+ if (ntohl(a->r.prefix.s_addr) > ntohl(b->r.prefix.s_addr))
+ return (1);
+ if (ntohl(a->r.netmask.s_addr) < ntohl(b->r.netmask.s_addr))
+ return (-1);
+ if (ntohl(a->r.netmask.s_addr) > ntohl(b->r.netmask.s_addr))
+ return (1);
+ return (0);
+}
+
+int
+kif_compare(struct kif_node *a, struct kif_node *b)
+{
+ return (b->k.ifindex - a->k.ifindex);
+}
+
+/* tree management */
+struct kroute_node *
+kroute_find(in_addr_t prefix, in_addr_t netmask)
+{
+ struct kroute_node s;
+
+ s.r.prefix.s_addr = prefix;
+ s.r.netmask.s_addr = netmask;
+
+ return (RB_FIND(kroute_tree, &krt, &s));
+}
+
+int
+kroute_insert(struct kroute_node *kr)
+{
+ if (RB_INSERT(kroute_tree, &krt, kr) != NULL) {
+ log_warnx("kroute_insert failed for %s/%u",
+ inet_ntoa(kr->r.prefix),
+ mask2prefixlen(kr->r.netmask.s_addr));
+ free(kr);
+ return (-1);
+ }
+
+ if (!(kr->r.flags & F_KERNEL)) {
+ /* don't validate or redistribute rip route */
+ kr->r.flags &= ~F_DOWN;
+ return (0);
+ }
+
+ if (kif_validate(kr->r.ifindex))
+ kr->r.flags &= ~F_DOWN;
+ else
+ kr->r.flags |= F_DOWN;
+
+ kr_redistribute(IMSG_NETWORK_ADD, &kr->r);
+
+ return (0);
+}
+
+int
+kroute_remove(struct kroute_node *kr)
+{
+ if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) {
+ log_warnx("kroute_remove failed for %s/%u",
+ inet_ntoa(kr->r.prefix),
+ mask2prefixlen(kr->r.netmask.s_addr));
+ return (-1);
+ }
+
+ kr_redistribute(IMSG_NETWORK_DEL, &kr->r);
+
+ free(kr);
+ return (0);
+}
+
+void
+kroute_clear(void)
+{
+ struct kroute_node *kr;
+
+ while ((kr = RB_MIN(kroute_tree, &krt)) != NULL)
+ kroute_remove(kr);
+}
+
+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);
+}
+
+int
+kif_validate(int ifindex)
+{
+ struct kif_node *kif;
+
+ if ((kif = kif_find(ifindex)) == NULL) {
+ log_warnx("interface with index %u not found", ifindex);
+ return (1);
+ }
+
+ return (kif->k.nh_reachable);
+}
+
+struct kroute_node *
+kroute_match(in_addr_t key)
+{
+ int i;
+ struct kroute_node *kr;
+
+ /* we will never match the default route */
+ for (i = 32; i > 0; i--)
+ if ((kr = kroute_find(key & prefixlen2mask(i),
+ prefixlen2mask(i))) != NULL)
+ return (kr);
+
+ /* if we don't have a match yet, try to find a default route */
+ if ((kr = kroute_find(0, 0)) != NULL)
+ return (kr);
+
+ return (NULL);
+}
+
+/* misc */
+int
+protect_lo(void)
+{
+ struct kroute_node *kr;
+
+ /* special protection for 127/8 */
+ if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
+ log_warn("protect_lo");
+ return (-1);
+ }
+ kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK);
+ kr->r.netmask.s_addr = htonl(IN_CLASSA_NET);
+ kr->r.flags = F_KERNEL|F_CONNECTED;
+
+ if (RB_INSERT(kroute_tree, &krt, kr) != NULL)
+ free(kr); /* kernel route already there, no problem */
+
+ return (0);
+}
+
+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 (htonl(0xffffffff << (32 - prefixlen)));
+}
+
+#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;
+ }
+}
+
+void
+if_change(u_short ifindex, int flags, struct if_data *ifd)
+{
+ struct kif_node *kif;
+ struct kroute_node *kr;
+ int type;
+ 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_UP ||
+ (ifd->ifi_link_state == LINK_STATE_UNKNOWN &&
+ ifd->ifi_type != IFT_CARP))) == kif->k.nh_reachable)
+ return; /* nothing changed wrt nexthop validity */
+
+ kif->k.nh_reachable = reachable;
+ type = reachable ? IMSG_NETWORK_ADD : IMSG_NETWORK_DEL;
+
+ /* notify ripe about interface link state */
+ main_imsg_compose_ripe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k));
+
+ /* update redistribute list */
+ RB_FOREACH(kr, kroute_tree, &krt)
+ if (kr->r.ifindex == ifindex) {
+ if (reachable)
+ kr->r.flags &= ~F_DOWN;
+ else
+ kr->r.flags |= F_DOWN;
+
+ kr_redistribute(type, &kr->r);
+ }
+}
+
+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 */
+int
+send_rtmsg(int fd, int action, struct kroute *kroute)
+{
+ struct {
+ struct rt_msghdr hdr;
+ struct sockaddr_in prefix;
+ struct sockaddr_in nexthop;
+ struct sockaddr_in mask;
+ } r;
+
+ if (kr_state.fib_sync == 0)
+ return (0);
+
+ bzero(&r, sizeof(r));
+ r.hdr.rtm_msglen = sizeof(r);
+ r.hdr.rtm_version = RTM_VERSION;
+ r.hdr.rtm_type = action;
+ r.hdr.rtm_flags = RTF_PROTO3;
+ if (action == RTM_CHANGE) /* force PROTO3 reset the other flags */
+ r.hdr.rtm_fmask =
+ RTF_PROTO3|RTF_PROTO2|RTF_PROTO1|RTF_REJECT|RTF_BLACKHOLE;
+ r.hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */
+ r.hdr.rtm_addrs = RTA_DST|RTA_GATEWAY|RTA_NETMASK;
+ r.prefix.sin_len = sizeof(r.prefix);
+ r.prefix.sin_family = AF_INET;
+ r.prefix.sin_addr.s_addr = kroute->prefix.s_addr;
+
+ r.nexthop.sin_len = sizeof(r.nexthop);
+ r.nexthop.sin_family = AF_INET;
+ r.nexthop.sin_addr.s_addr = kroute->nexthop.s_addr;
+ if (kroute->nexthop.s_addr != 0)
+ r.hdr.rtm_flags |= RTF_GATEWAY;
+
+ r.mask.sin_len = sizeof(r.mask);
+ r.mask.sin_family = AF_INET;
+ r.mask.sin_addr.s_addr = kroute->netmask.s_addr;
+
+retry:
+ if (write(fd, &r, sizeof(r)) == -1) {
+ switch (errno) {
+ case ESRCH:
+ if (r.hdr.rtm_type == RTM_CHANGE) {
+ r.hdr.rtm_type = RTM_ADD;
+ goto retry;
+ } else if (r.hdr.rtm_type == RTM_DELETE) {
+ log_info("route %s/%u vanished before delete",
+ inet_ntoa(kroute->prefix),
+ mask2prefixlen(kroute->netmask.s_addr));
+ return (0);
+ } else {
+ log_warnx("send_rtmsg: action %u, "
+ "prefix %s/%u: %s", r.hdr.rtm_type,
+ inet_ntoa(kroute->prefix),
+ mask2prefixlen(kroute->netmask.s_addr),
+ strerror(errno));
+ return (0);
+ }
+ break;
+ default:
+ log_warnx("send_rtmsg: action %u, prefix %s/%u: %s",
+ r.hdr.rtm_type, inet_ntoa(kroute->prefix),
+ mask2prefixlen(kroute->netmask.s_addr),
+ strerror(errno));
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+int
+fetchtable(void)
+{
+ size_t len;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_in *sa_in;
+ struct kroute_node *kr;
+ struct iface *iface = NULL;
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchtable");
+ 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 += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ if ((sa = rti_info[RTAX_DST]) == NULL)
+ continue;
+
+ if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
+ continue;
+
+#ifdef RTF_MPATH
+ if (rtm->rtm_flags & RTF_MPATH) /* multipath */
+ continue;
+#endif
+
+ if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
+ log_warn("fetchtable");
+ free(buf);
+ return (-1);
+ }
+
+ kr->r.flags = F_KERNEL;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ kr->r.prefix.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK];
+ if (rtm->rtm_flags & RTF_STATIC)
+ kr->r.flags |= F_STATIC;
+ if (rtm->rtm_flags & RTF_DYNAMIC)
+ kr->r.flags |= F_DYNAMIC;
+ if (rtm->rtm_flags & RTF_PROTO1)
+ kr->r.flags |= F_BGPD_INSERTED;
+ if (rtm->rtm_flags & RTF_PROTO2)
+ kr->r.flags |= F_OSPFD_INSERTED;
+ if (sa_in != NULL) {
+ if (sa_in->sin_len == 0)
+ break;
+ kr->r.netmask.s_addr =
+ sa_in->sin_addr.s_addr;
+ } else if (rtm->rtm_flags & RTF_HOST)
+ kr->r.netmask.s_addr = prefixlen2mask(32);
+ else
+ kr->r.netmask.s_addr =
+ prefixlen2mask(prefixlen_classful
+ (kr->r.prefix.s_addr));
+ break;
+ default:
+ free(kr);
+ continue;
+ }
+
+ kr->r.ifindex = rtm->rtm_index;
+
+ iface = if_find_index(rtm->rtm_index);
+ if (iface != NULL)
+ kr->r.metric = iface->cost;
+ else
+ kr->r.metric = 1;
+
+ if ((sa = rti_info[RTAX_GATEWAY]) != NULL)
+ switch (sa->sa_family) {
+ case AF_INET:
+ kr->r.nexthop.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ break;
+ case AF_LINK:
+ kr->r.flags |= F_CONNECTED;
+ break;
+ }
+
+ if (rtm->rtm_flags & RTF_PROTO3) {
+ send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
+ free(kr);
+ } else
+ kroute_insert(kr);
+
+ }
+ free(buf);
+ return (0);
+}
+
+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));
+ if (ifm.ifm_type != RTM_IFINFO)
+ continue;
+
+ sa = (struct sockaddr *)(next + sizeof(ifm));
+ get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
+
+ 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_UP ||
+ (ifm.ifm_data.ifi_link_state == LINK_STATE_UNKNOWN &&
+ ifm.ifm_data.ifi_type != IFT_CARP));
+ if ((sa = rti_info[RTAX_IFP]) != NULL)
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (sdl->sdl_nlen >= sizeof(kif->k.ifname))
+ memcpy(kif->k.ifname, sdl->sdl_data,
+ sizeof(kif->k.ifname) - 1);
+ else if (sdl->sdl_nlen > 0)
+ memcpy(kif->k.ifname, sdl->sdl_data,
+ sdl->sdl_nlen);
+ /* string already terminated via calloc() */
+ }
+
+ kif_insert(kif);
+ }
+ free(buf);
+ return (0);
+}
+
+int
+dispatch_rtmsg(void)
+{
+ char buf[RT_BUF_SIZE];
+ ssize_t n;
+ char *next, *lim;
+ struct rt_msghdr *rtm;
+ struct if_msghdr ifm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_in *sa_in;
+ struct kroute_node *kr;
+ struct in_addr prefix, nexthop, netmask;
+ int flags;
+ u_short ifindex = 0;
+
+ if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) {
+ log_warn("dispatch_rtmsg: read error");
+ return (-1);
+ }
+
+ if (n == 0) {
+ log_warnx("routing socket closed");
+ return (-1);
+ }
+
+ lim = buf + n;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+
+ prefix.s_addr = 0;
+ netmask.s_addr = 0;
+ flags = F_KERNEL;
+ nexthop.s_addr = 0;
+
+ if (rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE ||
+ rtm->rtm_type == RTM_DELETE) {
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ if (rtm->rtm_pid == kr_state.pid) /* cause by us */
+ continue;
+
+ if (rtm->rtm_errno) /* failed attempts... */
+ continue;
+
+ if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
+ continue;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ prefix.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ sa_in = (struct sockaddr_in *)
+ rti_info[RTAX_NETMASK];
+ if (sa_in != NULL) {
+ if (sa_in->sin_len != 0)
+ netmask.s_addr =
+ sa_in->sin_addr.s_addr;
+ } else if (rtm->rtm_flags & RTF_HOST)
+ netmask.s_addr = prefixlen2mask(32);
+ else
+ netmask.s_addr =
+ prefixlen2mask(prefixlen_classful(
+ prefix.s_addr));
+ if (rtm->rtm_flags & RTF_STATIC)
+ flags |= F_STATIC;
+ if (rtm->rtm_flags & RTF_DYNAMIC)
+ flags |= F_DYNAMIC;
+ if (rtm->rtm_flags & RTF_PROTO1)
+ flags |= F_BGPD_INSERTED;
+ if (rtm->rtm_flags & RTF_PROTO2)
+ flags |= F_OSPFD_INSERTED;
+ break;
+ default:
+ continue;
+ }
+
+ ifindex = rtm->rtm_index;
+ if ((sa = rti_info[RTAX_GATEWAY]) != NULL) {
+ switch (sa->sa_family) {
+ case AF_INET:
+ nexthop.s_addr = ((struct
+ sockaddr_in *)sa)->sin_addr.s_addr;
+ break;
+ case AF_LINK:
+ flags |= F_CONNECTED;
+ break;
+ }
+ }
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_ADD:
+ case RTM_CHANGE:
+ if (nexthop.s_addr == 0 && !(flags & F_CONNECTED)) {
+ log_warnx("dispatch_rtmsg no nexthop for %s/%u",
+ inet_ntoa(prefix),
+ mask2prefixlen(netmask.s_addr));
+ continue;
+ }
+
+ if ((kr = kroute_find(prefix.s_addr, netmask.s_addr)) !=
+ NULL) {
+ /* rip route overridden by kernel */
+ /* pref is not checked because this is forced */
+ if (kr->r.flags & F_RIPD_INSERTED)
+ flags |= F_RIPD_INSERTED;
+ if (kr->r.flags & F_REDISTRIBUTED)
+ flags |= F_REDISTRIBUTED;
+ kr->r.nexthop.s_addr = nexthop.s_addr;
+ kr->r.flags = flags;
+ kr->r.ifindex = ifindex;
+
+ if (kif_validate(kr->r.ifindex))
+ kr->r.flags &= ~F_DOWN;
+ else
+ kr->r.flags |= F_DOWN;
+
+ /* just readd, the RDE will care */
+ kr_redistribute(IMSG_NETWORK_ADD, &kr->r);
+ } else {
+ if ((kr = calloc(1,
+ sizeof(struct kroute_node))) == NULL) {
+ log_warn("dispatch_rtmsg");
+ return (-1);
+ }
+ kr->r.prefix.s_addr = prefix.s_addr;
+ kr->r.netmask.s_addr = netmask.s_addr;
+ kr->r.nexthop.s_addr = nexthop.s_addr;
+ kr->r.flags = flags;
+ kr->r.ifindex = ifindex;
+
+ kroute_insert(kr);
+ }
+ break;
+ case RTM_DELETE:
+ if ((kr = kroute_find(prefix.s_addr, netmask.s_addr)) ==
+ NULL)
+ continue;
+ if (!(kr->r.flags & F_KERNEL))
+ continue;
+ if (kr->r.flags & F_RIPD_INSERTED)
+ main_imsg_compose_rde(IMSG_KROUTE_GET, 0,
+ &kr->r, sizeof(struct kroute));
+ if (kroute_remove(kr) == -1)
+ return (-1);
+ break;
+ 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;
+ }
+ }
+ return (0);
+}
diff --git a/usr.sbin/ripd/log.c b/usr.sbin/ripd/log.c
new file mode 100644
index 00000000000..e581250f667
--- /dev/null
+++ b/usr.sbin/ripd/log.c
@@ -0,0 +1,222 @@
+/* $OpenBSD: log.c,v 1.1 2006/10/18 16:11:58 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 "ripd.h"
+#include "log.h"
+
+static const char * const procnames[] = {
+ "parent",
+ "ripe",
+ "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[ripd_process],
+ strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal in %s: %s: %s",
+ procnames[ripd_process], emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal in %s: %s",
+ procnames[ripd_process], emsg);
+
+ if (ripd_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_ACTIVE:
+ return ("ACTIVE");
+ 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");
+ case IF_TYPE_NBMA:
+ return ("NBMA");
+ case IF_TYPE_POINTOMULTIPOINT:
+ return ("POINTOMULTIPOINT");
+ }
+ /* NOTREACHED */
+ return ("UNKNOWN");
+}
+
+const char *
+if_auth_name(enum auth_type type)
+{
+ switch (type) {
+ case AUTH_NONE:
+ return ("none");
+ case AUTH_SIMPLE:
+ return ("simple");
+ case AUTH_CRYPT:
+ return ("crypt");
+ }
+ /* NOTREACHED */
+ return ("unknown");
+}
+
+const char *
+if_state_name(int state)
+{
+ switch (state) {
+ case IF_STA_DOWN:
+ return ("DOWN");
+ case IF_STA_ACTIVE:
+ return ("ACTIVE");
+ default:
+ return ("UNKNOWN");
+ }
+}
diff --git a/usr.sbin/ripd/log.h b/usr.sbin/ripd/log.h
new file mode 100644
index 00000000000..f1be0c23b55
--- /dev/null
+++ b/usr.sbin/ripd/log.h
@@ -0,0 +1,35 @@
+/* $OpenBSD: log.h,v 1.1 2006/10/18 16:11:58 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;
+const char *if_type_name(enum iface_type);
+const char *nbr_state_name(int);
+
+#endif /* _LOG_H_ */
diff --git a/usr.sbin/ripd/neighbor.c b/usr.sbin/ripd/neighbor.c
new file mode 100644
index 00000000000..edd33019bc2
--- /dev/null
+++ b/usr.sbin/ripd/neighbor.c
@@ -0,0 +1,357 @@
+/* $OpenBSD: neighbor.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * 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 "ripd.h"
+#include "rip.h"
+#include "ripe.h"
+#include "log.h"
+#include "rde.h"
+
+void nbr_start_timer(struct nbr *);
+void nbr_reset_timer(struct nbr *);
+
+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_REQUEST_RCVD, NBR_ACT_STRT_TIMER, NBR_STA_ACTIVE},
+ {NBR_STA_DOWN, NBR_EVT_RESPONSE_RCVD, NBR_ACT_STRT_TIMER, NBR_STA_ACTIVE},
+ {NBR_STA_ACTIVE, NBR_EVT_RESPONSE_RCVD, NBR_ACT_RST_TIMER, NBR_STA_ACTIVE},
+ {NBR_STA_ACTIVE, NBR_EVT_REQUEST_RCVD, NBR_ACT_NOTHING, NBR_STA_ACTIVE},
+ {NBR_STA_ACTIVE, NBR_EVT_TIMEOUT, NBR_ACT_DEL, NBR_STA_DOWN},
+ {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN},
+ {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0},
+};
+
+const char * const nbr_event_names[] = {
+ "RESPONSE RCVD",
+ "REQUEST RCVD",
+ "NBR TIMEOUT",
+ "NBR KILL",
+ "NOTHING"
+};
+
+const char * const nbr_action_names[] = {
+ "START TIMER",
+ "RESET TIMER",
+ "DELETE NBR",
+ "NOTHING"
+};
+
+int
+nbr_fsm(struct nbr *nbr, enum nbr_event event)
+{
+ struct timeval now;
+ int old_state;
+ int new_state = 0;
+ int i;
+
+ 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) {
+ /* 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_TIMER:
+ nbr_reset_timer(nbr);
+ break;
+ case NBR_ACT_STRT_TIMER:
+ nbr_start_timer(nbr);
+ break;
+ case NBR_ACT_DEL:
+ nbr_act_del(nbr);
+ break;
+ case NBR_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ if (new_state != 0)
+ nbr->state = new_state;
+
+ if (old_state != nbr->state) {
+ /* neighbor changed from/to ACTIVE */
+ 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 (0);
+}
+
+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->rp_list);
+ TAILQ_INIT(&nbr->rq_list);
+
+ /* set event structures */
+ evtimer_set(&nbr->timeout_timer, nbr_timeout_timer, nbr);
+
+ log_debug("nbr_new: neighbor ID %s, peerid %lu",
+ inet_ntoa(nbr->id), nbr->peerid);
+
+ return (nbr);
+}
+
+void
+nbr_act_del(struct nbr *nbr)
+{
+ struct nbr_failed *nbr_failed;
+ struct iface *iface;
+
+ if (nbr->iface->auth_type == AUTH_CRYPT) {
+ if ((nbr_failed = calloc(1, sizeof(*nbr_failed))) == NULL)
+ fatal("nbr_act_del");
+
+ nbr_failed->addr = nbr->addr;
+ nbr_failed->auth_seq_num = nbr->auth_seq_num;
+ iface = nbr->iface;
+ LIST_INSERT_HEAD(&iface->failed_nbr_list,
+ nbr_failed, entry);
+ }
+
+ log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id),
+ nbr->peerid);
+
+ /* clear lists */
+ //response_list_clr(&nbr->rr_list);
+
+ LIST_REMOVE(nbr, entry);
+ LIST_REMOVE(nbr, hash);
+
+ free(nbr);
+}
+
+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);
+}
+
+struct nbr_failed *
+nbr_failed_find(struct iface *iface, u_int32_t src_ip)
+{
+ struct nbr_failed *nbr_failed = NULL;
+
+ LIST_FOREACH(nbr_failed, &iface->failed_nbr_list, entry) {
+ if (nbr_failed->addr.s_addr == src_ip) {
+ return (nbr_failed);
+ }
+ }
+
+ return (NULL);
+}
+
+void
+nbr_failed_delete(struct iface *iface, struct nbr_failed *nbr_failed)
+{
+ LIST_REMOVE(nbr_failed, entry);
+
+ free(nbr_failed);
+}
+
+/* timers */
+/* ARGSUSED */
+void
+nbr_timeout_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ nbr_fsm(nbr, NBR_EVT_TIMEOUT);
+}
+
+/* actions */
+void
+nbr_reset_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = NBR_TIMEOUT;
+
+ if (evtimer_add(&nbr->timeout_timer, &tv) == -1)
+ fatal("nbr_reset_timer");
+}
+
+void
+nbr_start_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = NBR_TIMEOUT;
+
+ if (evtimer_add(&nbr->timeout_timer, &tv) == -1)
+ fatal("nbr_start_timer");
+}
+
+/* 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.nbr_state = nbr->state;
+ nctl.iface_state = nbr->iface->state;
+
+ gettimeofday(&now, NULL);
+ if (evtimer_pending(&nbr->timeout_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ if (nbr->state & NBR_STA_DOWN)
+ nctl.dead_timer = NBR_TIMEOUT - res.tv_sec;
+ else
+ nctl.dead_timer = res.tv_sec;
+ } else
+ nctl.dead_timer = 0;
+
+ if (nbr->state == NBR_STA_ACTIVE) {
+ nctl.uptime = now.tv_sec - nbr->uptime;
+ } else
+ nctl.uptime = 0;
+
+ return (&nctl);
+}
diff --git a/usr.sbin/ripd/packet.c b/usr.sbin/ripd/packet.c
new file mode 100644
index 00000000000..6b812f34ccf
--- /dev/null
+++ b/usr.sbin/ripd/packet.c
@@ -0,0 +1,251 @@
+/* $OpenBSD: packet.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * Copyright (c) 2004, 2005 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/uio.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+
+#include <net/if_dl.h>
+
+#include <errno.h>
+#include <event.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ripd.h"
+#include "rip.h"
+#include "log.h"
+#include "ripe.h"
+
+int rip_hdr_sanity_check(struct rip_hdr *);
+struct iface *find_iface(struct ripd_conf *, unsigned int, struct in_addr);
+
+int
+gen_rip_hdr(struct buf *buf, u_int8_t command)
+{
+ struct rip_hdr rip_hdr;
+
+ bzero(&rip_hdr, sizeof(rip_hdr));
+ rip_hdr.version = RIP_VERSION;
+ rip_hdr.command = command;
+
+ return (buf_add(buf, &rip_hdr, sizeof(rip_hdr)));
+}
+
+/* send and receive packets */
+int
+send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst)
+{
+ /* 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)
+{
+ char cbuf[CMSG_SPACE(sizeof(struct sockaddr_dl))];
+ struct sockaddr_in src;
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct sockaddr_dl *dst = NULL;
+ struct nbr_failed *nbr_failed = NULL;
+ struct ripd_conf *xconf = bula;
+ struct iface *iface;
+ struct rip_hdr *rip_hdr;
+ struct nbr *nbr;
+ char *buf;
+ ssize_t r;
+ u_int16_t len, srcport;
+ u_int32_t auth_crypt_num = 0;
+
+ if (event != EV_READ)
+ return;
+
+ /* setup buffer */
+ buf = pkt_ptr;
+
+ bzero(&msg, sizeof(msg));
+
+ iov.iov_base = buf;
+ iov.iov_len = READ_BUF_SIZE;
+ msg.msg_name = &src;
+ msg.msg_namelen = sizeof(src);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+
+ if ((r = recvmsg(fd, &msg, 0)) == -1) {
+ if (errno != EINTR && errno != EAGAIN)
+ log_debug("recv_packet: read error: %s",
+ strerror(errno));
+ return;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVIF) {
+ dst = (struct sockaddr_dl *)CMSG_DATA(cmsg);
+ break;
+ }
+ }
+
+ if (dst == NULL)
+ return;
+
+ len = (u_int16_t)r;
+
+ /* Check the packet is not from one of the local interfaces */
+ LIST_FOREACH(iface, &xconf->iface_list, entry) {
+ if (iface->addr.s_addr == src.sin_addr.s_addr)
+ return;
+ }
+
+ /* find a matching interface */
+ if ((iface = find_iface(xconf, dst->sdl_index, src.sin_addr)) == NULL) {
+ log_debug("recv_packet: cannot find a matching interface");
+ return;
+ }
+
+ srcport = ntohs(src.sin_port);
+
+ /* RIP header sanity checks */
+ if (len < RIP_HDR_LEN) {
+ log_debug("recv_packet: bad packet size");
+ return;
+ }
+ rip_hdr = (struct rip_hdr *)buf;
+
+ if (rip_hdr_sanity_check(rip_hdr) == -1)
+ return;
+
+ nbr = nbr_find_ip(iface, src.sin_addr.s_addr);
+
+ if (nbr == NULL && iface->auth_type == AUTH_CRYPT)
+ nbr_failed = nbr_failed_find(iface, src.sin_addr.s_addr);
+
+ /* switch RIP command */
+ switch (rip_hdr->command) {
+ case COMMAND_REQUEST:
+ if ((msg.msg_flags & MSG_MCAST) == 0 && srcport == RIP_PORT)
+ return;
+
+ /* XXX: it would be better to not create a nbr on request
+ * becouse this could lead to DoS even on a authenticated
+ * environment.
+ */
+ if (nbr == NULL) {
+ nbr = nbr_new(src.sin_addr.s_addr, iface, 0);
+ if (nbr_failed != NULL) {
+ nbr->auth_seq_num = nbr_failed->auth_seq_num;
+ nbr_failed_delete(iface, nbr_failed);
+ }
+ nbr->addr = src.sin_addr;
+ }
+ nbr->port = srcport;
+ nbr_fsm(nbr, NBR_EVT_REQUEST_RCVD);
+
+ buf += RIP_HDR_LEN;
+ len -= RIP_HDR_LEN;
+
+ recv_request(iface, nbr, buf, len);
+ break;
+ case COMMAND_RESPONSE:
+ if (srcport != RIP_PORT)
+ return;
+
+ if (auth_validate(&buf, &len, iface, nbr, nbr_failed,
+ &auth_crypt_num)) {
+ log_warnx("recv_packet: authentication error, "
+ "interface %s", iface->name);
+ return;
+ }
+
+ if (nbr == NULL) {
+ nbr = nbr_new(src.sin_addr.s_addr, iface, 0);
+ if (nbr_failed != NULL)
+ nbr_failed_delete(iface, nbr_failed);
+ nbr->addr = src.sin_addr;
+ }
+ nbr->auth_seq_num = auth_crypt_num;
+ nbr_fsm(nbr, NBR_EVT_RESPONSE_RCVD);
+
+ recv_response(iface, nbr, buf, len);
+ break;
+ default:
+ log_debug("recv_packet: unknown RIP command, interface %s",
+ iface->name);
+ }
+}
+
+int
+rip_hdr_sanity_check(struct rip_hdr *rip_hdr)
+{
+ if (rip_hdr->version != RIP_VERSION) {
+ log_debug("recv_packet: invalid RIP version %d",
+ rip_hdr->version);
+ return (-1);
+ }
+
+ return (0);
+}
+
+struct iface *
+find_iface(struct ripd_conf *xconf, unsigned int ifindex, struct in_addr src)
+{
+ struct iface *iface = NULL;
+
+ /* returned interface needs to be active */
+ LIST_FOREACH(iface, &xconf->iface_list, entry) {
+ if (ifindex != 0 && ifindex == iface->ifindex &&
+ !iface->passive && (iface->addr.s_addr &
+ iface->mask.s_addr) == (src.s_addr & iface->mask.s_addr))
+ /*
+ * XXX may fail on P2P links because src and dst don't
+ * have to share a common subnet on the otherhand
+ * checking somthing like this will help to support
+ * multiple networks configured on one interface.
+ */
+ return (iface);
+ }
+
+ return (NULL);
+}
diff --git a/usr.sbin/ripd/parse.y b/usr.sbin/ripd/parse.y
new file mode 100644
index 00000000000..e5e98ca8b18
--- /dev/null
+++ b/usr.sbin/ripd/parse.y
@@ -0,0 +1,763 @@
+/* $OpenBSD: parse.y,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * Copyright (c) 2004, 2005 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/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ripd.h"
+#include "rip.h"
+#include "ripe.h"
+#include "log.h"
+
+static struct ripd_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 ripd_conf *);
+int check_file_secrecy(int fd, const char *fname);
+u_int32_t get_rtr_id(void);
+
+static struct {
+ char auth_key[MAX_SIMPLE_AUTH_LEN];
+ struct auth_md_head md_list;
+ enum auth_type auth_type;
+ u_int8_t auth_keyid;
+ u_int8_t cost;
+} *defs, globaldefs, 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 *);
+
+typedef struct {
+ union {
+ u_int32_t number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token SPLIT_HORIZON TRIGGERED_UPDATES FIBUPDATE REDISTRIBUTE
+%token AUTHKEY AUTHTYPE AUTHMD AUTHMDKEYID
+%token INTERFACE
+%token COST PASSIVE
+%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'
+ ;
+
+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("recognized values are yes or no");
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+varset : STRING '=' string {
+ if (conf->opts & RIPD_OPT_VERBOSE)
+ printf("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+conf_main : SPLIT_HORIZON STRING {
+ /* clean flags first */
+ conf->options &= ~(OPT_SPLIT_HORIZON |
+ OPT_SPLIT_POISONED);
+ if (!strcmp($2, "none"))
+ /* nothing */ ;
+ else if (!strcmp($2, "default"))
+ conf->options |= OPT_SPLIT_HORIZON;
+ else if (!strcmp($2, "poisoned"))
+ conf->options |= OPT_SPLIT_POISONED;
+ else {
+ yyerror("unknon split horizon type");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | TRIGGERED_UPDATES yesno {
+ if ($2 == 1)
+ conf->options |= OPT_TRIGGERED_UPDATES;
+ else
+ conf->options &= ~OPT_TRIGGERED_UPDATES;
+ }
+ | FIBUPDATE yesno {
+ if ($2 == 0)
+ conf->flags |= RIPD_FLAG_NO_FIB_UPDATE;
+ else
+ conf->flags &= ~RIPD_FLAG_NO_FIB_UPDATE;
+ }
+ | REDISTRIBUTE STRING {
+ if (!strcmp($2, "static"))
+ conf->redistribute_flags |=
+ REDISTRIBUTE_STATIC;
+ else if (!strcmp($2, "connected"))
+ conf->redistribute_flags |=
+ REDISTRIBUTE_CONNECTED;
+ else if (!strcmp($2, "default"))
+ conf->redistribute_flags |=
+ REDISTRIBUTE_DEFAULT;
+ else if (!strcmp($2, "none"))
+ conf->redistribute_flags = 0;
+ else {
+ yyerror("unknown redistribute type");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ ;
+
+authmd : AUTHMD number STRING {
+ if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) {
+ yyerror("auth-md key-id out of range "
+ "(%d-%d)", MIN_MD_ID, MAX_MD_ID);
+ free($3);
+ YYERROR;
+ }
+ if (strlen($3) > MD5_DIGEST_LENGTH) {
+ yyerror("auth-md key length out of range "
+ "(max length %d)",
+ MD5_DIGEST_LENGTH);
+ free($3);
+ YYERROR;
+ }
+ md_list_add(&defs->md_list, $2, $3);
+ free($3);
+ }
+
+authmdkeyid : AUTHMDKEYID number {
+ if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) {
+ yyerror("auth-md-keyid out of range "
+ "(%d-%d)", MIN_MD_ID, MAX_MD_ID);
+ YYERROR;
+ }
+ defs->auth_keyid = $2;
+ }
+
+authtype : AUTHTYPE STRING {
+ enum auth_type type;
+
+ if (!strcmp($2, "none"))
+ type = AUTH_NONE;
+ else if (!strcmp($2, "simple"))
+ type = AUTH_SIMPLE;
+ else if (!strcmp($2, "crypt"))
+ type = AUTH_CRYPT;
+ else {
+ yyerror("unknown auth-type");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ defs->auth_type = type;
+ }
+ ;
+
+authkey : AUTHKEY STRING {
+ if (strlen($2) > MAX_SIMPLE_AUTH_LEN) {
+ yyerror("auth-key too long (max lenght %d)",
+ MAX_SIMPLE_AUTH_LEN);
+ free($2);
+ YYERROR;
+ }
+ strncpy(defs->auth_key, $2,
+ sizeof(defs->auth_key));
+ free($2);
+ }
+ ;
+
+defaults : COST number {
+ if ($2 < 1 || $2 > INFINITY) {
+ yyerror("cost out of range (%d-%d)", 1,
+ INFINITY);
+ YYERROR;
+ }
+ defs->cost = $2;
+ }
+ | authtype
+ | authkey
+ | authmdkeyid
+ | authmd
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl
+ ;
+
+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));
+ md_list_copy(&ifacedefs.md_list,
+ &defs->md_list);
+ defs = &ifacedefs;
+ } interface_block {
+ iface->cost = defs->cost;
+ iface->auth_type = defs->auth_type;
+ iface->auth_keyid = defs->auth_keyid;
+ memcpy(iface->auth_key, defs->auth_key,
+ sizeof(iface->auth_key));
+ md_list_copy(&iface->auth_md_list, &defs->md_list);
+ md_list_clr(&defs->md_list);
+ defs = &globaldefs;
+ }
+ ;
+
+interface_block : '{' optnl interfaceopts_l '}'
+ | '{' optnl '}'
+ ;
+
+interfaceopts_l : interfaceopts_l interfaceoptsl
+ | interfaceoptsl
+ ;
+
+interfaceoptsl : PASSIVE nl { iface->passive = 1; }
+ | defaults 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[] = {
+ {"auth-key", AUTHKEY},
+ {"auth-md", AUTHMD},
+ {"auth-md-keyid", AUTHMDKEYID},
+ {"auth-type", AUTHTYPE},
+ {"cost", COST},
+ {"fib-update", FIBUPDATE},
+ {"interface", INTERFACE},
+ {"passive", PASSIVE},
+ {"redistribute", REDISTRIBUTE},
+ {"split-horizon", SPLIT_HORIZON},
+ {"triggered-updates", TRIGGERED_UPDATES}
+ };
+ 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 ripd_conf *
+parse_config(char *filename, int opts)
+{
+ struct sym *sym, *next;
+
+ if ((conf = calloc(1, sizeof(struct ripd_conf))) == NULL)
+ fatal("parse_config");
+
+ bzero(&globaldefs, sizeof(globaldefs));
+ defs = &globaldefs;
+ TAILQ_INIT(&defs->md_list);
+ defs->cost = 1;
+ defs->auth_type = AUTH_NONE;
+
+ if ((fin = fopen(filename, "r")) == NULL) {
+ warn("%s", filename);
+ free(conf);
+ return (NULL);
+ }
+ infile = filename;
+
+ conf->opts = opts;
+ conf->redistribute_flags = 0;
+
+ if (!(conf->opts & RIPD_OPT_NOACTION))
+ 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 & RIPD_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);
+ }
+ }
+
+ /* free global config defaults */
+ md_list_clr(&globaldefs.md_list);
+
+ 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->auth_keyid = 1;
+ i->passive = 0;
+
+ return (i);
+}
+
+void
+clear_config(struct ripd_conf *xconf)
+{
+ struct iface *i;
+
+ while ((i = LIST_FIRST(&conf->iface_list)) != NULL) {
+ LIST_REMOVE(i, entry);
+ if_del(i);
+ }
+
+ free(xconf);
+}
diff --git a/usr.sbin/ripd/printconf.c b/usr.sbin/ripd/printconf.c
new file mode 100644
index 00000000000..a87cde902f3
--- /dev/null
+++ b/usr.sbin/ripd/printconf.c
@@ -0,0 +1,106 @@
+/* $OpenBSD: printconf.c,v 1.1 2006/10/18 16:11:58 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 "rip.h"
+#include "ripd.h"
+#include "ripe.h"
+
+void print_mainconf(struct ripd_conf *);
+void print_iface(struct iface *);
+
+void
+print_mainconf(struct ripd_conf *conf)
+{
+ if (conf->flags |= RIPD_FLAG_NO_FIB_UPDATE)
+ printf("fib-update yes\n");
+ else
+ printf("fib-update no\n");
+
+ if (conf->redistribute_flags & REDISTRIBUTE_STATIC)
+ printf("redistribute static\n");
+ else if (conf->redistribute_flags & REDISTRIBUTE_CONNECTED)
+ printf("redistribute connected\n");
+ else if (conf->redistribute_flags & REDISTRIBUTE_DEFAULT)
+ printf("redistribute default\n");
+ else
+ printf("redistribute none\n");
+
+ if (conf->options & OPT_SPLIT_HORIZON)
+ printf("split-horizon default\n");
+ else if (conf->options & OPT_SPLIT_POISONED)
+ printf("split-horizon poisoned\n");
+ else
+ printf("split-horizon none\n");
+
+ if (conf->options & OPT_TRIGGERED_UPDATES)
+ printf("triggered-updates yes\n");
+ else
+ printf("triggered-updates no\n");
+}
+
+void
+print_iface(struct iface *iface)
+{
+ printf("interface %s {\n", iface->name);
+
+ if (iface->passive)
+ printf("\tpassive\n");
+
+ printf("\tcost %d\n", iface->cost);
+
+ printf("\tauth-type %s\n", if_auth_name(iface->auth_type));
+ switch (iface->auth_type) {
+ case AUTH_NONE:
+ break;
+ case AUTH_SIMPLE:
+ printf("\tauth-key XXXXXX\n");
+ break;
+ case AUTH_CRYPT:
+ break;
+ default:
+ printf("\tunknown auth type!\n");
+ break;
+ }
+
+ printf("}\n");
+}
+
+void
+print_config(struct ripd_conf *conf)
+{
+ struct iface *iface;
+
+ printf("\n");
+ print_mainconf(conf);
+ printf("\n");
+
+ LIST_FOREACH(iface, &conf->iface_list, entry) {
+ printf("ooo\n");
+ print_iface(iface);
+ }
+ printf("\n");
+
+}
diff --git a/usr.sbin/ripd/rde.c b/usr.sbin/ripd/rde.c
new file mode 100644
index 00000000000..262eff65309
--- /dev/null
+++ b/usr.sbin/ripd/rde.c
@@ -0,0 +1,455 @@
+/* $OpenBSD: rde.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
+ * 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/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 "ripd.h"
+#include "rip.h"
+#include "ripe.h"
+#include "log.h"
+#include "rde.h"
+
+struct ripd_conf *rdeconf = NULL;
+struct imsgbuf *ibuf_ripe;
+struct imsgbuf *ibuf_main;
+
+void rde_sig_handler(int, short, void *);
+void rde_shutdown(void);
+void rde_dispatch_imsg(int, short, void *);
+void rde_dispatch_parent(int, short, void *);
+int rde_imsg_compose_parent(int, pid_t, void *, u_int16_t);
+int rde_imsg_compose_ripe(int, u_int32_t, pid_t, void *, u_int16_t);
+int rde_check_route(struct rip_route *);
+void triggered_update(struct rt_node *);
+
+/* ARGSUSED */
+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");
+ }
+}
+
+/* route decision engine */
+pid_t
+rde(struct ripd_conf *xconf, int pipe_parent2rde[2], int pipe_ripe2rde[2],
+ int pipe_parent2ripe[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(RIPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("route decision engine");
+ ripd_process = PROC_RDE_ENGINE;
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ 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_ripe2rde[0]);
+ close(pipe_parent2rde[0]);
+ close(pipe_parent2ripe[0]);
+ close(pipe_parent2ripe[1]);
+
+ if ((ibuf_ripe = malloc(sizeof(struct imsgbuf))) == NULL ||
+ (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_ripe, pipe_ripe2rde[1], rde_dispatch_imsg);
+ imsg_init(ibuf_main, pipe_parent2rde[1], rde_dispatch_parent);
+
+ /* setup event handler */
+ ibuf_ripe->events = EV_READ;
+ event_set(&ibuf_ripe->ev, ibuf_ripe->fd, ibuf_ripe->events,
+ ibuf_ripe->handler, ibuf_ripe);
+ event_add(&ibuf_ripe->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();
+ event_dispatch();
+
+ rde_shutdown();
+ /* NOTREACHED */
+
+ return (0);
+}
+
+void
+rde_shutdown(void)
+{
+ rt_clear();
+
+ msgbuf_clear(&ibuf_ripe->w);
+ free(ibuf_ripe);
+ 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_ripe(int type, u_int32_t peerid, pid_t pid, void *data,
+ u_int16_t datalen)
+{
+ return (imsg_compose(ibuf_ripe, type, peerid, pid, data, datalen));
+}
+
+/* ARGSUSED */
+void
+rde_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct imsgbuf *ibuf = bula;
+ struct rip_route rr;
+ 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("rde_dispatch_imsg: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_ROUTE_FEED:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr))
+ fatalx("invalid size of RDE request");
+
+ memcpy(&rr, imsg.data, sizeof(rr));
+
+ if (rde_check_route(&rr) == -1)
+ log_debug("rde_dispatch_imsg: "
+ "packet malformed\n");
+ break;
+ case IMSG_FULL_REQUEST:
+ bzero(&rr, sizeof(rr));
+ /* AFI == 0 && metric == INFINITY request the
+ whole routing table
+ */
+ rr.metric = INFINITY;
+ rde_imsg_compose_ripe(IMSG_REQUEST_ADD, 0,
+ 0, &rr, sizeof(rr));
+ rde_imsg_compose_ripe(IMSG_SEND_REQUEST, 0,
+ 0, NULL, 0);
+ break;
+ case IMSG_FULL_RESPONSE:
+ rt_snap(imsg.hdr.peerid);
+ rde_imsg_compose_ripe(IMSG_SEND_RESPONSE,
+ imsg.hdr.peerid, 0, NULL, 0);
+ break;
+ case IMSG_ROUTE_REQUEST:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr))
+ fatalx("invalid size of RDE request");
+
+ memcpy(&rr, imsg.data, sizeof(rr));
+
+ rt_complete(&rr);
+ rde_imsg_compose_ripe(IMSG_RESPONSE_ADD,
+ imsg.hdr.peerid, 0, &rr, sizeof(rr));
+
+ break;
+ case IMSG_ROUTE_REQUEST_END:
+ rde_imsg_compose_ripe(IMSG_SEND_RESPONSE,
+ imsg.hdr.peerid, 0, NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_RIB:
+ rt_dump(imsg.hdr.pid);
+
+ imsg_compose(ibuf_ripe, IMSG_CTL_END, 0, imsg.hdr.pid,
+ NULL, 0);
+
+ break;
+ default:
+ log_debug("rde_dispatch_msg: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+/* ARGSUSED */
+void
+rde_dispatch_parent(int fd, short event, void *bula)
+{
+ struct imsg imsg;
+ struct rt_node *rt;
+ struct kroute kr;
+ struct imsgbuf *ibuf = bula;
+ ssize_t 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("rde_dispatch_parent: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_NETWORK_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(kr)) {
+ log_warnx("rde_dispatch: wrong imsg len");
+ break;
+ }
+
+ memcpy(&kr, imsg.data, sizeof(kr));
+
+ rt = rt_new_kr(&kr);
+ rt_insert(rt);
+ break;
+ case IMSG_NETWORK_DEL:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(kr)) {
+ log_warnx("rde_dispatch: wrong imsg len");
+ break;
+ }
+ memcpy(&kr, imsg.data, sizeof(kr));
+
+ if ((rt = rt_find(kr.prefix.s_addr,
+ kr.netmask.s_addr)) != NULL)
+ rt_remove(rt);
+ break;
+ case IMSG_KROUTE_GET:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(kr)) {
+ log_warnx("rde_dispatch: wrong imsg len");
+ break;
+ }
+ memcpy(&kr, imsg.data, sizeof(kr));
+
+ if ((rt = rt_find(kr.prefix.s_addr,
+ kr.netmask.s_addr)) != NULL)
+ rde_send_change_kroute(rt);
+ else
+ /* should not happen */
+ imsg_compose(ibuf_main, IMSG_KROUTE_DELETE, 0,
+ 0, &kr, sizeof(kr));
+
+ break;
+ default:
+ log_debug("rde_dispatch_parent: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+rde_send_change_kroute(struct rt_node *r)
+{
+ struct kroute kr;
+
+ bzero(&kr, sizeof(kr));
+ kr.prefix.s_addr = r->prefix.s_addr;
+ kr.nexthop.s_addr = r->nexthop.s_addr;
+ kr.netmask.s_addr = r->netmask.s_addr;
+ kr.metric = r->metric;
+ kr.flags = r->flags;
+ kr.ifindex = r->ifindex;
+
+ imsg_compose(ibuf_main, IMSG_KROUTE_CHANGE, 0, 0, &kr, sizeof(kr));
+}
+
+void
+rde_send_delete_kroute(struct rt_node *r)
+{
+ struct kroute kr;
+
+ bzero(&kr, sizeof(kr));
+ kr.prefix.s_addr = r->prefix.s_addr;
+ kr.nexthop.s_addr = r->nexthop.s_addr;
+ kr.netmask.s_addr = r->netmask.s_addr;
+ kr.metric = r->metric;
+ kr.flags = r->flags;
+ kr.ifindex = r->ifindex;
+
+ imsg_compose(ibuf_main, IMSG_KROUTE_DELETE, 0, 0, &kr, sizeof(kr));
+}
+
+int
+rde_check_route(struct rip_route *e)
+{
+ struct rt_node *rn;
+ struct iface *iface;
+ int metric;
+
+ if ((e->nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
+ htonl(INADDR_LOOPBACK & IN_CLASSA_NET) ||
+ (e->nexthop.s_addr == INADDR_ANY))
+ return (-1);
+
+ if ((e->address.s_addr & htonl(IN_CLASSA_NET)) ==
+ htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
+ return (-1);
+
+ if (e->metric > INFINITY)
+ return (-1);
+
+ if ((iface = if_find_index(e->ifindex)) == NULL)
+ return (-1);
+
+ metric = MIN(INFINITY, e->metric + iface->cost);
+ if (metric >= INFINITY)
+ return (0);
+
+ if ((rn = rt_find(e->address.s_addr, e->mask.s_addr)) == NULL) {
+ rn = rt_new_rr(e, metric);
+ rt_insert(rn);
+ rde_send_change_kroute(rn);
+ route_start_timeout(rn);
+ triggered_update(rn);
+ } else {
+ /*
+ * XXX don't we have to track all incomming routes?
+ * what happens if the kernel route is removed later.
+ */
+ if (rn->flags & F_KERNEL)
+ return (0);
+
+ if (metric < rn->metric) {
+ rn->metric = metric;
+ rn->nexthop.s_addr = e->nexthop.s_addr;
+ rn->ifindex = e->ifindex;
+ rde_send_change_kroute(rn);
+ triggered_update(rn);
+ } else if (e->nexthop.s_addr == rn->nexthop.s_addr &&
+ e->metric > metric) {
+ rn->metric = metric;
+ rde_send_change_kroute(rn);
+ triggered_update(rn);
+ }
+
+ if (e->nexthop.s_addr == rn->nexthop.s_addr)
+ route_reset_timers(rn);
+ }
+
+ return (0);
+}
+
+void
+triggered_update(struct rt_node *rn)
+{
+ struct rip_route rr;
+
+ rr.address.s_addr = rn->prefix.s_addr;
+ rr.mask.s_addr = rn->netmask.s_addr;
+ rr.nexthop.s_addr = rn->nexthop.s_addr;
+ rr.metric = rn->metric;
+ rr.ifindex = rn->ifindex;
+
+ rde_imsg_compose_ripe(IMSG_SEND_TRIGGERED_UPDATE, 0, 0, &rr,
+ sizeof(struct rip_route));
+}
diff --git a/usr.sbin/ripd/rde.h b/usr.sbin/ripd/rde.h
new file mode 100644
index 00000000000..f27d561a8ae
--- /dev/null
+++ b/usr.sbin/ripd/rde.h
@@ -0,0 +1,64 @@
+/* $OpenBSD: rde.h,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * Copyright (c) 2004, 2005 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 timeout_timer;
+ struct event garbage_timer;
+ struct in_addr prefix;
+ struct in_addr netmask;
+ struct in_addr nexthop;
+ u_short ifindex;
+ u_int16_t flags;
+ u_int8_t metric;
+};
+
+/* rde.c */
+pid_t rde(struct ripd_conf *, int [2], int [2], int [2]);
+void rde_send_change_kroute(struct rt_node *);
+void rde_send_delete_kroute(struct rt_node *);
+int rde_imsg_compose_ripe(int, u_int32_t, pid_t, void *,
+ u_int16_t);
+
+/* rde_routes.c */
+void rt_init(void);
+int rt_compare(struct rt_node *, struct rt_node *);
+struct rt_node *rt_find(in_addr_t, in_addr_t);
+struct rt_node *rt_new_kr(struct kroute *);
+struct rt_node *rt_new_rr(struct rip_route *, int);
+int rt_insert(struct rt_node *);
+int rt_remove(struct rt_node *);
+void rt_complete(struct rip_route *);
+void rt_snap(u_int32_t);
+void rt_clear(void);
+void route_reset_timers(struct rt_node *);
+int route_start_timeout(struct rt_node *);
+void rt_dump(pid_t);
+
+#endif /* _RDE_H_ */
diff --git a/usr.sbin/ripd/rde_rib.c b/usr.sbin/ripd/rde_rib.c
new file mode 100644
index 00000000000..e26292ee953
--- /dev/null
+++ b/usr.sbin/ripd/rde_rib.c
@@ -0,0 +1,258 @@
+/* $OpenBSD: rde_rib.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ *
+ * 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 <net/if_types.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <event.h>
+
+#include "ripd.h"
+#include "rip.h"
+#include "log.h"
+#include "rde.h"
+
+extern struct ripd_conf *rdeconf;
+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)
+
+void route_action_timeout(int, short, void *);
+void route_action_garbage(int, short, void *);
+int route_start_timeout(struct rt_node *);
+
+/* timers */
+int
+route_start_timeout(struct rt_node *rn)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = ROUTE_TIMEOUT;
+
+ return (evtimer_add(&rn->timeout_timer, &tv));
+}
+
+/* ARGSUSED */
+void
+route_action_timeout(int fd, short event, void *arg)
+{
+ struct rt_node *r = arg;
+ struct timeval tv;
+
+ timerclear(&tv);
+ r->metric = INFINITY;
+ tv.tv_sec = ROUTE_GARBAGE;
+
+ if (evtimer_add(&r->garbage_timer, &tv) == -1)
+ fatal("route_action_timeout");
+
+ rde_send_change_kroute(r);
+}
+
+/* ARGSUSED */
+void
+route_action_garbage(int fd, short event, void *arg)
+{
+ struct rt_node *r = arg;
+
+ rde_send_delete_kroute(r);
+ rt_remove(r);
+}
+
+void
+route_reset_timers(struct rt_node *r)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = ROUTE_TIMEOUT;
+ evtimer_del(&r->timeout_timer);
+ evtimer_del(&r->garbage_timer);
+
+ event_add(&r->timeout_timer, &tv);
+}
+
+/* route table */
+void
+rt_init(void)
+{
+ RB_INIT(&rt);
+}
+
+int
+rt_compare(struct rt_node *a, struct rt_node *b)
+{
+ 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);
+ if (ntohl(a->netmask.s_addr) < ntohl(b->netmask.s_addr))
+ return (-1);
+ if (ntohl(a->netmask.s_addr) > ntohl(b->netmask.s_addr))
+ return (1);
+
+ return (0);
+}
+
+struct rt_node *
+rt_find(in_addr_t prefix, in_addr_t netmask)
+{
+ struct rt_node s;
+
+ s.prefix.s_addr = prefix;
+ s.netmask.s_addr = netmask;
+
+ return (RB_FIND(rt_tree, &rt, &s));
+}
+
+struct rt_node *
+rt_new_kr(struct kroute *kr)
+{
+ struct rt_node *rn;
+
+ if ((rn = calloc(1, sizeof(*rn))) == NULL)
+ fatal("rt_new_kr");
+
+ evtimer_set(&rn->timeout_timer, route_action_timeout, rn);
+ evtimer_set(&rn->garbage_timer, route_action_garbage, rn);
+
+ rn->prefix.s_addr = kr->prefix.s_addr;
+ rn->netmask.s_addr = kr->netmask.s_addr;
+ rn->nexthop.s_addr = kr->nexthop.s_addr;
+ rn->metric = kr->metric;
+ rn->ifindex = kr->ifindex;
+ rn->flags = F_KERNEL;
+
+ return (rn);
+}
+
+struct rt_node *
+rt_new_rr(struct rip_route *e, int metric)
+{
+ struct rt_node *rn;
+
+ if ((rn = calloc(1, sizeof(*rn))) == NULL)
+ fatal("rt_new_rr");
+
+ evtimer_set(&rn->timeout_timer, route_action_timeout, rn);
+ evtimer_set(&rn->garbage_timer, route_action_garbage, rn);
+
+ rn->prefix.s_addr = e->address.s_addr;
+ rn->netmask.s_addr = e->mask.s_addr;
+ rn->nexthop.s_addr = e->nexthop.s_addr;
+ rn->metric = metric;
+ rn->ifindex = e->ifindex;
+ rn->flags = F_RIPD_INSERTED;
+
+ return (rn);
+}
+
+int
+rt_insert(struct rt_node *r)
+{
+ if (RB_INSERT(rt_tree, &rt, r) != NULL) {
+ log_warnx("rt_insert failed for %s/%s",
+ inet_ntoa(r->prefix), inet_ntoa(r->netmask));
+ 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/%s",
+ inet_ntoa(r->prefix), inet_ntoa(r->netmask));
+ return (-1);
+ }
+
+ free(r);
+ return (0);
+}
+
+void
+rt_snap(u_int32_t peerid)
+{
+ struct rt_node *r;
+ struct rip_route rr;
+
+ bzero(&rr, sizeof(rr));
+
+ RB_FOREACH(r, rt_tree, &rt) {
+ rr.address = r->prefix;
+ rr.mask = r->netmask;
+ rr.nexthop = r->nexthop;
+ rr.metric = r->metric;
+ rr.ifindex = r->ifindex;
+
+ rde_imsg_compose_ripe(IMSG_RESPONSE_ADD, peerid, 0, &rr,
+ sizeof(rr));
+ }
+}
+
+void
+rt_dump(pid_t pid)
+{
+ struct rt_node *r;
+ static struct ctl_rt rtctl;
+
+ RB_FOREACH(r, rt_tree, &rt) {
+ rtctl.prefix.s_addr = r->prefix.s_addr;
+ rtctl.netmask.s_addr = r->netmask.s_addr;
+ rtctl.nexthop.s_addr = r->nexthop.s_addr;
+ rtctl.metric = r->metric;
+ rtctl.flags = r->flags;
+
+ rde_imsg_compose_ripe(IMSG_CTL_SHOW_RIB, 0, pid, &rtctl,
+ sizeof(rtctl));
+ }
+}
+
+void
+rt_complete(struct rip_route *rr)
+{
+ struct rt_node *rn;
+
+ if ((rn = rt_find(rr->address.s_addr, rr->mask.s_addr)) == NULL)
+ rr->metric = INFINITY;
+ else
+ rr->metric = rn->metric;
+}
+
+void
+rt_clear(void)
+{
+ struct rt_node *r;
+
+ while ((r = RB_MIN(rt_tree, &rt)) != NULL)
+ rt_remove(r);
+}
diff --git a/usr.sbin/ripd/rip.h b/usr.sbin/ripd/rip.h
new file mode 100644
index 00000000000..a1b2a3c3d91
--- /dev/null
+++ b/usr.sbin/ripd/rip.h
@@ -0,0 +1,82 @@
+/* $OpenBSD: rip.h,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ *
+ * 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.
+ */
+
+/* RIP protocol definitions */
+
+#ifndef _RIP_H_
+#define _RIP_H_
+
+/* misc */
+#define RIP_VERSION 2
+#define ALL_RIP_ROUTERS "224.0.0.9"
+#define RIP_PORT 520
+
+#define MIN_MD_ID 0
+#define MAX_MD_ID 255
+
+/* metric */
+#define INFINITY 16
+
+/* timers */
+#define KEEPALIVE 30
+#define OFFSET 10
+
+#define MAX_RIP_ENTRIES 25
+
+/* RIP command */
+#define COMMAND_REQUEST 1
+#define COMMAND_RESPONSE 2
+
+#define RIP_HDR_LEN sizeof(struct rip_hdr)
+#define RIP_ENTRY_LEN sizeof(struct rip_entry)
+
+struct rip_hdr {
+ u_int8_t command;
+ u_int8_t version;
+ u_int16_t dummy;
+};
+
+struct rip_entry {
+ u_int16_t AFI;
+ u_int16_t route_tag;
+ u_int32_t address;
+ u_int32_t mask;
+ u_int32_t nexthop;
+ u_int32_t metric;
+};
+
+/* auth */
+#define AUTH 0xFFFF
+#define AUTH_TRLR_HDR_LEN 4
+
+/* auth general struct */
+struct rip_auth {
+ u_int16_t auth_fixed;
+ u_int16_t auth_type;
+};
+
+/* Keyed MD5 auth struct */
+struct md5_auth {
+ u_int16_t auth_offset;
+ u_int8_t auth_keyid;
+ u_int8_t auth_length;
+ u_int32_t auth_seq;
+ u_int64_t auth_reserved;
+};
+
+#endif /* _RIP_H_ */
diff --git a/usr.sbin/ripd/ripd.8 b/usr.sbin/ripd/ripd.8
new file mode 100644
index 00000000000..42092db8d0d
--- /dev/null
+++ b/usr.sbin/ripd/ripd.8
@@ -0,0 +1,81 @@
+.\" $OpenBSD: ripd.8,v 1.1 2006/10/18 16:11:58 norby Exp $
+.\"
+.\" Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+.\" 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 RIPD 8
+.Os
+.Sh NAME
+.Nm ripd
+.Nd "Routing Information Protocol daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnv
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is the Routing Information Protocol
+.Pq RIP
+daemon which manages routers' routing tables.
+For RIPD to work,
+multicast routing must be enabled by setting multicast_router=YES in
+.Pa /etc/rc.conf.local .
+.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/ripd.sockXX" -compact
+.It /etc/ripd.conf
+Default
+.Nm
+configuration file.
+.It /var/run/ripd.sock
+Unix-domain socket used for communication with
+.Xr ripctl 8 .
+.El
+.Sh SEE ALSO
+.Xr ripd.conf 5 ,
+.Xr rc.conf 8 ,
+.Xr ripctl 8
+.Rs
+.%R RFC 2453
+.%T RIP Version 2
+.%D November 1998
+.Re
+.Rs
+.%R RFC 2082
+.%T "RIP-2 MD5 Authentication"
+.%D January 1997
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 4.0 .
diff --git a/usr.sbin/ripd/ripd.conf.5 b/usr.sbin/ripd/ripd.conf.5
new file mode 100644
index 00000000000..7a93b144bf8
--- /dev/null
+++ b/usr.sbin/ripd/ripd.conf.5
@@ -0,0 +1,195 @@
+.\" $OpenBSD: ripd.conf.5,v 1.1 2006/10/18 16:11:58 norby Exp $
+.\"
+.\" Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+.\" 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 RIPD.CONF 5
+.Os
+.Sh NAME
+.Nm ripd.conf
+.Nd Routing Information Protocol daemon configuration file
+.Sh DESCRIPTION
+The
+.Xr ripd 8
+daemon implements the Routing Information Protocol version 2 as described
+in rfc 2453.
+.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 ripd 8 .
+.It Sy Interfaces Configuration
+Interface specific parameters.
+.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 cost ) .
+Macros are not expanded inside quotes.
+.Pp
+For example:
+.Bd -literal -offset indent
+hi="2"
+interface em0 {
+ cost $hi
+}
+.Ed
+.Sh GLOBAL CONFIGURATION
+Global settings concerns the main behaviour of the daemon.
+.Bl -tag -width Ds
+.It Xo
+.Ic fib-update
+.Pq Ic yes Ns \&| Ns Ic no
+.Xc
+If set to
+.Ic no ,
+do not update the Forwarding Information Base, a.k.a. the kernel routing
+table.
+The default is
+.Ic yes .
+.Pp
+.It Xo
+.Ic redistribute
+.Sm off
+.Po Ic static Ns \&| Ns Ic connected Ns \&| Ns
+.Ic default Ns \&|
+.Ic none Pc
+.Sm on
+.Xc
+If set to
+.Ic connected ,
+routes to directly attached networks will be
+announced over RIP.
+If set to
+.Ic static ,
+static routes will be announced over RIP.
+If set to
+.Ic default ,
+a default route pointing to this router will be announced over RIP.
+By default no additional routes will be announced over RIP.
+.Pp
+.It Xo
+.Ic split-horizon
+.Sm off
+.Po Ic default Ns \&| Ns Ic poisoned Ns \&| Ns
+.Ic none Pc
+.Sm on
+.Xc
+If set to
+.Ic default ,
+do not redistribute routes to the interface from which they were learned.
+If set to
+.Ic poisoned ,
+redistribute routes to the interface from which they were learned, but
+forcing the metric to infinity.
+Both these behaviours can resolve routing loops when a router goes down.
+The default is
+.Ic none .
+.Pp
+.It Xo
+.Ic triggered-updates
+.Pq Ic yes Ns \&| Ns Ic no
+.Xc
+Immediately trigger responses when a route changes.
+The default is
+.Ic yes .
+.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 auth-key Ar key
+Set the authentication key for
+.Ic simple
+authentication.
+Up to 16 characters can be specified.
+.It Ic auth-md Ar key-id key
+Set the authentication
+.Ar key-id
+and
+.Ar key
+for
+.Ic crypt
+authentication.
+The valid range for
+.Ar key-id
+is 0\-255.
+Up to 16 characters can be specified for
+.Ar key .
+Multiple keys may be specified.
+.It Ic auth-md-keyid Ar key-id
+Configure the
+.Ar key-id
+to use for
+.Ic crypt
+authentication.
+The valid range for
+.Ar key-id
+is 0\-255.
+The default key-id is 1.
+.It Xo
+.Ic auth-type
+.Po Ic none Ns \&| Ns
+.Ic simple Ns \&| Ns Ic crypt Pc
+.Xc
+Set the authentication type.
+The default is
+.Ic none .
+Simple authentication uses a plaintext password, up to 16 characters.
+Crypt authentication uses an MD5 hash.
+.It Ic cost Ar number
+Cost of the network connected through this interface.
+Values must be between 1 and 16, inclusive.
+.It Ic passive
+Prevent transmission and reception of RIP packets on this interface.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/ripd.conf" -compact
+.It Pa /etc/ripd.conf
+.Xr ripd 8
+configuration file
+.El
+.Sh SEE ALSO
+.Xr rc.conf.local 8 ,
+.Xr ripctl 8 ,
+.Xr ripd 8
+.Rs
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 4.0 .
diff --git a/usr.sbin/ripd/ripd.h b/usr.sbin/ripd/ripd.h
new file mode 100644
index 00000000000..12f143437e2
--- /dev/null
+++ b/usr.sbin/ripd/ripd.h
@@ -0,0 +1,392 @@
+/* $OpenBSD: ripd.h,v 1.1 2006/10/18 16:11:58 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.
+ */
+
+#ifndef _RIPD_H_
+#define _RIPD_H_
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+#include <md5.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <event.h>
+
+#define CONF_FILE "/etc/ripd.conf"
+#define RIPD_SOCKET "/var/run/ripd.sock"
+#define RIPD_USER "_ripd"
+#define READ_BUF_SIZE 65535
+
+#define NBR_HASHSIZE 128
+#define NBR_IDSELF 1
+#define NBR_CNTSTART (NBR_IDSELF + 1)
+
+#define ROUTE_TIMEOUT 180
+#define ROUTE_GARBAGE 120
+
+#define NBR_TIMEOUT 180
+
+#define READ_BUF_SIZE 65535
+#define RT_BUF_SIZE 16384
+#define MAX_RTSOCK_BUF 128 * 1024
+
+#define RIPD_FLAG_NO_FIB_UPDATE 0x0001
+
+#define F_RIPD_INSERTED 0x0001
+#define F_KERNEL 0x0002
+#define F_BGPD_INSERTED 0x0004
+#define F_CONNECTED 0x0008
+#define F_DOWN 0x0010
+#define F_STATIC 0x0020
+#define F_DYNAMIC 0x0040
+#define F_OSPFD_INSERTED 0x0080
+#define F_REDISTRIBUTED 0x0100
+
+#define REDISTRIBUTE_NONE 0x0
+#define REDISTRIBUTE_STATIC 0x01
+#define REDISTRIBUTE_CONNECTED 0x02
+#define REDISTRIBUTE_DEFAULT 0x04
+
+#define OPT_SPLIT_HORIZON 0x01
+#define OPT_SPLIT_POISONED 0x02
+#define OPT_TRIGGERED_UPDATES 0x04
+
+/* buffer */
+struct buf {
+ TAILQ_ENTRY(buf) entry;
+ u_char *buf;
+ size_t size;
+ size_t max;
+ size_t wpos;
+ size_t rpos;
+};
+
+struct msgbuf {
+ TAILQ_HEAD(, buf) bufs;
+ u_int32_t queued;
+ int fd;
+};
+
+#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_END,
+ IMSG_CTL_RELOAD,
+ IMSG_CTL_IFINFO,
+ IMSG_IFINFO,
+ IMSG_CTL_FIB_COUPLE,
+ IMSG_CTL_FIB_DECOUPLE,
+ IMSG_CTL_KROUTE,
+ IMSG_CTL_KROUTE_ADDR,
+ IMSG_CTL_SHOW_INTERFACE,
+ IMSG_CTL_SHOW_IFACE,
+ IMSG_CTL_SHOW_NBR,
+ IMSG_CTL_SHOW_RIB,
+ IMSG_KROUTE_CHANGE,
+ IMSG_KROUTE_DELETE,
+ IMSG_KROUTE_GET,
+ IMSG_NETWORK_ADD,
+ IMSG_NETWORK_DEL,
+ IMSG_ROUTE_FEED,
+ IMSG_RESPONSE_ADD,
+ IMSG_SEND_RESPONSE,
+ IMSG_FULL_RESPONSE,
+ IMSG_ROUTE_REQUEST,
+ IMSG_ROUTE_REQUEST_END,
+ IMSG_FULL_REQUEST,
+ IMSG_REQUEST_ADD,
+ IMSG_SEND_REQUEST,
+ IMSG_SEND_TRIGGERED_UPDATE
+};
+
+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_ACTIVE (~IF_STA_DOWN)
+#define IF_STA_ANY 0x7f
+
+/* interface events */
+enum iface_event {
+ IF_EVT_NOTHING,
+ IF_EVT_UP,
+ IF_EVT_DOWN
+};
+
+/* interface actions */
+enum iface_action {
+ IF_ACT_NOTHING,
+ IF_ACT_STRT,
+ IF_ACT_RST
+};
+
+/* interface types */
+enum iface_type {
+ IF_TYPE_POINTOPOINT,
+ IF_TYPE_BROADCAST,
+ IF_TYPE_NBMA,
+ IF_TYPE_POINTOMULTIPOINT
+};
+
+/* neighbor states */
+#define NBR_STA_DOWN 0x01
+#define NBR_STA_ACTIVE (~NBR_STA_DOWN)
+#define NBR_STA_ANY 0xff
+
+struct auth_md {
+ TAILQ_ENTRY(auth_md) entry;
+ char key[MD5_DIGEST_LENGTH];
+ u_int8_t keyid;
+};
+
+#define MAX_SIMPLE_AUTH_LEN 16
+
+/* auth types */
+enum auth_type {
+ AUTH_NONE = 1,
+ AUTH_SIMPLE,
+ AUTH_CRYPT
+};
+
+TAILQ_HEAD(auth_md_head, auth_md);
+TAILQ_HEAD(packet_head, packet_entry);
+
+struct iface {
+ LIST_ENTRY(iface) entry;
+ LIST_HEAD(, nbr) nbr_list;
+ LIST_HEAD(, nbr_failed) failed_nbr_list;
+ char name[IF_NAMESIZE];
+ char auth_key[MAX_SIMPLE_AUTH_LEN];
+ struct in_addr addr;
+ struct in_addr dst;
+ struct in_addr mask;
+ struct packet_head rq_list;
+ struct packet_head rp_list;
+ struct auth_md_head auth_md_list;
+
+ time_t uptime;
+ u_long mtu;
+ u_long baudrate;
+ int fd; /* XXX */
+ int state;
+ u_short ifindex;
+ u_int16_t cost;
+ u_int16_t flags;
+ enum iface_type type;
+ enum auth_type auth_type;
+ u_int8_t linktype;
+ u_int8_t media_type;
+ u_int8_t passive;
+ u_int8_t linkstate;
+ u_int8_t auth_keyid;
+};
+
+struct rip_route {
+ struct in_addr address;
+ struct in_addr mask;
+ struct in_addr nexthop;
+ int refcount;
+ u_short ifindex;
+ u_int8_t metric;
+};
+
+struct packet_entry {
+ TAILQ_ENTRY(packet_entry) entry;
+ struct rip_route *rr;
+};
+
+enum {
+ PROC_MAIN,
+ PROC_RIP_ENGINE,
+ PROC_RDE_ENGINE
+} ripd_process;
+
+struct ripd_conf {
+ struct event ev;
+ struct event report_timer;
+ LIST_HEAD(, iface) iface_list;
+ u_int32_t opts;
+#define RIPD_OPT_VERBOSE 0x00000001
+#define RIPD_OPT_VERBOSE2 0x00000002
+#define RIPD_OPT_NOACTION 0x00000004
+ int flags;
+ int redistribute_flags;
+ int options;
+ int rip_socket;
+};
+
+/* kroute */
+struct kroute {
+ struct in_addr prefix;
+ struct in_addr netmask;
+ struct in_addr nexthop;
+ u_int8_t metric;
+ u_int16_t flags;
+ u_short ifindex;
+};
+
+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;
+
+ time_t uptime;
+ time_t report_timer;
+
+ u_int32_t baudrate;
+
+ unsigned int ifindex;
+ int state;
+ int mtu;
+
+ u_int16_t flags;
+ u_int16_t metric;
+ enum iface_type type;
+ u_int8_t linkstate;
+ u_int8_t mediatype;
+ u_int8_t passive;
+};
+
+struct ctl_rt {
+ struct in_addr prefix;
+ struct in_addr netmask;
+ struct in_addr nexthop;
+ time_t uptime;
+ time_t expire;
+ u_int32_t metric;
+ u_int8_t flags;
+};
+
+struct ctl_nbr {
+ char name[IF_NAMESIZE];
+ struct in_addr id;
+ struct in_addr addr;
+ time_t dead_timer;
+ time_t uptime;
+ int nbr_state;
+ int iface_state;
+};
+
+int kif_init(void);
+int kr_init(int);
+int kr_change(struct kroute *);
+int kr_delete(struct kroute *);
+void kr_shutdown(void);
+void kr_fib_couple(void);
+void kr_fib_decouple(void);
+void kr_dispatch_msg(int, short, void *);
+void kr_show_route(struct imsg *);
+void kr_ifinfo(char *, pid_t);
+struct kif *kif_findname(char *);
+
+in_addr_t prefixlen2mask(u_int8_t);
+u_int8_t mask2prefixlen(in_addr_t);
+
+/* ripd.c */
+void main_imsg_compose_ripe(int, pid_t, void *, u_int16_t);
+void main_imsg_compose_rde(int, pid_t, void *, u_int16_t);
+int rip_redistribute(struct kroute *);
+
+/* parse.y */
+struct ripd_conf *parse_config(char *, int);
+int cmdline_symset(char *);
+
+/* 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 *);
+
+/* 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 *);
+
+/* printconf.c */
+void print_config(struct ripd_conf *);
+
+/* log.c */
+const char *if_state_name(int);
+const char *if_auth_name(enum auth_type);
+const char *nbr_state_name(int);
+
+/* interface.c */
+struct iface *if_find_index(u_short);
+
+#endif /* _RIPD_H_ */
diff --git a/usr.sbin/ripd/ripe.c b/usr.sbin/ripd/ripe.c
new file mode 100644
index 00000000000..3f651bb5411
--- /dev/null
+++ b/usr.sbin/ripd/ripe.c
@@ -0,0 +1,504 @@
+/* $OpenBSD: ripe.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * 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/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if_types.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 "ripd.h"
+#include "rip.h"
+#include "ripe.h"
+#include "log.h"
+#include "control.h"
+
+void ripe_sig_handler(int, short, void *);
+void ripe_shutdown(void);
+
+struct ripd_conf *oeconf = NULL;
+struct imsgbuf *ibuf_main;
+struct imsgbuf *ibuf_rde;
+
+/* ARGSUSED */
+void
+ripe_sig_handler(int sig, short event, void *bula)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ ripe_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+/* rip engine */
+pid_t
+ripe(struct ripd_conf *xconf, int pipe_parent2ripe[2], int pipe_ripe2rde[2],
+ int pipe_parent2rde[2])
+{
+ struct iface *iface = NULL;
+ struct sockaddr_in addr;
+ struct passwd *pw;
+ struct event ev_sigint, ev_sigterm;
+ pid_t pid;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ /* create ripd control socket outside chroot */
+ if (control_init() == -1)
+ fatalx("control socket setup failed");
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(RIP_PORT);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if ((xconf->rip_socket = socket(AF_INET, SOCK_DGRAM,
+ IPPROTO_UDP)) == -1)
+ fatalx("if_init: cannot create socket");
+
+ if (bind(xconf->rip_socket, (struct sockaddr *)&addr,
+ sizeof(addr)) == -1)
+ fatal("error creating socket");
+
+ /* set some defaults */
+ if (if_set_opt(xconf->rip_socket) == -1)
+ fatal("if_set_opt");
+
+ if (if_set_mcast_ttl(xconf->rip_socket, IP_DEFAULT_MULTICAST_TTL) == -1)
+ fatal("if_set_mcast_ttl");
+
+ if (if_set_mcast_loop(xconf->rip_socket) == -1)
+ fatal("if_set_mcast_loop");
+
+ if (if_set_tos(xconf->rip_socket, IPTOS_PREC_INTERNETCONTROL) == -1)
+ fatal("if_set_tos");
+
+ if_set_recvbuf(xconf->rip_socket);
+
+ oeconf = xconf;
+
+ if ((pw = getpwnam(RIPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("rip engine");
+ ripd_process = PROC_RIP_ENGINE;
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ event_init();
+ nbr_init(NBR_HASHSIZE);
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, ripe_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, ripe_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* setup pipes */
+ close(pipe_parent2ripe[0]);
+ close(pipe_ripe2rde[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_ripe2rde[0], ripe_dispatch_rde);
+ imsg_init(ibuf_main, pipe_parent2ripe[1], ripe_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(&oeconf->ev, oeconf->rip_socket, EV_READ|EV_PERSIST,
+ recv_packet, oeconf);
+ event_add(&oeconf->ev, NULL);
+
+ /* listen on ripd control socket */
+ TAILQ_INIT(&ctl_conns);
+ control_listen();
+
+ if ((pkt_ptr = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("ripe");
+
+ /* start interfaces */
+ LIST_FOREACH(iface, &xconf->iface_list, entry) {
+ if_init(xconf, iface);
+ if (if_fsm(iface, IF_EVT_UP))
+ log_debug("error starting interface: %s", iface->name);
+ }
+
+ evtimer_set(&oeconf->report_timer, report_timer, oeconf);
+ start_report_timer();
+
+ ripe_imsg_compose_rde(IMSG_FULL_REQUEST, 0, 0, NULL, 0);
+
+ event_dispatch();
+
+ ripe_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+/* imesg */
+int
+ripe_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
+ripe_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));
+}
+
+/* ARGSUSED */
+void
+ripe_dispatch_main(int fd, short event, void *bula)
+{
+ struct imsg imsg;
+ struct imsgbuf *ibuf = bula;
+ int n, link_ok;
+ struct kif *kif;
+ 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("ripe_dispatch_main: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_IFINFO:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct kif))
+ fatalx("IFINFO imsg with wrong len");
+ kif = imsg.data;
+ link_ok = (kif->flags & IFF_UP) &&
+ (kif->link_state == LINK_STATE_UP ||
+ (kif->link_state == LINK_STATE_UNKNOWN &&
+ kif->media_type != IFT_CARP));
+
+ LIST_FOREACH(iface, &oeconf->iface_list, entry) {
+ if (kif->ifindex == iface->ifindex) {
+ iface->flags = kif->flags;
+ iface->linkstate = kif->link_state;
+
+ if (link_ok) {
+ if_fsm(iface, IF_EVT_UP);
+ log_warnx("interface %s up",
+ iface->name);
+ } else {
+ if_fsm(iface, IF_EVT_DOWN);
+ log_warnx("interface %s down",
+ iface->name);
+ }
+ }
+ }
+ break;
+ case IMSG_CTL_IFINFO:
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_KROUTE_ADDR:
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("ripe_dispatch_main: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+/* ARGSUSED */
+void
+ripe_dispatch_rde(int fd, short event, void *bula)
+{
+ struct rip_route *rr;
+ struct imsg imsg;
+ struct imsgbuf *ibuf = bula;
+ struct iface *iface;
+ struct nbr *nbr;
+ 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("ripe_dispatch_rde: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_REQUEST_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr))
+ fatalx("invalid size of RDE request");
+
+ if ((rr = malloc(sizeof(*rr))) == NULL)
+ fatal("ripe_dispatch_rde");
+
+ memcpy(rr, imsg.data, sizeof(*rr));
+
+ if (imsg.hdr.peerid != 0) {
+ if ((nbr = nbr_find_peerid(imsg.hdr.peerid)) ==
+ NULL) {
+ log_debug("unknown neighbor id %u",
+ imsg.hdr.peerid);
+ break;
+ }
+ add_entry(&nbr->rq_list, rr);
+ break;
+ }
+
+ LIST_FOREACH(iface, &oeconf->iface_list, entry) {
+ add_entry(&iface->rq_list, rr);
+ }
+ break;
+ case IMSG_SEND_REQUEST:
+ if (imsg.hdr.peerid != 0) {
+ if ((nbr = nbr_find_peerid(imsg.hdr.peerid)) ==
+ NULL) {
+ log_debug("unknown neighbor id %u",
+ imsg.hdr.peerid);
+ break;
+ }
+ send_request(&nbr->rq_list, NULL, nbr);
+ break;
+ }
+
+ LIST_FOREACH(iface, &oeconf->iface_list, entry) {
+ send_request(&iface->rq_list, iface, NULL);
+ }
+ break;
+ case IMSG_RESPONSE_ADD:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr))
+ fatalx("invalid size of RDE request");
+
+ if ((rr = malloc(sizeof(*rr))) == NULL)
+ fatal("ripe_dispatch_rde");
+
+ memcpy(rr, imsg.data, sizeof(*rr));
+
+ if (imsg.hdr.peerid == 0) {
+ LIST_FOREACH(iface, &oeconf->iface_list,
+ entry) {
+ if ((iface->addr.s_addr &
+ iface->mask.s_addr) !=
+ rr->address.s_addr ||
+ iface->mask.s_addr !=
+ rr->mask.s_addr)
+ add_entry(&iface->rp_list,
+ rr);
+ }
+ break;
+ }
+
+ if ((nbr = nbr_find_peerid(imsg.hdr.peerid)) == NULL) {
+ log_debug("unknown neighbor id %u",
+ imsg.hdr.peerid);
+ break;
+ }
+ iface = nbr->iface;
+ if ((iface->addr.s_addr & iface->mask.s_addr) !=
+ rr->address.s_addr ||
+ iface->mask.s_addr != rr->mask.s_addr)
+ add_entry(&nbr->rp_list, rr);
+ break;
+ case IMSG_SEND_RESPONSE:
+ if (imsg.hdr.peerid == 0) {
+ LIST_FOREACH(iface, &oeconf->iface_list,
+ entry) {
+ send_response(&iface->rp_list,
+ iface, NULL);
+ }
+ break;
+ }
+
+ if ((nbr = nbr_find_peerid(imsg.hdr.peerid)) == NULL) {
+ log_debug("unknown neighbor id %u",
+ imsg.hdr.peerid);
+ break;
+ }
+ send_response(&nbr->rp_list, NULL, nbr);
+ break;
+ case IMSG_SEND_TRIGGERED_UPDATE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct
+ rip_route))
+ fatalx("invalid size of RDE request");
+
+ rr = imsg.data;
+
+ LIST_FOREACH(iface, &oeconf->iface_list,
+ entry) {
+ if (rr->ifindex != iface->ifindex)
+ send_triggered_update(iface, rr);
+ }
+ break;
+ case IMSG_CTL_END:
+ case IMSG_CTL_SHOW_RIB:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("ripe_dispatch_rde: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+ripe_shutdown(void)
+{
+ struct iface *iface;
+
+ LIST_FOREACH(iface, &oeconf->iface_list, entry) {
+ if (if_fsm(iface, IF_EVT_DOWN)) {
+ log_debug("error stopping interface %s",
+ iface->name);
+ }
+ }
+ while ((iface = LIST_FIRST(&oeconf->iface_list)) != NULL) {
+ LIST_REMOVE(iface, entry);
+ if_del(iface);
+ }
+
+ close(oeconf->rip_socket);
+
+ /* 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(oeconf);
+ free(pkt_ptr);
+
+ log_info("rip engine exiting");
+ _exit(0);
+}
+
+void
+ripe_iface_ctl(struct ctl_conn *c, unsigned int idx)
+{
+ struct iface *iface;
+ struct ctl_iface *ictl;
+
+ LIST_FOREACH(iface, &oeconf->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
+ripe_nbr_ctl(struct ctl_conn *c)
+{
+ struct iface *iface;
+ struct nbr *nbr;
+ struct ctl_nbr *nctl;
+
+ LIST_FOREACH(iface, &oeconf->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/ripd/ripe.h b/usr.sbin/ripd/ripe.h
new file mode 100644
index 00000000000..cf7b8a1ea94
--- /dev/null
+++ b/usr.sbin/ripd/ripe.h
@@ -0,0 +1,146 @@
+/* $OpenBSD: ripe.h,v 1.1 2006/10/18 16:11:58 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
+ * Copyright (c) 2004, 2005 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 _RIPE_H_
+#define _RIPE_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;
+char *pkt_ptr;
+
+/* neighbor events */
+enum nbr_event {
+ NBR_EVT_RESPONSE_RCVD,
+ NBR_EVT_REQUEST_RCVD,
+ NBR_EVT_TIMEOUT,
+ NBR_EVT_KILL_NBR,
+ NBR_EVT_NOTHING
+};
+
+/* neighbor actions */
+enum nbr_action {
+ NBR_ACT_STRT_TIMER,
+ NBR_ACT_RST_TIMER,
+ NBR_ACT_DEL,
+ NBR_ACT_NOTHING
+};
+
+struct nbr_failed {
+ LIST_ENTRY(nbr_failed) entry;
+ struct in_addr addr;
+ u_int32_t auth_seq_num;
+};
+
+struct nbr {
+ LIST_ENTRY(nbr) entry, hash;
+ struct event timeout_timer;
+ struct in_addr addr;
+ struct in_addr id;
+
+ struct packet_head rq_list;
+ struct packet_head rp_list;
+
+ struct iface *iface;
+
+ u_int32_t peerid; /* unique ID in DB */
+ u_int32_t auth_seq_num;
+ u_int16_t port;
+ time_t uptime;
+ int state;
+ int flags;
+};
+
+/* packet */
+int send_packet(struct iface *, void *, size_t, struct sockaddr_in *);
+void recv_packet(int, short, void *);
+int gen_rip_hdr(struct buf *, u_int8_t);
+
+/* interface */
+void if_init(struct ripd_conf *, struct iface *);
+int if_fsm(struct iface *, enum iface_event);
+int if_set_mcast(struct iface *);
+int if_set_mcast_ttl(int, u_int8_t);
+int if_set_mcast_loop(int);
+int if_set_opt(int);
+int if_set_tos(int, int);
+void if_set_recvbuf(int);
+struct iface *if_new(struct kif *);
+void if_del(struct iface *);
+const char *if_event_name(int);
+const char *if_action_name(int);
+int if_join_group(struct iface *, struct in_addr *);
+int if_leave_group(struct iface *, struct in_addr *);
+struct ctl_iface *if_to_ctl(struct iface *);
+
+/* communications */
+void recv_request(struct iface *, struct nbr *, char *, u_int16_t);
+void recv_response(struct iface *, struct nbr *, char *, u_int16_t);
+void add_entry(struct packet_head *, struct rip_route *);
+int send_triggered_update(struct iface *, struct rip_route *);
+int send_request(struct packet_head *, struct iface *, struct nbr *);
+int send_response(struct packet_head *, struct iface *, struct nbr *);
+int start_report_timer(void);
+void report_timer(int, short, void *);
+
+/* ripe.c */
+pid_t ripe(struct ripd_conf *, int [2], int [2], int [2]);
+int ripe_imsg_compose_parent(int, pid_t, void *, u_int16_t);
+int ripe_imsg_compose_rde(int, u_int32_t, pid_t, void *,
+ u_int16_t);
+void ripe_dispatch_main(int, short, void *);
+void ripe_dispatch_rde(int, short, void *);
+void ripe_iface_ctl(struct ctl_conn *, unsigned int);
+void ripe_nbr_ctl(struct ctl_conn *);
+
+/* auth.c */
+int auth_validate(char **, u_int16_t *, struct iface *, struct nbr *,
+ struct nbr_failed *, u_int32_t *);
+int auth_gen(struct buf *, struct iface *);
+int auth_add_trailer(struct buf *, struct iface *);
+void md_list_add(struct auth_md_head *, u_int8_t, char *);
+void md_list_copy(struct auth_md_head *, struct auth_md_head *);
+void md_list_clr(struct auth_md_head *);
+
+/* neighbor.c */
+void nbr_init(u_int32_t);
+struct nbr *nbr_new(u_int32_t, struct iface *, int);
+void nbr_act_del(struct nbr *);
+
+struct nbr *nbr_find_ip(struct iface *, u_int32_t);
+struct nbr *nbr_find_peerid(u_int32_t);
+struct nbr_failed *nbr_failed_find(struct iface *, u_int32_t);
+void nbr_failed_delete(struct iface *, struct nbr_failed *);
+
+int nbr_fsm(struct nbr *, enum nbr_event);
+
+void nbr_timeout_timer(int, short, void *);
+
+void nbr_act_delete(struct nbr *);
+
+const char *nbr_event_name(int);
+const char *nbr_action_name(int);
+
+struct ctl_nbr *nbr_to_ctl(struct nbr *);
+
+#endif /* _RIPE_H_ */