summaryrefslogtreecommitdiff
path: root/usr.sbin/ripd
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/ripd')
-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_ */