summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/ospf6d/Makefile21
-rw-r--r--usr.sbin/ospf6d/area.c117
-rw-r--r--usr.sbin/ospf6d/buffer.c218
-rw-r--r--usr.sbin/ospf6d/carp.c181
-rw-r--r--usr.sbin/ospf6d/control.c303
-rw-r--r--usr.sbin/ospf6d/control.h50
-rw-r--r--usr.sbin/ospf6d/database.c422
-rw-r--r--usr.sbin/ospf6d/hello.c281
-rw-r--r--usr.sbin/ospf6d/imsg.c181
-rw-r--r--usr.sbin/ospf6d/in_cksum.c82
-rw-r--r--usr.sbin/ospf6d/interface.c803
-rw-r--r--usr.sbin/ospf6d/iso_cksum.c57
-rw-r--r--usr.sbin/ospf6d/kroute.c1480
-rw-r--r--usr.sbin/ospf6d/log.c305
-rw-r--r--usr.sbin/ospf6d/log.h36
-rw-r--r--usr.sbin/ospf6d/lsack.c297
-rw-r--r--usr.sbin/ospf6d/lsreq.c251
-rw-r--r--usr.sbin/ospf6d/lsupdate.c600
-rw-r--r--usr.sbin/ospf6d/name2id.c228
-rw-r--r--usr.sbin/ospf6d/neighbor.c705
-rw-r--r--usr.sbin/ospf6d/ospf6.h240
-rw-r--r--usr.sbin/ospf6d/ospf6d.8160
-rw-r--r--usr.sbin/ospf6d/ospf6d.c826
-rw-r--r--usr.sbin/ospf6d/ospf6d.conf.5311
-rw-r--r--usr.sbin/ospf6d/ospf6d.h618
-rw-r--r--usr.sbin/ospf6d/ospfe.c1094
-rw-r--r--usr.sbin/ospf6d/ospfe.h233
-rw-r--r--usr.sbin/ospf6d/packet.c361
-rw-r--r--usr.sbin/ospf6d/parse.y1061
-rw-r--r--usr.sbin/ospf6d/printconf.c147
-rw-r--r--usr.sbin/ospf6d/rde.c1178
-rw-r--r--usr.sbin/ospf6d/rde.h169
-rw-r--r--usr.sbin/ospf6d/rde_lsdb.c737
-rw-r--r--usr.sbin/ospf6d/rde_spf.c1077
34 files changed, 14830 insertions, 0 deletions
diff --git a/usr.sbin/ospf6d/Makefile b/usr.sbin/ospf6d/Makefile
new file mode 100644
index 00000000000..64f3c9968aa
--- /dev/null
+++ b/usr.sbin/ospf6d/Makefile
@@ -0,0 +1,21 @@
+# $OpenBSD: Makefile,v 1.1 2007/10/08 10:44:50 norby Exp $
+
+
+PROG= ospf6d
+SRCS= area.c buffer.c carp.c control.c database.c hello.c \
+ imsg.c in_cksum.c interface.c iso_cksum.c kroute.c lsack.c \
+ lsreq.c lsupdate.c log.c neighbor.c ospf6d.c ospfe.c packet.c \
+ parse.y printconf.c rde.c rde_lsdb.c rde_spf.c name2id.c
+
+MAN= ospf6d.8 ospf6d.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
+DPADD+= ${LIBEVENT}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ospf6d/area.c b/usr.sbin/ospf6d/area.c
new file mode 100644
index 00000000000..31e09e6a030
--- /dev/null
+++ b/usr.sbin/ospf6d/area.c
@@ -0,0 +1,117 @@
+/* $OpenBSD: area.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2007 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/tree.h>
+
+#include <err.h>
+#include <stdlib.h>
+
+#include "ospf6.h"
+#include "ospf6d.h"
+#include "ospfe.h"
+#include "rde.h"
+#include "log.h"
+
+struct area *
+area_new(void)
+{
+ struct area *area = NULL;
+
+ if ((area = calloc(1, sizeof(*area))) == NULL)
+ errx(1, "area_new: calloc");
+
+ LIST_INIT(&area->iface_list);
+ LIST_INIT(&area->nbr_list);
+ RB_INIT(&area->lsa_tree);
+
+ return (area);
+}
+
+int
+area_del(struct area *area)
+{
+ struct iface *iface = NULL;
+ struct vertex *v, *nv;
+ struct rde_nbr *n;
+
+ /* area is removed so neutralize the demotion done by the area */
+ if (area->active == 0)
+ ospfe_demote_area(area, 1);
+
+ /* clean lists */
+ while ((iface = LIST_FIRST(&area->iface_list)) != NULL) {
+ LIST_REMOVE(iface, entry);
+ if_del(iface);
+ }
+
+ while ((n = LIST_FIRST(&area->nbr_list)) != NULL)
+ rde_nbr_del(n);
+
+ for (v = RB_MIN(lsa_tree, &area->lsa_tree); v != NULL; v = nv) {
+ nv = RB_NEXT(lsa_tree, &area->lsa_tree, v);
+ vertex_free(v);
+ }
+
+ free(area);
+
+ return (0);
+}
+
+struct area *
+area_find(struct ospfd_conf *conf, struct in_addr area_id)
+{
+ struct area *area;
+
+ LIST_FOREACH(area, &conf->area_list, entry) {
+ if (area->id.s_addr == area_id.s_addr) {
+ return (area);
+ }
+ }
+
+ return (NULL);
+}
+
+void
+area_track(struct area *area, int state)
+{
+ int old = area->active;
+
+ if (state & NBR_STA_FULL)
+ area->active++;
+ else if (area->active == 0)
+ fatalx("area_track: area already inactive");
+ else
+ area->active--;
+
+ if (area->active == 0 || old == 0)
+ ospfe_demote_area(area, old == 0);
+}
+
+int
+area_border_router(struct ospfd_conf *conf)
+{
+ struct area *area;
+ int active = 0;
+
+ LIST_FOREACH(area, &conf->area_list, entry)
+ if (area->active > 0)
+ active++;
+
+ return (active > 1);
+}
diff --git a/usr.sbin/ospf6d/buffer.c b/usr.sbin/ospf6d/buffer.c
new file mode 100644
index 00000000000..033ea9daef5
--- /dev/null
+++ b/usr.sbin/ospf6d/buffer.c
@@ -0,0 +1,218 @@
+/* $OpenBSD: buffer.c,v 1.1 2007/10/08 10:44:50 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 "ospf6d.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/ospf6d/carp.c b/usr.sbin/ospf6d/carp.c
new file mode 100644
index 00000000000..d34c277deab
--- /dev/null
+++ b/usr.sbin/ospf6d/carp.c
@@ -0,0 +1,181 @@
+/* $OpenBSD: carp.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 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/ioctl.h>
+#include <net/if.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ospf6d.h"
+#include "log.h"
+
+struct carpgroup {
+ TAILQ_ENTRY(carpgroup) entry;
+ char *group;
+ int do_demote;
+ int changed_by;
+};
+
+TAILQ_HEAD(carpgroups, carpgroup) carpgroups =
+ TAILQ_HEAD_INITIALIZER(carpgroups);
+
+struct carpgroup *carp_group_find(char *group);
+int carp_demote_ioctl(char *, int);
+
+struct carpgroup *
+carp_group_find(char *group)
+{
+ struct carpgroup *c;
+
+ TAILQ_FOREACH(c, &carpgroups, entry)
+ if (!strcmp(c->group, group))
+ return (c);
+
+ return (NULL);
+}
+
+int
+carp_demote_init(char *group, int force)
+{
+ struct carpgroup *c;
+ int level;
+
+ if ((c = carp_group_find(group)) == NULL) {
+ if ((c = calloc(1, sizeof(struct carpgroup))) == NULL) {
+ log_warn("carp_demote_init calloc");
+ return (-1);
+ }
+ if ((c->group = strdup(group)) == NULL) {
+ log_warn("carp_demote_init strdup");
+ free(c);
+ return (-1);
+ }
+
+ /* only demote if this group already is demoted */
+ if ((level = carp_demote_get(group)) == -1)
+ return (-1);
+ if (level > 0 || force)
+ c->do_demote = 1;
+
+ TAILQ_INSERT_TAIL(&carpgroups, c, entry);
+ }
+
+ return (0);
+}
+
+void
+carp_demote_shutdown(void)
+{
+ struct carpgroup *c;
+
+ while ((c = TAILQ_FIRST(&carpgroups)) != NULL) {
+ TAILQ_REMOVE(&carpgroups, c, entry);
+ for (; c->changed_by > 0; c->changed_by--)
+ if (c->do_demote)
+ carp_demote_ioctl(c->group, -1);
+
+ free(c->group);
+ free(c);
+ }
+}
+
+int
+carp_demote_get(char *group)
+{
+ int s;
+ struct ifgroupreq ifgr;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ log_warn("carp_demote_get: socket");
+ return (-1);
+ }
+
+ bzero(&ifgr, sizeof(ifgr));
+ strlcpy(ifgr.ifgr_name, group, sizeof(ifgr.ifgr_name));
+
+ if (ioctl(s, SIOCGIFGATTR, (caddr_t)&ifgr) == -1) {
+ if (errno == ENOENT)
+ log_warnx("group \"%s\" does not exist", group);
+ else
+ log_warn("carp_demote_get: ioctl");
+ close(s);
+ return (-1);
+ }
+
+ close(s);
+ return ((int)ifgr.ifgr_attrib.ifg_carp_demoted);
+}
+
+int
+carp_demote_set(char *group, int demote)
+{
+ struct carpgroup *c;
+
+ if ((c = carp_group_find(group)) == NULL) {
+ log_warnx("carp_group_find for %s returned NULL?!", group);
+ return (-1);
+ }
+
+ if (c->changed_by + demote < 0) {
+ log_warnx("carp_demote_set: changed_by + demote < 0");
+ return (-1);
+ }
+
+ if (c->do_demote && carp_demote_ioctl(group, demote) == -1)
+ return (-1);
+
+ c->changed_by += demote;
+
+ /* enable demotion when we return to 0, i. e. all sessions up */
+ if (demote < 0 && c->changed_by == 0)
+ c->do_demote = 1;
+
+ return (0);
+}
+
+int
+carp_demote_ioctl(char *group, int demote)
+{
+ int s, res;
+ struct ifgroupreq ifgr;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ log_warn("carp_demote_get: socket");
+ return (-1);
+ }
+
+ bzero(&ifgr, sizeof(ifgr));
+ strlcpy(ifgr.ifgr_name, group, sizeof(ifgr.ifgr_name));
+ ifgr.ifgr_attrib.ifg_carp_demoted = demote;
+
+ if ((res = ioctl(s, SIOCSIFGATTR, (caddr_t)&ifgr)) == -1)
+ log_warn("unable to %s the demote state "
+ "of group '%s'", (demote > 0) ? "increment" : "decrement",
+ group);
+ else
+ log_info("%s the demote state of group '%s'",
+ (demote > 0) ? "incremented" : "decremented", group);
+
+ close(s);
+ return (res);
+}
diff --git a/usr.sbin/ospf6d/control.c b/usr.sbin/ospf6d/control.c
new file mode 100644
index 00000000000..f7b14cfb4fc
--- /dev/null
+++ b/usr.sbin/ospf6d/control.c
@@ -0,0 +1,303 @@
+/* $OpenBSD: control.c,v 1.1 2007/10/08 10:44:50 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 "ospf6d.h"
+#include "ospf6.h"
+#include "ospfe.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, OSPF6D_SOCKET, sizeof(sun.sun_path));
+
+ if (unlink(OSPF6D_SOCKET) == -1)
+ if (errno != ENOENT) {
+ log_warn("control_init: unlink %s", OSPF6D_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", OSPF6D_SOCKET);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(OSPF6D_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("control_init: chmod");
+ close(fd);
+ (void)unlink(OSPF6D_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(OSPF6D_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)) == -1 || n == 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_FIB_COUPLE:
+ case IMSG_CTL_FIB_DECOUPLE:
+ ospfe_fip_update(imsg.hdr.type);
+ /* FALLTHROUGH */
+ case IMSG_CTL_RELOAD:
+ c->ibuf.pid = imsg.hdr.pid;
+ ospfe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
+ break;
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_KROUTE_ADDR:
+ case IMSG_CTL_IFINFO:
+ c->ibuf.pid = imsg.hdr.pid;
+ ospfe_imsg_compose_parent(imsg.hdr.type,
+ imsg.hdr.pid, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_SHOW_INTERFACE:
+ if (imsg.hdr.len == IMSG_HEADER_SIZE +
+ sizeof(ifidx)) {
+ memcpy(&ifidx, imsg.data, sizeof(ifidx));
+ ospfe_iface_ctl(c, ifidx);
+ imsg_compose(&c->ibuf, IMSG_CTL_END, 0,
+ 0, NULL, 0);
+ }
+ break;
+ case IMSG_CTL_SHOW_DATABASE:
+ case IMSG_CTL_SHOW_DB_EXT:
+ case IMSG_CTL_SHOW_DB_NET:
+ case IMSG_CTL_SHOW_DB_RTR:
+ case IMSG_CTL_SHOW_DB_SELF:
+ case IMSG_CTL_SHOW_DB_SUM:
+ case IMSG_CTL_SHOW_DB_ASBR:
+ case IMSG_CTL_SHOW_RIB:
+ case IMSG_CTL_SHOW_SUM:
+ c->ibuf.pid = imsg.hdr.pid;
+ ospfe_imsg_compose_rde(imsg.hdr.type, 0, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_SHOW_NBR:
+ ospfe_nbr_ctl(c);
+ 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("fcntl F_GETFL");
+
+ if (bm == BM_NONBLOCK)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fcntl F_SETFL");
+}
diff --git a/usr.sbin/ospf6d/control.h b/usr.sbin/ospf6d/control.h
new file mode 100644
index 00000000000..7d753010700
--- /dev/null
+++ b/usr.sbin/ospf6d/control.h
@@ -0,0 +1,50 @@
+/* $OpenBSD: control.h,v 1.1 2007/10/08 10:44:50 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/ospf6d/database.c b/usr.sbin/ospf6d/database.c
new file mode 100644
index 00000000000..955bc469b40
--- /dev/null
+++ b/usr.sbin/ospf6d/database.c
@@ -0,0 +1,422 @@
+/* $OpenBSD: database.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2007 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ospf6d.h"
+#include "ospf6.h"
+#include "log.h"
+#include "ospfe.h"
+
+extern struct ospfd_conf *oeconf;
+
+void db_sum_list_next(struct nbr *);
+
+/* database description packet handling */
+int
+send_db_description(struct nbr *nbr)
+{
+ struct sockaddr_in6 dst;
+ struct db_dscrp_hdr dd_hdr;
+ struct lsa_entry *le, *nle;
+ struct buf *buf;
+ int ret = 0;
+
+ if ((buf = buf_open(nbr->iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_db_description");
+
+ /* OSPF header */
+ if (gen_ospf_hdr(buf, nbr->iface, PACKET_TYPE_DD))
+ goto fail;
+
+ /* reserve space for database description header */
+ if (buf_reserve(buf, sizeof(dd_hdr)) == NULL)
+ goto fail;
+
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_INIT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_SNAP:
+ log_debug("send_db_description: cannot send packet in state %s,"
+ " neighbor ID %s", nbr_state_name(nbr->state),
+ inet_ntoa(nbr->id));
+ ret = -1;
+ goto done;
+ case NBR_STA_XSTRT:
+ nbr->options |= OSPF_DBD_MS | OSPF_DBD_M | OSPF_DBD_I;
+ break;
+ case NBR_STA_XCHNG:
+ if (nbr->master) {
+ /* master */
+ nbr->options |= OSPF_DBD_MS;
+ } else {
+ /* slave */
+ nbr->options &= ~OSPF_DBD_MS;
+ }
+
+ if (TAILQ_EMPTY(&nbr->db_sum_list))
+ nbr->options &= ~OSPF_DBD_M;
+ else
+ nbr->options |= OSPF_DBD_M;
+
+ nbr->options &= ~OSPF_DBD_I;
+
+ /* build LSA list, keep space for a possible md5 sum */
+ for (le = TAILQ_FIRST(&nbr->db_sum_list); le != NULL &&
+ buf->wpos + sizeof(struct lsa_hdr) < buf->max -
+ MD5_DIGEST_LENGTH; le = nle) {
+ nbr->dd_end = nle = TAILQ_NEXT(le, entry);
+ if (buf_add(buf, le->le_lsa, sizeof(struct lsa_hdr)))
+ goto fail;
+ }
+ break;
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ if (nbr->master) {
+ /* master */
+ nbr->options |= OSPF_DBD_MS;
+ } else {
+ /* slave */
+ nbr->options &= ~OSPF_DBD_MS;
+ }
+ nbr->options &= ~OSPF_DBD_M;
+ nbr->options &= ~OSPF_DBD_I;
+
+ break;
+ default:
+ fatalx("send_db_description: unknown neighbor state");
+ }
+
+ /* set destination */
+ dst.sin6_family = AF_INET6;
+ dst.sin6_len = sizeof(struct sockaddr_in6);
+
+ switch (nbr->iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_pton(AF_INET6, AllSPFRouters, &dst.sin6_addr);
+ dd_hdr.iface_mtu = htons(nbr->iface->mtu);
+ break;
+ case IF_TYPE_BROADCAST:
+ dst.sin6_addr = nbr->addr;
+ dd_hdr.iface_mtu = htons(nbr->iface->mtu);
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ /* XXX not supported */
+ break;
+ case IF_TYPE_VIRTUALLINK:
+ dst.sin6_addr = nbr->iface->dst;
+ dd_hdr.iface_mtu = 0;
+ break;
+ default:
+ fatalx("send_db_description: unknown interface type");
+ }
+
+ dd_hdr.opts = oeconf->options;
+ dd_hdr.bits = nbr->options;
+ dd_hdr.dd_seq_num = htonl(nbr->dd_seq_num);
+
+ memcpy(buf_seek(buf, sizeof(struct ospf_hdr), sizeof(dd_hdr)),
+ &dd_hdr, sizeof(dd_hdr));
+
+ /* calculate checksum */
+ if (upd_ospf_hdr(buf, nbr->iface))
+ goto fail;
+
+ /* transmit packet */
+ ret = send_packet(nbr->iface, buf->buf, buf->wpos, &dst);
+done:
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_db_description");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_db_description(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct db_dscrp_hdr dd_hdr;
+ int dupe = 0;
+
+ if (len < sizeof(dd_hdr)) {
+ log_warnx("recv_dd_description: "
+ "bad packet size, neighbor ID %s", inet_ntoa(nbr->id));
+ return;
+ }
+ memcpy(&dd_hdr, buf, sizeof(dd_hdr));
+ buf += sizeof(dd_hdr);
+ len -= sizeof(dd_hdr);
+
+ /* db description packet sanity checks */
+ if (ntohs(dd_hdr.iface_mtu) > nbr->iface->mtu) {
+ log_warnx("recv_dd_description: invalid MTU %d sent by "
+ "neighbor ID %s, expected %d", ntohs(dd_hdr.iface_mtu),
+ inet_ntoa(nbr->id), nbr->iface->mtu);
+ return;
+ }
+
+ if (nbr->last_rx_options == dd_hdr.opts &&
+ nbr->last_rx_bits == dd_hdr.bits &&
+ ntohl(dd_hdr.dd_seq_num) == nbr->dd_seq_num - nbr->master ? 1 : 0) {
+ log_debug("recv_db_description: dupe");
+ dupe = 1;
+ }
+
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_SNAP:
+ log_debug("recv_db_description: packet ignored in state %s, "
+ "neighbor ID %s", nbr_state_name(nbr->state),
+ inet_ntoa(nbr->id));
+ return;
+ case NBR_STA_INIT:
+ /* evaluate dr and bdr after issuing a 2-Way event */
+ nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
+ if_fsm(nbr->iface, IF_EVT_NBR_CHNG);
+ if (nbr->state != NBR_STA_XSTRT)
+ return;
+ /* FALLTHROUGH */
+ case NBR_STA_XSTRT:
+ if (dupe)
+ return;
+ /*
+ * check bits: either I,M,MS or only M
+ */
+ if (dd_hdr.bits == (OSPF_DBD_I | OSPF_DBD_M | OSPF_DBD_MS)) {
+ /* if nbr Router ID is larger than own -> slave */
+ if ((ntohl(nbr->id.s_addr)) >
+ ntohl(ospfe_router_id())) {
+ /* slave */
+ nbr->master = 0;
+ nbr->dd_seq_num = ntohl(dd_hdr.dd_seq_num);
+
+ /* event negotiation done */
+ nbr_fsm(nbr, NBR_EVT_NEG_DONE);
+ }
+ } else if (!(dd_hdr.bits & (OSPF_DBD_I | OSPF_DBD_MS))) {
+ /* M only case: we are master */
+ if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num) {
+ log_warnx("recv_db_description: invalid "
+ "seq num, mine %x his %x",
+ nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+ nbr->dd_seq_num++;
+
+ /* this packet may already have data so pass it on */
+ if (len > 0) {
+ nbr->dd_pending++;
+ ospfe_imsg_compose_rde(IMSG_DD, nbr->peerid,
+ 0, buf, len);
+ }
+
+ /* event negotiation done */
+ nbr_fsm(nbr, NBR_EVT_NEG_DONE);
+
+ } else {
+ /* ignore packet */
+ log_debug("recv_db_description: packet ignored in "
+ "state %s (bad flags), neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+ }
+ break;
+ case NBR_STA_XCHNG:
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ if (dd_hdr.bits & OSPF_DBD_I ||
+ !(dd_hdr.bits & OSPF_DBD_MS) == !nbr->master) {
+ log_warnx("recv_db_description: seq num mismatch, "
+ "bad flags");
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+
+ if (nbr->last_rx_options != dd_hdr.opts) {
+ log_warnx("recv_db_description: seq num mismatch, "
+ "bad options");
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+
+ if (dupe) {
+ if (!nbr->master)
+ /* retransmit */
+ start_db_tx_timer(nbr);
+ return;
+ }
+
+ if (nbr->state != NBR_STA_XCHNG) {
+ log_warnx("recv_db_description: invalid "
+ "seq num, mine %x his %x",
+ nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+
+ /* sanity check dd seq number */
+ if (nbr->master) {
+ /* master */
+ if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num) {
+ log_warnx("recv_db_description: invalid "
+ "seq num, mine %x his %x",
+ nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+ nbr->dd_seq_num++;
+ } else {
+ /* slave */
+ if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num + 1) {
+ log_warnx("recv_db_description: invalid "
+ "seq num, mine %x his %x",
+ nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+ nbr->dd_seq_num = ntohl(dd_hdr.dd_seq_num);
+ }
+
+ /* forward to RDE and let it decide which LSAs to request */
+ if (len > 0) {
+ nbr->dd_pending++;
+ ospfe_imsg_compose_rde(IMSG_DD, nbr->peerid, 0,
+ buf, len);
+ }
+
+ /* next packet */
+ db_sum_list_next(nbr);
+ start_db_tx_timer(nbr);
+
+ if (!(dd_hdr.bits & OSPF_DBD_M) &&
+ TAILQ_EMPTY(&nbr->db_sum_list))
+ if (!nbr->master || !(nbr->options & OSPF_DBD_M))
+ nbr_fsm(nbr, NBR_EVT_XCHNG_DONE);
+ break;
+ default:
+ fatalx("recv_db_description: unknown neighbor state");
+ }
+
+ nbr->last_rx_options = dd_hdr.opts;
+ nbr->last_rx_bits = dd_hdr.bits;
+}
+
+void
+db_sum_list_add(struct nbr *nbr, struct lsa_hdr *lsa)
+{
+ struct lsa_entry *le;
+
+ if ((le = calloc(1, sizeof(*le))) == NULL)
+ fatal("db_sum_list_add");
+
+ TAILQ_INSERT_TAIL(&nbr->db_sum_list, le, entry);
+ le->le_lsa = lsa;
+}
+
+void
+db_sum_list_next(struct nbr *nbr)
+{
+ struct lsa_entry *le;
+
+ while ((le = TAILQ_FIRST(&nbr->db_sum_list)) != nbr->dd_end) {
+ TAILQ_REMOVE(&nbr->db_sum_list, le, entry);
+ free(le->le_lsa);
+ free(le);
+ }
+}
+
+void
+db_sum_list_clr(struct nbr *nbr)
+{
+ nbr->dd_end = NULL;
+ db_sum_list_next(nbr);
+}
+
+/* timers */
+/* ARGSUSED */
+void
+db_tx_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+ struct timeval tv;
+
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_INIT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_SNAP:
+ return ;
+ case NBR_STA_XSTRT:
+ case NBR_STA_XCHNG:
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ send_db_description(nbr);
+ break;
+ default:
+ log_debug("db_tx_timer: unknown neighbor state, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ break;
+ }
+
+ /* reschedule db_tx_timer but only in master mode */
+ if (nbr->master) {
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->rxmt_interval;
+ if (evtimer_add(&nbr->db_tx_timer, &tv) == -1)
+ fatal("db_tx_timer");
+ }
+}
+
+void
+start_db_tx_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ if (nbr == nbr->iface->self)
+ return;
+
+ timerclear(&tv);
+ if (evtimer_add(&nbr->db_tx_timer, &tv) == -1)
+ fatal("start_db_tx_timer");
+}
+
+void
+stop_db_tx_timer(struct nbr *nbr)
+{
+ if (nbr == nbr->iface->self)
+ return;
+
+ if (evtimer_del(&nbr->db_tx_timer) == -1)
+ fatal("stop_db_tx_timer");
+}
diff --git a/usr.sbin/ospf6d/hello.c b/usr.sbin/ospf6d/hello.c
new file mode 100644
index 00000000000..4e3f3d42710
--- /dev/null
+++ b/usr.sbin/ospf6d/hello.c
@@ -0,0 +1,281 @@
+/* $OpenBSD: hello.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * 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/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <event.h>
+
+#include "ospf6d.h"
+#include "ospf6.h"
+#include "log.h"
+#include "ospfe.h"
+
+extern struct ospfd_conf *oeconf;
+
+/* hello packet handling */
+int
+send_hello(struct iface *iface)
+{
+ struct sockaddr_in6 dst;
+ struct hello_hdr hello;
+ struct nbr *nbr;
+ struct buf *buf;
+ int ret;
+
+ dst.sin6_family = AF_INET6;
+ dst.sin6_len = sizeof(struct sockaddr_in6);
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ inet_pton(AF_INET6, AllSPFRouters, &dst.sin6_addr);
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ log_debug("send_hello: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ case IF_TYPE_VIRTUALLINK:
+ dst.sin6_addr = iface->dst;
+ break;
+ default:
+ fatalx("send_hello: unknown interface type");
+ }
+
+ /* XXX READ_BUF_SIZE */
+ if ((buf = buf_dynamic(PKG_DEF_SIZE, READ_BUF_SIZE)) == NULL)
+ fatal("send_hello");
+
+ /* OSPF header */
+ if (gen_ospf_hdr(buf, iface, PACKET_TYPE_HELLO))
+ goto fail;
+
+ /* hello header */
+ hello.iface_id = iface->ifindex;
+ hello.rtr_priority = iface->priority;
+
+ /* XXX options */
+ hello.opts1 = 0;
+ hello.opts2 = 0;
+ hello.opts3 = 0;
+ hello.opts3 |= OSPF_OPTION_R; /* XXX */
+
+ hello.hello_interval = htons(iface->hello_interval);
+ hello.rtr_dead_interval = htons(iface->dead_interval);
+
+ if (iface->dr) {
+ hello.d_rtr = iface->dr->id.s_addr;
+ iface->self->dr.s_addr = iface->dr->id.s_addr;
+ } else
+ hello.d_rtr = 0;
+ if (iface->bdr) {
+ hello.bd_rtr = iface->bdr->id.s_addr;
+ iface->self->bdr.s_addr = iface->bdr->id.s_addr;
+ } else
+ hello.bd_rtr = 0;
+
+ if (buf_add(buf, &hello, sizeof(hello)))
+ goto fail;
+
+ /* active neighbor(s) */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self))
+ if (buf_add(buf, &nbr->id, sizeof(nbr->id)))
+ goto fail;
+ }
+
+ /* calculate checksum */
+ if (upd_ospf_hdr(buf, iface))
+ goto fail;
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_hello");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_hello(struct iface *iface, struct in6_addr src, u_int32_t rtr_id,
+ char *buf, u_int16_t len)
+{
+ struct hello_hdr hello;
+ struct nbr *nbr = NULL, *dr;
+ u_int32_t nbr_id;
+ int nbr_change = 0;
+
+ if (len < sizeof(hello) && (len & 0x03)) {
+ log_warnx("recv_hello: bad packet size, interface %s",
+ iface->name);
+ return;
+ }
+
+ memcpy(&hello, buf, sizeof(hello));
+ buf += sizeof(hello);
+ len -= sizeof(hello);
+
+#if 0
+XXX
+ if (iface->type != IF_TYPE_POINTOPOINT &&
+ iface->type != IF_TYPE_VIRTUALLINK)
+ if (hello.mask != iface->mask.s_addr) {
+ log_warnx("recv_hello: invalid netmask, interface %s",
+ iface->name);
+ return;
+ }
+#endif
+
+ if (ntohs(hello.hello_interval) != iface->hello_interval) {
+ log_warnx("recv_hello: invalid hello-interval %d, "
+ "interface %s", ntohs(hello.hello_interval),
+ iface->name);
+ return;
+ }
+
+ if (ntohl(hello.rtr_dead_interval) != iface->dead_interval) {
+ log_warnx("recv_hello: invalid router-dead-interval %d, "
+ "interface %s", ntohl(hello.rtr_dead_interval),
+ iface->name);
+ return;
+ }
+
+ if ((hello.opts3 & OSPF_OPTION_E && iface->area->stub) || /* XXX */
+ ((hello.opts3 & OSPF_OPTION_E) == 0 && !iface->area->stub)) { /* XXX */
+ log_warnx("recv_hello: ExternalRoutingCapability mismatch, "
+ "interface %s", iface->name);
+ return;
+ }
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ /* match router-id */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr == iface->self)
+ continue;
+ if (nbr->id.s_addr == rtr_id)
+ break;
+ }
+ break;
+ case IF_TYPE_BROADCAST:
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ /* match src IP */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr == iface->self)
+ continue;
+//XXX if (nbr->addr.s_addr == src.s_addr)
+ if (IN6_ARE_ADDR_EQUAL(&nbr->addr, &src))
+ break;
+ }
+ break;
+ default:
+ fatalx("recv_hello: unknown interface type");
+ }
+
+ if (!nbr) {
+ nbr = nbr_new(rtr_id, iface, 0);
+ /* set neighbor parameters */
+ nbr->dr.s_addr = hello.d_rtr;
+ nbr->bdr.s_addr = hello.bd_rtr;
+ nbr->priority = hello.rtr_priority;
+ nbr_change = 1;
+ }
+
+ /* actually the neighbor address shouldn't be stored on virtual links */
+//XXX nbr->addr.s_addr = src.s_addr;
+ nbr->addr = src;
+ nbr->options = hello.opts3; /* XXX */
+
+ nbr_fsm(nbr, NBR_EVT_HELLO_RCVD);
+
+ while (len >= sizeof(nbr_id)) {
+ memcpy(&nbr_id, buf, sizeof(nbr_id));
+ if (nbr_id == ospfe_router_id()) {
+ /* seen myself */
+ if (nbr->state & NBR_STA_PRELIM)
+ nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
+ break;
+ }
+ buf += sizeof(nbr_id);
+ len -= sizeof(nbr_id);
+ }
+
+ if (len == 0) {
+ nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD);
+ /* set neighbor parameters */
+ nbr->dr.s_addr = hello.d_rtr;
+ nbr->bdr.s_addr = hello.bd_rtr;
+ nbr->priority = hello.rtr_priority;
+ return;
+ }
+
+ if (nbr->priority != hello.rtr_priority) {
+ nbr->priority = hello.rtr_priority;
+ nbr_change = 1;
+ }
+
+ if (iface->state & IF_STA_WAITING &&
+//XXX hello.d_rtr == nbr->addr.s_addr && hello.bd_rtr == 0)
+ hello.d_rtr == nbr->id.s_addr && hello.bd_rtr == 0)
+ if_fsm(iface, IF_EVT_BACKUP_SEEN);
+
+//XXX if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->addr.s_addr) {
+ if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->id.s_addr) {
+ /*
+ * In case we see the BDR make sure that the DR is around
+ * with a bidirectional (2_WAY or better) connection
+ */
+ LIST_FOREACH(dr, &iface->nbr_list, entry)
+//XXX if (hello.d_rtr == dr->addr.s_addr &&
+ if (hello.d_rtr == dr->id.s_addr &&
+ dr->state & NBR_STA_BIDIR)
+ if_fsm(iface, IF_EVT_BACKUP_SEEN);
+ }
+#if 0
+ if ((nbr->addr.s_addr == nbr->dr.s_addr &&
+ nbr->addr.s_addr != hello.d_rtr) ||
+ (nbr->addr.s_addr != nbr->dr.s_addr &&
+ nbr->addr.s_addr == hello.d_rtr))
+ /* neighbor changed from or to DR */
+ nbr_change = 1;
+ if ((nbr->addr.s_addr == nbr->bdr.s_addr &&
+ nbr->addr.s_addr != hello.bd_rtr) ||
+ (nbr->addr.s_addr != nbr->bdr.s_addr &&
+ nbr->addr.s_addr == hello.bd_rtr))
+ /* neighbor changed from or to BDR */
+ nbr_change = 1;
+#endif
+ nbr->dr.s_addr = hello.d_rtr;
+ nbr->bdr.s_addr = hello.bd_rtr;
+
+ if (nbr_change)
+ if_fsm(iface, IF_EVT_NBR_CHNG);
+
+ /* TODO NBMA needs some special handling */
+}
diff --git a/usr.sbin/ospf6d/imsg.c b/usr.sbin/ospf6d/imsg.c
new file mode 100644
index 00000000000..1af8491ec67
--- /dev/null
+++ b/usr.sbin/ospf6d/imsg.c
@@ -0,0 +1,181 @@
+/* $OpenBSD: imsg.c,v 1.1 2007/10/08 10:44:50 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 "ospf6d.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 (-2);
+ }
+
+ 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;
+
+ datalen += IMSG_HEADER_SIZE;
+ if (datalen > MAX_IMSGSIZE) {
+ log_warnx("imsg_create: len %u > MAX_IMSGSIZE; "
+ "type %u peerid %lu", datalen + IMSG_HEADER_SIZE,
+ type, peerid);
+ return (NULL);
+ }
+
+ hdr.type = type;
+ hdr.peerid = peerid;
+ if ((hdr.pid = pid) == 0)
+ hdr.pid = ibuf->pid;
+ if ((wbuf = buf_dynamic(datalen, MAX_IMSGSIZE)) == 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;
+ struct imsg_hdr *hdr;
+
+ hdr = (struct imsg_hdr *)msg->buf;
+ hdr->len = (u_int16_t)msg->wpos;
+ 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/ospf6d/in_cksum.c b/usr.sbin/ospf6d/in_cksum.c
new file mode 100644
index 00000000000..5fe4c3cdb3f
--- /dev/null
+++ b/usr.sbin/ospf6d/in_cksum.c
@@ -0,0 +1,82 @@
+/* $OpenBSD: in_cksum.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+/* $NetBSD: in_cksum.c,v 1.3 1995/04/22 13:53:48 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL)
+ */
+
+#include <sys/types.h>
+
+#include "ospf6d.h"
+#include "log.h"
+
+/*
+ * Checksum routine for Internet Protocol family headers.
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ * In particular, it should not be this one.
+ */
+u_int16_t
+in_cksum(void *p, size_t l)
+{
+ unsigned int sum = 0;
+ int len;
+ u_char *cp = p;
+
+ /* ensure that < 2^16 bytes being summed */
+ if (l >= (1 << 16))
+ fatalx("in_cksum: packet to big");
+ len = (int)l;
+
+ if (((long)cp & 1) == 0) {
+ while (len > 1) {
+ sum += htons(*(u_short *)cp);
+ cp += 2;
+ len -= 2;
+ }
+ } else {
+ while (len > 1) {
+ sum += *cp++ << 8;
+ sum += *cp++;
+ len -= 2;
+ }
+ }
+ if (len == 1)
+ sum += *cp << 8;
+
+ sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */
+ sum += sum >> 16; /* add potential last carry */
+ sum = ntohs(sum);
+ return (0xffff & ~sum);
+}
diff --git a/usr.sbin/ospf6d/interface.c b/usr.sbin/ospf6d/interface.c
new file mode 100644
index 00000000000..be415114742
--- /dev/null
+++ b/usr.sbin/ospf6d/interface.c
@@ -0,0 +1,803 @@
+/* $OpenBSD: interface.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2007 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 "ospf6d.h"
+#include "ospf6.h"
+#include "log.h"
+#include "ospfe.h"
+
+void if_hello_timer(int, short, void *);
+void if_start_hello_timer(struct iface *);
+void if_stop_hello_timer(struct iface *);
+void if_stop_wait_timer(struct iface *);
+void if_wait_timer(int, short, void *);
+void if_start_wait_timer(struct iface *);
+void if_stop_wait_timer(struct iface *);
+struct nbr *if_elect(struct nbr *, struct nbr *);
+
+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_WAITING, IF_EVT_BACKUP_SEEN, IF_ACT_ELECT, 0},
+ {IF_STA_WAITING, IF_EVT_WTIMER, IF_ACT_ELECT, 0},
+ {IF_STA_ANY, IF_EVT_WTIMER, IF_ACT_NOTHING, 0},
+ {IF_STA_WAITING, IF_EVT_NBR_CHNG, IF_ACT_NOTHING, 0},
+ {IF_STA_MULTI, IF_EVT_NBR_CHNG, IF_ACT_ELECT, 0},
+ {IF_STA_ANY, IF_EVT_NBR_CHNG, IF_ACT_NOTHING, 0},
+ {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN},
+ {IF_STA_ANY, IF_EVT_LOOP, IF_ACT_RST, IF_STA_LOOPBACK},
+ {IF_STA_LOOPBACK, IF_EVT_UNLOOP, IF_ACT_NOTHING, IF_STA_DOWN},
+ {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0},
+};
+
+static int vlink_cnt = 0;
+
+const char * const if_event_names[] = {
+ "NOTHING",
+ "UP",
+ "WAITTIMER",
+ "BACKUPSEEN",
+ "NEIGHBORCHANGE",
+ "LOOP",
+ "UNLOOP",
+ "DOWN"
+};
+
+const char * const if_action_names[] = {
+ "NOTHING",
+ "START",
+ "ELECT",
+ "RESET"
+};
+
+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_names[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_ELECT:
+ ret = if_act_elect(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_names[event],
+ if_state_name(old_state));
+ return (-1);
+ }
+
+ if (new_state != 0)
+ iface->state = new_state;
+
+ if (iface->state != old_state)
+ orig_rtr_lsa(iface->area);
+
+ if (old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT) &&
+ (iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0)
+ ospfe_demote_iface(iface, 0);
+ if ((old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0 &&
+ iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT))
+ ospfe_demote_iface(iface, 1);
+
+ log_debug("if_fsm: event %s resulted in action %s and changing "
+ "state for interface %s from %s to %s",
+ if_event_names[event], if_action_names[iface_fsm[i].action],
+ iface->name, if_state_name(old_state), if_state_name(iface->state));
+
+ return (ret);
+}
+
+struct iface *
+if_new(struct kif *kif, struct kif_addr *ka)
+{
+ struct iface *iface;
+
+ 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->ls_ack_list);
+
+ iface->crypt_seq_num = arc4random() & 0x0fffffff;
+
+ if (kif == NULL) {
+ iface->type = IF_TYPE_VIRTUALLINK;
+ snprintf(iface->name, sizeof(iface->name), "vlink%d",
+ vlink_cnt++);
+ iface->flags |= IFF_UP;
+ iface->mtu = IP_MSS;
+ return (iface);
+ }
+
+ strlcpy(iface->name, kif->ifname, sizeof(iface->name));
+
+ /* 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;
+ iface->state = IF_STA_LOOPBACK;
+ }
+
+ /* 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;
+ iface->baudrate = kif->baudrate;
+
+ /* set address, mask and p2p addr */
+ iface->addr = ka->addr;
+ iface->mask = ka->mask;
+ if (kif->flags & IFF_POINTOPOINT) {
+ iface->dst = ka->dstbrd;
+ }
+
+ return (iface);
+}
+
+void
+if_del(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+
+ log_debug("if_del: interface %s", iface->name);
+
+ /* revert the demotion when the interface is deleted */
+ if ((iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0)
+ ospfe_demote_iface(iface, 1);
+
+ /* clear lists etc */
+ while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL)
+ nbr_del(nbr);
+
+ if (evtimer_pending(&iface->hello_timer, NULL))
+ evtimer_del(&iface->hello_timer);
+ if (evtimer_pending(&iface->wait_timer, NULL))
+ evtimer_del(&iface->wait_timer);
+ if (evtimer_pending(&iface->lsack_tx_timer, NULL))
+ evtimer_del(&iface->lsack_tx_timer);
+
+ ls_ack_list_clr(iface);
+ free(iface);
+}
+
+void
+if_init(struct ospfd_conf *xconf, struct iface *iface)
+{
+ /* init the dummy local neighbor */
+ iface->self = nbr_new(ospfe_router_id(), iface, 1);
+
+ /* set event handlers for interface */
+ evtimer_set(&iface->lsack_tx_timer, ls_ack_tx_timer, iface);
+ evtimer_set(&iface->hello_timer, if_hello_timer, iface);
+ evtimer_set(&iface->wait_timer, if_wait_timer, iface);
+
+ iface->fd = xconf->ospf_socket;
+
+ ospfe_demote_iface(iface, 0);
+}
+
+/* timers */
+/* ARGSUSED */
+void
+if_hello_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+ struct timeval tv;
+
+ send_hello(iface);
+
+ /* reschedule hello_timer */
+ timerclear(&tv);
+ tv.tv_sec = iface->hello_interval;
+ if (evtimer_add(&iface->hello_timer, &tv) == -1)
+ fatal("if_hello_timer");
+}
+
+void
+if_start_hello_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ if (evtimer_add(&iface->hello_timer, &tv) == -1)
+ fatal("if_start_hello_timer");
+}
+
+void
+if_stop_hello_timer(struct iface *iface)
+{
+ if (evtimer_del(&iface->hello_timer) == -1)
+ fatal("if_stop_hello_timer");
+}
+
+/* ARGSUSED */
+void
+if_wait_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+
+ if_fsm(iface, IF_EVT_WTIMER);
+}
+
+void
+if_start_wait_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = iface->dead_interval;
+ if (evtimer_add(&iface->wait_timer, &tv) == -1)
+ fatal("if_start_wait_timer");
+}
+
+void
+if_stop_wait_timer(struct iface *iface)
+{
+ if (evtimer_del(&iface->wait_timer) == -1)
+ fatal("if_stop_wait_timer");
+}
+
+/* actions */
+int
+if_act_start(struct iface *iface)
+{
+ struct in6_addr addr;
+ struct timeval now;
+
+ if (!((iface->flags & IFF_UP) &&
+ (LINK_STATE_IS_UP(iface->linkstate) ||
+ (iface->linkstate == LINK_STATE_UNKNOWN &&
+ iface->media_type != IFT_CARP)))) {
+ log_debug("if_act_start: interface %s link down",
+ iface->name);
+ return (0);
+ }
+
+ if (iface->media_type == IFT_CARP && iface->passive == 0) {
+ /* force passive mode on carp interfaces */
+ log_warnx("if_act_start: forcing interface %s to passive",
+ iface->name);
+ iface->passive = 1;
+ }
+
+ if (iface->passive) {
+ /* for an update of stub network entries */
+ orig_rtr_lsa(iface->area);
+ return (0);
+ }
+
+ gettimeofday(&now, NULL);
+ iface->uptime = now.tv_sec;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_pton(AF_INET6, AllSPFRouters, &addr);
+
+ if (if_join_group(iface, &addr))
+ return (-1);
+ iface->state = IF_STA_POINTTOPOINT;
+ break;
+ case IF_TYPE_VIRTUALLINK:
+ iface->state = IF_STA_POINTTOPOINT;
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_NBMA:
+ log_debug("if_act_start: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ case IF_TYPE_BROADCAST:
+ inet_pton(AF_INET6, AllSPFRouters, &addr);
+
+ if (if_join_group(iface, &addr))
+ return (-1);
+ if (iface->priority == 0) {
+ iface->state = IF_STA_DROTHER;
+ } else {
+ iface->state = IF_STA_WAITING;
+ if_start_wait_timer(iface);
+ }
+ break;
+ default:
+ fatalx("if_act_start: unknown interface type");
+ }
+
+ /* hello timer needs to be started in any case */
+ if_start_hello_timer(iface);
+ return (0);
+}
+
+struct nbr *
+if_elect(struct nbr *a, struct nbr *b)
+{
+ if (a->priority > b->priority)
+ return (a);
+ if (a->priority < b->priority)
+ return (b);
+ if (ntohl(a->id.s_addr) > ntohl(b->id.s_addr))
+ return (a);
+ return (b);
+}
+
+int
+if_act_elect(struct iface *iface)
+{
+ struct in6_addr addr;
+ struct nbr *nbr, *bdr = NULL, *dr = NULL;
+ int round = 0;
+ int changed = 0;
+ int old_state;
+ char b1[16], b2[16], b3[16], b4[16];
+
+start:
+ /* elect backup designated router */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->priority == 0 || nbr == dr || /* not electable */
+ nbr->state & NBR_STA_PRELIM || /* not available */
+ nbr->dr.s_addr == nbr->id.s_addr) /* don't elect DR */
+ continue;
+ if (bdr != NULL) {
+ /*
+ * routers announcing themselves as BDR have higher
+ * precedence over those routers announcing a
+ * different BDR.
+ */
+ if (nbr->bdr.s_addr == nbr->id.s_addr) {
+ if (bdr->bdr.s_addr == bdr->id.s_addr)
+ bdr = if_elect(bdr, nbr);
+ else
+ bdr = nbr;
+ } else if (bdr->bdr.s_addr != bdr->id.s_addr)
+ bdr = if_elect(bdr, nbr);
+ } else
+ bdr = nbr;
+ }
+
+ /* elect designated router */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->priority == 0 || nbr->state & NBR_STA_PRELIM ||
+ (nbr != dr && nbr->dr.s_addr != nbr->id.s_addr))
+ /* only DR may be elected check priority too */
+ continue;
+ if (dr == NULL)
+ dr = nbr;
+ else
+ dr = if_elect(dr, nbr);
+ }
+
+ if (dr == NULL) {
+ /* no designate router found use backup DR */
+ dr = bdr;
+ bdr = NULL;
+ }
+
+ /*
+ * if we are involved in the election (e.g. new DR or no
+ * longer BDR) redo the election
+ */
+ if (round == 0 &&
+ ((iface->self == dr && iface->self != iface->dr) ||
+ (iface->self != dr && iface->self == iface->dr) ||
+ (iface->self == bdr && iface->self != iface->bdr) ||
+ (iface->self != bdr && iface->self == iface->bdr))) {
+ /*
+ * Reset announced DR/BDR to calculated one, so
+ * that we may get elected in the second round.
+ * This is needed to drop from a DR to a BDR.
+ */
+ iface->self->dr.s_addr = dr->id.s_addr;
+ if (bdr)
+ iface->self->bdr.s_addr = bdr->id.s_addr;
+ round = 1;
+ goto start;
+ }
+
+ log_debug("if_act_elect: interface %s old dr %s new dr %s, "
+ "old bdr %s new bdr %s", iface->name,
+ iface->dr ? inet_ntop(AF_INET, &iface->dr->addr, b1, sizeof(b1)) :
+ "none", dr ? inet_ntop(AF_INET, &dr->addr, b2, sizeof(b2)) : "none",
+ iface->bdr ? inet_ntop(AF_INET, &iface->bdr->addr, b3, sizeof(b3)) :
+ "none", bdr ? inet_ntop(AF_INET, &bdr->addr, b4, sizeof(b4)) :
+ "none");
+
+ /*
+ * After the second round still DR or BDR change state to DR or BDR,
+ * etc.
+ */
+ old_state = iface->state;
+ if (dr == iface->self)
+ iface->state = IF_STA_DR;
+ else if (bdr == iface->self)
+ iface->state = IF_STA_BACKUP;
+ else
+ iface->state = IF_STA_DROTHER;
+
+ /* TODO if iface is NBMA send all non eligible neighbors event Start */
+
+ /*
+ * if DR or BDR changed issue a AdjOK? event for all neighbors > 2-Way
+ */
+ if (iface->dr != dr || iface->bdr != bdr)
+ changed = 1;
+
+ iface->dr = dr;
+ iface->bdr = bdr;
+
+ if (changed) {
+ inet_pton(AF_INET6, AllDRouters, &addr);
+ if (old_state & IF_STA_DRORBDR &&
+ (iface->state & IF_STA_DRORBDR) == 0) {
+ if (if_leave_group(iface, &addr))
+ return (-1);
+ } else if ((old_state & IF_STA_DRORBDR) == 0 &&
+ iface->state & IF_STA_DRORBDR) {
+ if (if_join_group(iface, &addr))
+ return (-1);
+ }
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->state & NBR_STA_BIDIR)
+ nbr_fsm(nbr, NBR_EVT_ADJ_OK);
+ }
+
+ orig_rtr_lsa(iface->area);
+ if (iface->state & IF_STA_DR || old_state & IF_STA_DR)
+ orig_net_lsa(iface);
+ }
+
+ if_start_hello_timer(iface);
+ return (0);
+}
+
+int
+if_act_reset(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+ struct in6_addr addr;
+
+ if (iface->passive) {
+ /* for an update of stub network entries */
+ orig_rtr_lsa(iface->area);
+ return (0);
+ }
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ inet_pton(AF_INET6, AllSPFRouters, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: error leaving group %s, "
+ "interface %s", log_in6addr(&addr), iface->name);
+ }
+ if (iface->state & IF_STA_DRORBDR) {
+ inet_pton(AF_INET6, AllDRouters, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: "
+ "error leaving group %s, interface %s",
+ log_in6addr(&addr), iface->name);
+ }
+ }
+ break;
+ case IF_TYPE_VIRTUALLINK:
+ /* nothing */
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ log_debug("if_act_reset: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ 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));
+ }
+ }
+
+ iface->dr = NULL;
+ iface->bdr = NULL;
+
+ ls_ack_list_clr(iface);
+ stop_ls_ack_tx_timer(iface);
+ if_stop_hello_timer(iface);
+ if_stop_wait_timer(iface);
+
+ /* send empty hello to tell everybody that we are going down */
+ send_hello(iface);
+
+ return (0);
+}
+
+struct ctl_iface *
+if_to_ctl(struct iface *iface)
+{
+ static struct ctl_iface ictl;
+ struct timeval tv, now, res;
+ struct nbr *nbr;
+
+ 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.rtr_id.s_addr = ospfe_router_id();
+ memcpy(&ictl.area, &iface->area->id, sizeof(ictl.area));
+ if (iface->dr) {
+ memcpy(&ictl.dr_id, &iface->dr->id, sizeof(ictl.dr_id));
+ memcpy(&ictl.dr_addr, &iface->dr->addr, sizeof(ictl.dr_addr));
+ } else {
+ bzero(&ictl.dr_id, sizeof(ictl.dr_id));
+ bzero(&ictl.dr_addr, sizeof(ictl.dr_addr));
+ }
+ if (iface->bdr) {
+ memcpy(&ictl.bdr_id, &iface->bdr->id, sizeof(ictl.bdr_id));
+ memcpy(&ictl.bdr_addr, &iface->bdr->addr,
+ sizeof(ictl.bdr_addr));
+ } else {
+ bzero(&ictl.bdr_id, sizeof(ictl.bdr_id));
+ bzero(&ictl.bdr_addr, sizeof(ictl.bdr_addr));
+ }
+ ictl.ifindex = iface->ifindex;
+ ictl.state = iface->state;
+ ictl.mtu = iface->mtu;
+ ictl.nbr_cnt = 0;
+ ictl.adj_cnt = 0;
+ ictl.baudrate = iface->baudrate;
+ ictl.dead_interval = iface->dead_interval;
+ ictl.transmit_delay = iface->transmit_delay;
+ ictl.hello_interval = iface->hello_interval;
+ ictl.flags = iface->flags;
+ ictl.metric = iface->metric;
+ ictl.rxmt_interval = iface->rxmt_interval;
+ ictl.type = iface->type;
+ ictl.linkstate = iface->linkstate;
+ ictl.mediatype = iface->media_type;
+ ictl.priority = iface->priority;
+ ictl.passive = iface->passive;
+
+ gettimeofday(&now, NULL);
+ if (evtimer_pending(&iface->hello_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ ictl.hello_timer = res.tv_sec;
+ } else
+ ictl.hello_timer = -1;
+
+ if (iface->state != IF_STA_DOWN) {
+ ictl.uptime = now.tv_sec - iface->uptime;
+ } else
+ ictl.uptime = 0;
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr == iface->self)
+ continue;
+ ictl.nbr_cnt++;
+ if (nbr->state & NBR_STA_ADJFORM)
+ ictl.adj_cnt++;
+ }
+
+ return (&ictl);
+}
+
+/* misc */
+int
+if_set_recvif(int fd, int enable)
+{
+#if 0
+ if (setsockopt(fd, IPPROTO_IPV6, IP_RECVIF, &enable,
+ sizeof(enable)) < 0) {
+ log_warn("if_set_recvif: error setting IP_RECVIF");
+ return (-1);
+ }
+#endif
+ 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 in6_addr *addr)
+{
+ struct ipv6_mreq mreq;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ log_debug("if_join_group: interface %s addr %s",
+ iface->name, log_in6addr(addr));
+ mreq.ipv6mr_multiaddr = *addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ log_warn("if_join_group: error IPV6_JOIN_GROUP, "
+ "interface %s address %s", iface->name,
+ log_in6addr(addr));
+ return (-1);
+ }
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ case IF_TYPE_NBMA:
+ log_debug("if_join_group: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ default:
+ fatalx("if_join_group: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_leave_group(struct iface *iface, struct in6_addr *addr)
+{
+ struct ipv6_mreq mreq;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ log_debug("if_leave_group: interface %s addr %s",
+ iface->name, log_in6addr(addr));
+ mreq.ipv6mr_multiaddr = *addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("if_leave_group: error IPV6_LEAVE_GROUP, "
+ "interface %s address %s", iface->name,
+ log_in6addr(addr));
+ return (-1);
+ }
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ case IF_TYPE_NBMA:
+ log_debug("if_leave_group: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ default:
+ fatalx("if_leave_group: unknown interface type");
+ }
+ return (0);
+}
+
+int
+if_set_mcast(struct iface *iface)
+{
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ log_debug("if_set_mcast: iface %s", iface->name);
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &iface->ifindex, sizeof(iface->ifindex)) < 0) {
+ log_debug("if_set_mcast: error setting "
+ "IP_MULTICAST_IF, interface %s", iface->name);
+ return (-1);
+ }
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ case IF_TYPE_NBMA:
+ log_debug("if_set_mcast: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ default:
+ fatalx("if_set_mcast: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_set_mcast_loop(int fd)
+{
+ u_int loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ (u_int *)&loop, sizeof(loop)) < 0) {
+ log_warn("if_set_mcast_loop: error setting "
+ "IPV6_MULTICAST_LOOP");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ip_hdrincl(int fd)
+{
+#if 0
+ int hincl = 1;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IP_HDRINCL, &hincl,
+ sizeof(hincl)) < 0) {
+ log_warn("if_set_ip_hdrincl: error setting IP_HDRINCL");
+ return (-1);
+ }
+#endif
+ return (0);
+}
diff --git a/usr.sbin/ospf6d/iso_cksum.c b/usr.sbin/ospf6d/iso_cksum.c
new file mode 100644
index 00000000000..f2eea518f9c
--- /dev/null
+++ b/usr.sbin/ospf6d/iso_cksum.c
@@ -0,0 +1,57 @@
+/* $OpenBSD: iso_cksum.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@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 "ospf6d.h"
+
+/* implementation of Fletcher Checksum -- see RFC 1008 for more info */
+
+/* pos needs to be 0 for verify and 2 <= pos < len for calculation */
+u_int16_t
+iso_cksum(void *buf, u_int16_t len, u_int16_t pos)
+{
+ u_int8_t *p = buf;
+ int c0 = 0, c1 = 0, x; /* counters */
+ u_int16_t sop;
+
+ sop = len - pos - 1; /* pos is an offset (pos 2 is 3rd element) */
+ p += 2; /* jump over age field */
+ len -= 2;
+ while (len--) {
+ c0 += *p++;
+ c1 += c0;
+ if ((len & 0xfff) == 0) {
+ /* overflow protection */
+ c0 %= 255;
+ c1 %= 255;
+ }
+ }
+
+ if (pos) {
+ x = ((sop * c0 - c1)) % 255;
+ if (x <= 0)
+ x += 255;
+ c1 = 510 - c0 - x;
+ if (c1 > 255)
+ c1 -= 255;
+ c0 = x;
+ }
+
+ return (c0 << 8 | c1);
+}
diff --git a/usr.sbin/ospf6d/kroute.c b/usr.sbin/ospf6d/kroute.c
new file mode 100644
index 00000000000..fd0d99ec4a4
--- /dev/null
+++ b/usr.sbin/ospf6d/kroute.c
@@ -0,0 +1,1480 @@
+/* $OpenBSD: kroute.c,v 1.1 2007/10/08 10:44:50 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 <sys/uio.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 "ospf6d.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 kroute_node *next;
+};
+
+struct kif_node {
+ RB_ENTRY(kif_node) entry;
+ TAILQ_HEAD(, kif_addr) addrs;
+ struct kif k;
+};
+
+void kr_redist_remove(struct kroute_node *, struct kroute_node *);
+int kr_redist_eval(struct kroute *, struct rroute *);
+void kr_redistribute(struct kroute_node *);
+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, u_int8_t);
+struct kroute_node *kroute_matchgw(struct kroute_node *, struct in_addr);
+int kroute_insert(struct kroute_node *);
+int kroute_remove(struct kroute_node *);
+void kroute_clear(void);
+
+struct kif_node *kif_find(u_short);
+struct kif_node *kif_insert(u_short);
+int kif_remove(struct kif_node *);
+void kif_clear(void);
+struct kif *kif_update(u_short, int, struct if_data *,
+ struct sockaddr_dl *);
+int kif_validate(u_short);
+
+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_newaddr(u_short, struct sockaddr_in6 *,
+ struct sockaddr_in6 *, struct sockaddr_in6 *);
+void if_announce(void *);
+
+int send_rtmsg(int, int, struct kroute *);
+int dispatch_rtmsg(void);
+int fetchtable(void);
+int fetchifs(u_short);
+
+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;
+
+ kroute->rtlabel = rtlabel_tag2id(kroute->ext_tag);
+
+ if ((kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen)) !=
+ NULL) {
+ if (!(kr->r.flags & F_KERNEL))
+ action = RTM_CHANGE;
+ else { /* a non-ospf route already exists. not a problem */
+ if (!(kr->r.flags & F_BGPD_INSERTED)) {
+ do {
+ kr->r.flags |= F_OSPFD_INSERTED;
+ kr = kr->next;
+ } while (kr);
+ return (0);
+ }
+ /*
+ * XXX as long as there is no multipath support in
+ * bgpd this is safe else we end up in a bad situation.
+ */
+ /*
+ * ospf route has higher pref
+ * - reset flags to the ospf ones
+ * - use RTM_CHANGE
+ * - zero out ifindex (this is no longer relevant)
+ */
+ action = RTM_CHANGE;
+ kr->r.flags = kroute->flags | F_OSPFD_INSERTED;
+ kr->r.ifindex = 0;
+ rtlabel_unref(kr->r.rtlabel);
+ kr->r.ext_tag = kroute->ext_tag;
+ kr->r.rtlabel = kroute->rtlabel;
+ }
+ }
+
+ /* nexthop within 127/8 -> ignore silently */
+ if ((kroute->nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
+ htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
+ return (0);
+
+ /*
+ * Ingnore updates that did not change the route.
+ * Currently only the nexthop can change.
+ */
+ if (kr && kr->r.nexthop.s_addr == kroute->nexthop.s_addr)
+ 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.prefixlen = kroute->prefixlen;
+ kr->r.nexthop.s_addr = kroute->nexthop.s_addr;
+ kr->r.flags = kroute->flags | F_OSPFD_INSERTED;
+ kr->r.ext_tag = kroute->ext_tag;
+ kr->r.rtlabel = kroute->rtlabel;
+
+ 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->prefixlen)) ==
+ NULL)
+ return (0);
+
+ if (!(kr->r.flags & F_OSPFD_INSERTED))
+ return (0);
+
+ if (kr->r.flags & F_KERNEL) {
+ /* remove F_OSPFD_INSERTED flag, route still exists in kernel */
+ do {
+ kr->r.flags &= ~F_OSPFD_INSERTED;
+ kr = kr->next;
+ } while (kr);
+ 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;
+ struct kroute_node *kn;
+ 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) {
+ kn = kr;
+ do {
+ main_imsg_compose_ospfe(IMSG_CTL_KROUTE,
+ imsg->hdr.pid,
+ &kn->r, sizeof(kn->r));
+ } while ((kn = kn->next) != NULL);
+ }
+ 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_ospfe(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_ospfe(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_ospfe(IMSG_CTL_IFINFO,
+ pid, &kif->k, sizeof(kif->k));
+ }
+
+ main_imsg_compose_ospfe(IMSG_CTL_END, pid, NULL, 0);
+}
+
+void
+kr_redist_remove(struct kroute_node *kh, struct kroute_node *kn)
+{
+ struct rroute rr;
+
+ /* was the route redistributed? */
+ if ((kn->r.flags & F_REDISTRIBUTED) == 0)
+ return;
+
+ /* remove redistributed flag */
+ kn->r.flags &= ~F_REDISTRIBUTED;
+ rr.kr = kn->r;
+ rr.metric = DEFAULT_REDIST_METRIC; /* some dummy value */
+
+ /* probably inform the RDE (check if no other path is redistributed) */
+ for (kn = kh; kn; kn = kn->next)
+ if (kn->r.flags & F_REDISTRIBUTED)
+ break;
+
+ if (kn == NULL)
+ main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &rr,
+ sizeof(struct rroute));
+}
+
+int
+kr_redist_eval(struct kroute *kr, struct rroute *rr)
+{
+ u_int32_t a, metric = 0;
+
+ /* Only non-ospfd routes are considered for redistribution. */
+ if (!(kr->flags & F_KERNEL))
+ goto dont_redistribute;
+
+ /* Dynamic routes are not redistributable. */
+ if (kr->flags & F_DYNAMIC)
+ goto dont_redistribute;
+
+ /* interface is not up and running so don't announce */
+ if (kr->flags & F_DOWN)
+ goto dont_redistribute;
+
+ /*
+ * 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)
+ goto dont_redistribute;
+ /*
+ * Consider networks with nexthop loopback as not redistributable.
+ */
+ if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK))
+ goto dont_redistribute;
+
+ /* Should we redistrubute this route? */
+ if (!ospf_redistribute(kr, &metric))
+ goto dont_redistribute;
+
+ /* prefix should be redistributed */
+ kr->flags |= F_REDISTRIBUTED;
+ /*
+ * only on of all multipath routes can be redistributed so
+ * redistribute the best one.
+ */
+ if (rr->metric > metric) {
+ rr->kr = *kr;
+ rr->metric = metric;
+ }
+ return (1);
+
+dont_redistribute:
+ /* was the route redistributed? */
+ if ((kr->flags & F_REDISTRIBUTED) == 0)
+ return (0);
+
+ kr->flags &= ~F_REDISTRIBUTED;
+ return (1);
+}
+
+void
+kr_redistribute(struct kroute_node *kh)
+{
+ struct kroute_node *kn;
+ struct rroute rr;
+ int redistribute = 0;
+
+ bzero(&rr, sizeof(rr));
+ rr.metric = UINT_MAX;
+ for (kn = kh; kn; kn = kn->next)
+ if (kr_redist_eval(&kn->r, &rr))
+ redistribute = 1;
+
+ if (!redistribute)
+ return;
+
+ if (rr.kr.flags & F_REDISTRIBUTED) {
+ main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, &rr,
+ sizeof(struct rroute));
+ } else {
+ rr.metric = DEFAULT_REDIST_METRIC; /* some dummy value */
+ rr.kr = kh->r;
+ main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &rr,
+ sizeof(struct rroute));
+ }
+}
+
+void
+kr_reload(void)
+{
+ struct kroute_node *kr, *kn;
+ u_int32_t dummy;
+ int r;
+
+ RB_FOREACH(kr, kroute_tree, &krt) {
+ for (kn = kr; kn; kn = kn->next) {
+ r = ospf_redistribute(&kn->r, &dummy);
+ /*
+ * if it is redistributed, redistribute again metric
+ * may have changed.
+ */
+ if ((kn->r.flags & F_REDISTRIBUTED && !r) || r)
+ break;
+ }
+ if (kn) {
+ /*
+ * kr_redistribute copes with removes and RDE with
+ * duplicates
+ */
+ kr_redistribute(kr);
+ }
+ }
+}
+
+/* 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 (a->r.prefixlen < b->r.prefixlen)
+ return (-1);
+ if (a->r.prefixlen > b->r.prefixlen)
+ 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, u_int8_t prefixlen)
+{
+ struct kroute_node s;
+
+ s.r.prefix.s_addr = prefix;
+ s.r.prefixlen = prefixlen;
+
+ return (RB_FIND(kroute_tree, &krt, &s));
+}
+
+struct kroute_node *
+kroute_matchgw(struct kroute_node *kr, struct in_addr nh)
+{
+ in_addr_t nexthop;
+
+ nexthop = nh.s_addr;
+
+ while (kr) {
+ if (kr->r.nexthop.s_addr == nexthop)
+ return (kr);
+ kr = kr->next;
+ }
+
+ return (NULL);
+}
+
+int
+kroute_insert(struct kroute_node *kr)
+{
+ struct kroute_node *krm;
+
+ if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) {
+ /*
+ * Multipath route, add at end of list and clone the
+ * ospfd inserted flag.
+ */
+ kr->r.flags |= krm->r.flags & F_OSPFD_INSERTED;
+ while (krm->next != NULL)
+ krm = krm->next;
+ krm->next = kr;
+ kr->next = NULL; /* to be sure */
+ } else
+ krm = kr;
+
+ if (!(kr->r.flags & F_KERNEL)) {
+ /* don't validate or redistribute ospf 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(krm);
+ return (0);
+}
+
+int
+kroute_remove(struct kroute_node *kr)
+{
+ struct kroute_node *krm;
+
+ if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) {
+ log_warnx("kroute_remove failed to find %s/%u",
+ inet_ntoa(kr->r.prefix), kr->r.prefixlen);
+ return (-1);
+ }
+
+ if (krm == kr) {
+ /* head element */
+ if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) {
+ log_warnx("kroute_remove failed for %s/%u",
+ inet_ntoa(kr->r.prefix), kr->r.prefixlen);
+ return (-1);
+ }
+ if (kr->next != NULL) {
+ if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) {
+ log_warnx("kroute_remove failed to add %s/%u",
+ inet_ntoa(kr->r.prefix), kr->r.prefixlen);
+ return (-1);
+ }
+ }
+ } else {
+ /* somewhere in the list */
+ while (krm->next != kr && krm->next != NULL)
+ krm = krm->next;
+ if (krm->next == NULL) {
+ log_warnx("kroute_remove multipath list corrupted "
+ "for %s/%u", inet_ntoa(kr->r.prefix),
+ kr->r.prefixlen);
+ return (-1);
+ }
+ krm->next = kr->next;
+ }
+
+ kr_redist_remove(krm, kr);
+ rtlabel_unref(kr->r.rtlabel);
+
+ 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(u_short 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 in6_addr *addr, struct kif_addr **kap)
+{
+ struct kif_node *kif;
+ struct kif_addr *ka;
+
+ RB_FOREACH(kif, kif_tree, &kit)
+ if (!strcmp(ifname, kif->k.ifname)) {
+ ka = TAILQ_FIRST(&kif->addrs);
+ if (!IN6_IS_ADDR_UNSPECIFIED(addr)) {
+ TAILQ_FOREACH(ka, &kif->addrs, entry) {
+ if (IN6_ARE_ADDR_EQUAL(addr, &ka->addr))
+ break;
+ }
+ }
+ if (kap != NULL)
+ *kap = ka;
+ return (&kif->k);
+ }
+
+ return (NULL);
+}
+
+struct kif_node *
+kif_insert(u_short ifindex)
+{
+ struct kif_node *kif;
+
+ if ((kif = calloc(1, sizeof(struct kif_node))) == NULL)
+ return (NULL);
+
+ kif->k.ifindex = ifindex;
+ TAILQ_INIT(&kif->addrs);
+
+ if (RB_INSERT(kif_tree, &kit, kif) != NULL)
+ fatalx("kif_insert: RB_INSERT");
+
+ return (kif);
+}
+
+int
+kif_remove(struct kif_node *kif)
+{
+ struct kif_addr *ka;
+
+ if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
+ log_warnx("RB_REMOVE(kif_tree, &kit, kif)");
+ return (-1);
+ }
+
+ while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) {
+ TAILQ_REMOVE(&kif->addrs, ka, entry);
+ free(ka);
+ }
+ free(kif);
+ return (0);
+}
+
+void
+kif_clear(void)
+{
+ struct kif_node *kif;
+
+ while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
+ kif_remove(kif);
+}
+
+struct kif *
+kif_update(u_short ifindex, int flags, struct if_data *ifd,
+ struct sockaddr_dl *sdl)
+{
+ struct kif_node *kif;
+
+ if ((kif = kif_find(ifindex)) == NULL)
+ if ((kif = kif_insert(ifindex)) == NULL)
+ return (NULL);
+
+ 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;
+ kif->k.mtu = ifd->ifi_mtu;
+
+ if (sdl && sdl->sdl_family == AF_LINK) {
+ 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() */
+ }
+
+ return (&kif->k);
+}
+
+int
+kif_validate(u_short 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), 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.prefixlen = 8;
+ 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 kroute_node *kr, *tkr;
+ struct kif *kif;
+ u_int8_t reachable;
+
+ if ((kif = kif_update(ifindex, flags, ifd, NULL)) == NULL) {
+ log_warn("if_change: kif_update(%u)", ifindex);
+ return;
+ }
+
+ reachable = (kif->flags & IFF_UP) &&
+ (LINK_STATE_IS_UP(kif->link_state) ||
+ (kif->link_state == LINK_STATE_UNKNOWN &&
+ kif->media_type != IFT_CARP));
+
+ if (reachable == kif->nh_reachable)
+ return; /* nothing changed wrt nexthop validity */
+
+ kif->nh_reachable = reachable;
+
+ /* notify ospfe about interface link state */
+ main_imsg_compose_ospfe(IMSG_IFINFO, 0, kif, sizeof(struct kif));
+
+ /* update redistribute list */
+ RB_FOREACH(kr, kroute_tree, &krt) {
+ for (tkr = kr; tkr != NULL; tkr = tkr->next) {
+ if (tkr->r.ifindex == ifindex) {
+ if (reachable)
+ tkr->r.flags &= ~F_DOWN;
+ else
+ tkr->r.flags |= F_DOWN;
+
+ }
+ }
+ kr_redistribute(kr);
+ }
+}
+
+void
+if_newaddr(u_short ifindex, struct sockaddr_in6 *ifa, struct sockaddr_in6 *mask,
+ struct sockaddr_in6 *brd)
+{
+ struct kif_node *kif;
+ struct kif_addr *ka;
+
+ if (ifa == NULL || ifa->sin6_family != AF_INET6)
+ return;
+ if ((kif = kif_find(ifindex)) == NULL) {
+ log_warnx("if_newaddr: corresponding if %i not found", ifindex);
+ return;
+ }
+ if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL)
+ fatal("if_newaddr");
+ ka->addr = ifa->sin6_addr;
+ if (mask)
+ ka->mask = mask->sin6_addr;
+ else
+ bzero(&ka->mask, sizeof(ka->mask));
+ if (brd)
+ ka->dstbrd = brd->sin6_addr;
+ else
+ bzero(&ka->dstbrd, sizeof(ka->dstbrd));
+
+ log_debug("if_newaddr: ifindex %u, addr %s", ifindex,
+ log_in6addr(&ka->addr));
+ TAILQ_INSERT_TAIL(&kif->addrs, ka, entry);
+}
+
+void
+if_announce(void *msg)
+{
+ struct if_announcemsghdr *ifan;
+ struct kif_node *kif;
+
+ ifan = msg;
+
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ kif = kif_insert(ifan->ifan_index);
+ strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname));
+ 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 iovec iov[5];
+ struct rt_msghdr hdr;
+ struct sockaddr_in prefix;
+ struct sockaddr_in nexthop;
+ struct sockaddr_in mask;
+ struct sockaddr_rtlabel sa_rl;
+ int iovcnt = 0;
+ const char *label;
+
+ if (kr_state.fib_sync == 0)
+ return (0);
+
+ /* initialize header */
+ bzero(&hdr, sizeof(hdr));
+ hdr.rtm_version = RTM_VERSION;
+ hdr.rtm_type = action;
+ hdr.rtm_flags = RTF_PROTO2;
+ if (action == RTM_CHANGE) /* force PROTO2 reset the other flags */
+ hdr.rtm_fmask = RTF_PROTO2|RTF_PROTO1|RTF_REJECT|RTF_BLACKHOLE;
+ hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */
+ hdr.rtm_msglen = sizeof(hdr);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &hdr;
+ iov[iovcnt++].iov_len = sizeof(hdr);
+
+ bzero(&prefix, sizeof(prefix));
+ prefix.sin_len = sizeof(prefix);
+ prefix.sin_family = AF_INET;
+ prefix.sin_addr.s_addr = kroute->prefix.s_addr;
+ /* adjust header */
+ hdr.rtm_addrs |= RTA_DST;
+ hdr.rtm_msglen += sizeof(prefix);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &prefix;
+ iov[iovcnt++].iov_len = sizeof(prefix);
+
+ if (kroute->nexthop.s_addr != 0) {
+ bzero(&nexthop, sizeof(nexthop));
+ nexthop.sin_len = sizeof(nexthop);
+ nexthop.sin_family = AF_INET;
+ nexthop.sin_addr.s_addr = kroute->nexthop.s_addr;
+ /* adjust header */
+ hdr.rtm_flags |= RTF_GATEWAY;
+ hdr.rtm_addrs |= RTA_GATEWAY;
+ hdr.rtm_msglen += sizeof(nexthop);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &nexthop;
+ iov[iovcnt++].iov_len = sizeof(nexthop);
+ }
+
+ bzero(&mask, sizeof(mask));
+ mask.sin_len = sizeof(mask);
+ mask.sin_family = AF_INET;
+ mask.sin_addr.s_addr = prefixlen2mask(kroute->prefixlen);
+ /* adjust header */
+ hdr.rtm_addrs |= RTA_NETMASK;
+ hdr.rtm_msglen += sizeof(mask);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &mask;
+ iov[iovcnt++].iov_len = sizeof(mask);
+
+ if (kroute->rtlabel != 0) {
+ sa_rl.sr_len = sizeof(sa_rl);
+ sa_rl.sr_family = AF_UNSPEC;
+ label = rtlabel_id2name(kroute->rtlabel);
+ if (strlcpy(sa_rl.sr_label, label,
+ sizeof(sa_rl.sr_label)) >= sizeof(sa_rl.sr_label)) {
+ log_warnx("send_rtmsg: invalid rtlabel");
+ return (-1);
+ }
+ /* adjust header */
+ hdr.rtm_addrs |= RTA_LABEL;
+ hdr.rtm_msglen += sizeof(sa_rl);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &sa_rl;
+ iov[iovcnt++].iov_len = sizeof(sa_rl);
+ }
+
+retry:
+ if (writev(fd, iov, iovcnt) == -1) {
+ switch (errno) {
+ case ESRCH:
+ if (hdr.rtm_type == RTM_CHANGE) {
+ hdr.rtm_type = RTM_ADD;
+ goto retry;
+ } else if (hdr.rtm_type == RTM_DELETE) {
+ log_info("route %s/%u vanished before delete",
+ inet_ntoa(kroute->prefix),
+ kroute->prefixlen);
+ return (0);
+ } else {
+ log_warnx("send_rtmsg: action %u, "
+ "prefix %s/%u: %s", hdr.rtm_type,
+ inet_ntoa(kroute->prefix),
+ kroute->prefixlen, strerror(errno));
+ return (0);
+ }
+ break;
+ default:
+ log_warnx("send_rtmsg: action %u, prefix %s/%u: %s",
+ hdr.rtm_type, inet_ntoa(kroute->prefix),
+ kroute->prefixlen, strerror(errno));
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+int
+fetchtable(void)
+{
+ size_t len;
+ int mib[7];
+ char *buf, *next, *lim;
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_in *sa_in;
+ struct sockaddr_rtlabel *label;
+ struct kroute_node *kr;
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ mib[6] = 0; /* rtableid */
+
+ if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchtable");
+ return (-1);
+ }
+ if (sysctl(mib, 7, 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;
+
+ 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 (sa_in != NULL) {
+ if (sa_in->sin_len == 0)
+ break;
+ kr->r.prefixlen =
+ mask2prefixlen(sa_in->sin_addr.s_addr);
+ } else if (rtm->rtm_flags & RTF_HOST)
+ kr->r.prefixlen = 32;
+ else
+ kr->r.prefixlen =
+ prefixlen_classful(kr->r.prefix.s_addr);
+ break;
+ default:
+ free(kr);
+ continue;
+ }
+
+ kr->r.ifindex = rtm->rtm_index;
+ 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_PROTO2) {
+ send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
+ free(kr);
+ } else {
+ if ((label = (struct sockaddr_rtlabel *)
+ rti_info[RTAX_LABEL]) != NULL) {
+ kr->r.rtlabel =
+ rtlabel_name2id(label->sr_label);
+ kr->r.ext_tag =
+ rtlabel_id2tag(kr->r.rtlabel);
+ }
+ kroute_insert(kr);
+ }
+
+ }
+ free(buf);
+ return (0);
+}
+
+int
+fetchifs(u_short ifindex)
+{
+ size_t len;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct rt_msghdr *rtm;
+ struct if_msghdr ifm;
+ struct ifa_msghdr *ifam;
+ struct kif *kif = NULL;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ 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 += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ if (rtm->rtm_version != RTM_VERSION)
+ continue;
+ switch (rtm->rtm_type) {
+ case RTM_IFINFO:
+ bcopy(rtm, &ifm, sizeof ifm);
+ sa = (struct sockaddr *)(next + sizeof(ifm));
+ get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
+
+ if ((kif = kif_update(ifm.ifm_index,
+ ifm.ifm_flags, &ifm.ifm_data,
+ (struct sockaddr_dl *)rti_info[RTAX_IFP])) == NULL)
+ fatal("fetchifs");
+
+ kif->nh_reachable = (kif->flags & IFF_UP) &&
+ (LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state) ||
+ (ifm.ifm_data.ifi_link_state ==
+ LINK_STATE_UNKNOWN &&
+ ifm.ifm_data.ifi_type != IFT_CARP));
+ break;
+ case RTM_NEWADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
+ RTA_BRD)) == 0)
+ break;
+ sa = (struct sockaddr *)(ifam + 1);
+ get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
+
+ if_newaddr(ifam->ifam_index,
+ (struct sockaddr_in6 *)rti_info[RTAX_IFA],
+ (struct sockaddr_in6 *)rti_info[RTAX_NETMASK],
+ (struct sockaddr_in6 *)rti_info[RTAX_BRD]);
+ break;
+ }
+ }
+ 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 ifa_msghdr *ifam;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_in *sa_in;
+ struct sockaddr_rtlabel *label;
+ struct kroute_node *kr, *okr;
+ struct in_addr prefix, nexthop;
+ u_int8_t prefixlen;
+ int flags, mpath;
+ 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;
+ prefixlen = 0;
+ flags = F_KERNEL;
+ nexthop.s_addr = 0;
+ mpath = 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_tableid != 0)
+ continue;
+
+ if (rtm->rtm_pid == kr_state.pid) /* caused by us */
+ continue;
+
+ if (rtm->rtm_errno) /* failed attempts... */
+ continue;
+
+ if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
+ continue;
+
+#ifdef RTF_MPATH
+ if (rtm->rtm_flags & RTF_MPATH)
+ mpath = 1;
+#endif
+ 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)
+ prefixlen = mask2prefixlen(
+ sa_in->sin_addr.s_addr);
+ } else if (rtm->rtm_flags & RTF_HOST)
+ prefixlen = 32;
+ else
+ prefixlen =
+ 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;
+ 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), prefixlen);
+ continue;
+ }
+
+ if ((okr = kroute_find(prefix.s_addr, prefixlen)) !=
+ NULL) {
+ /* just add new multipath routes */
+ if (mpath && rtm->rtm_type == RTM_ADD)
+ goto add;
+ /* get the correct route */
+ kr = okr;
+ if (mpath && (kr = kroute_matchgw(okr,
+ nexthop)) == NULL) {
+ log_warnx("dispatch_rtmsg mpath route"
+ " not found");
+ /* add routes we missed out earlier */
+ goto add;
+ }
+
+ /*
+ * ospf route overridden by kernel. Preference
+ * of the route is not checked because this is
+ * forced -- most probably by a user.
+ */
+ if (kr->r.flags & F_OSPFD_INSERTED)
+ flags |= F_OSPFD_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;
+
+ rtlabel_unref(kr->r.rtlabel);
+ kr->r.rtlabel = 0;
+ kr->r.ext_tag = 0;
+ if ((label = (struct sockaddr_rtlabel *)
+ rti_info[RTAX_LABEL]) != NULL) {
+ kr->r.rtlabel =
+ rtlabel_name2id(label->sr_label);
+ kr->r.ext_tag =
+ rtlabel_id2tag(kr->r.rtlabel);
+ }
+
+ 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(okr);
+ } else {
+add:
+ 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.prefixlen = prefixlen;
+ kr->r.nexthop.s_addr = nexthop.s_addr;
+ kr->r.flags = flags;
+ kr->r.ifindex = ifindex;
+
+ if ((label = (struct sockaddr_rtlabel *)
+ rti_info[RTAX_LABEL]) != NULL) {
+ kr->r.rtlabel =
+ rtlabel_name2id(label->sr_label);
+ kr->r.ext_tag =
+ rtlabel_id2tag(kr->r.rtlabel);
+ }
+
+ kroute_insert(kr);
+ }
+ break;
+ case RTM_DELETE:
+ if ((kr = kroute_find(prefix.s_addr, prefixlen)) ==
+ NULL)
+ continue;
+ if (!(kr->r.flags & F_KERNEL))
+ continue;
+ /* get the correct route */
+ okr = kr;
+ if (mpath &&
+ (kr = kroute_matchgw(kr, nexthop)) == NULL) {
+ log_warnx("dispatch_rtmsg mpath route"
+ " not found");
+ return (-1);
+ }
+ /*
+ * last route is getting removed request the
+ * ospf route from the RDE to insert instead
+ */
+ if (okr == kr && kr->next == NULL &&
+ kr->r.flags & F_OSPFD_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_NEWADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
+ RTA_BRD)) == 0)
+ break;
+ sa = (struct sockaddr *)(ifam + 1);
+ get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
+
+ if_newaddr(ifam->ifam_index,
+ (struct sockaddr_in6 *)rti_info[RTAX_IFA],
+ (struct sockaddr_in6 *)rti_info[RTAX_NETMASK],
+ (struct sockaddr_in6 *)rti_info[RTAX_BRD]);
+ break;
+ case RTM_IFANNOUNCE:
+ if_announce(next);
+ break;
+ default:
+ /* ignore for now */
+ break;
+ }
+ }
+ return (0);
+}
diff --git a/usr.sbin/ospf6d/log.c b/usr.sbin/ospf6d/log.c
new file mode 100644
index 00000000000..911aed34235
--- /dev/null
+++ b/usr.sbin/ospf6d/log.c
@@ -0,0 +1,305 @@
+/* $OpenBSD: log.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2006 Claudio Jeker <claudio@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 <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "ospf6d.h"
+#include "log.h"
+
+static const char * const procnames[] = {
+ "parent",
+ "ospfe",
+ "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[ospfd_process],
+ strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal in %s: %s: %s",
+ procnames[ospfd_process], emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal in %s: %s",
+ procnames[ospfd_process], emsg);
+
+ if (ospfd_process == PROC_MAIN)
+ exit(1);
+ else /* parent copes via SIGCHLD */
+ _exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
+
+const char *
+log_in6addr(const struct in6_addr *addr)
+{
+ struct sockaddr_in6 sa_in6;
+ u_int16_t tmp16;
+
+ bzero(&sa_in6, sizeof(sa_in6));
+ sa_in6.sin6_len = sizeof(sa_in6);
+ sa_in6.sin6_family = AF_INET6;
+ memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
+
+ /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
+ if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) {
+ memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
+ sa_in6.sin6_scope_id = ntohs(tmp16);
+ sa_in6.sin6_addr.s6_addr[2] = 0;
+ sa_in6.sin6_addr.s6_addr[3] = 0;
+ }
+
+ return (log_sockaddr((struct sockaddr *)&sa_in6));
+}
+
+const char *
+log_sockaddr(struct sockaddr *sa)
+{
+ static char buf[NI_MAXHOST];
+
+ if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0,
+ NI_NUMERICHOST))
+ return ("(unknown)");
+ else
+ return (buf);
+}
+
+/* names */
+const char *
+nbr_state_name(int state)
+{
+ switch (state) {
+ case NBR_STA_DOWN:
+ return ("DOWN");
+ case NBR_STA_ATTEMPT:
+ return ("ATTMP");
+ case NBR_STA_INIT:
+ return ("INIT");
+ case NBR_STA_2_WAY:
+ return ("2-WAY");
+ case NBR_STA_XSTRT:
+ return ("EXSTA");
+ case NBR_STA_SNAP:
+ return ("SNAP");
+ case NBR_STA_XCHNG:
+ return ("EXCHG");
+ case NBR_STA_LOAD:
+ return ("LOAD");
+ case NBR_STA_FULL:
+ return ("FULL");
+ default:
+ return ("UNKNW");
+ }
+}
+
+const char *
+if_state_name(int state)
+{
+ switch (state) {
+ case IF_STA_DOWN:
+ return ("DOWN");
+ case IF_STA_LOOPBACK:
+ return ("LOOP");
+ case IF_STA_WAITING:
+ return ("WAIT");
+ case IF_STA_POINTTOPOINT:
+ return ("P2P");
+ case IF_STA_DROTHER:
+ return ("OTHER");
+ case IF_STA_BACKUP:
+ return ("BCKUP");
+ case IF_STA_DR:
+ return ("DR");
+ default:
+ return ("UNKNW");
+ }
+}
+
+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");
+ case IF_TYPE_VIRTUALLINK:
+ return ("VIRTUALLINK");
+ }
+ /* NOTREACHED */
+ return ("UNKNOWN");
+}
+
+const char *
+dst_type_name(enum dst_type type)
+{
+ switch (type) {
+ case DT_NET:
+ return ("Network");
+ case DT_RTR:
+ return ("Router");
+ }
+ /* NOTREACHED */
+ return ("unknown");
+}
+
+const char *
+path_type_name(enum path_type type)
+{
+ switch (type) {
+ case PT_INTRA_AREA:
+ return ("Intra-Area");
+ case PT_INTER_AREA:
+ return ("Inter-Area");
+ case PT_TYPE1_EXT:
+ return ("Type 1 ext");
+ case PT_TYPE2_EXT:
+ return ("Type 2 ext");
+ }
+ /* NOTREACHED */
+ return ("unknown");
+}
diff --git a/usr.sbin/ospf6d/log.h b/usr.sbin/ospf6d/log.h
new file mode 100644
index 00000000000..f9541236d0a
--- /dev/null
+++ b/usr.sbin/ospf6d/log.h
@@ -0,0 +1,36 @@
+/* $OpenBSD: log.h,v 1.1 2007/10/08 10:44:50 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 *log_in6addr(const struct in6_addr *);
+const char *log_sockaddr(struct sockaddr *);
+
+#endif /* _LOG_H_ */
diff --git a/usr.sbin/ospf6d/lsack.c b/usr.sbin/ospf6d/lsack.c
new file mode 100644
index 00000000000..945d34467b5
--- /dev/null
+++ b/usr.sbin/ospf6d/lsack.c
@@ -0,0 +1,297 @@
+/* $OpenBSD: lsack.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2007 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ospf6d.h"
+#include "ospf6.h"
+#include "log.h"
+#include "ospfe.h"
+
+void start_ls_ack_tx_timer_now(struct iface *);
+
+/* link state acknowledgement packet handling */
+int
+send_ls_ack(struct iface *iface, struct in6_addr addr, void *data, size_t len)
+{
+ struct sockaddr_in6 dst;
+ struct buf *buf;
+ int ret;
+
+ /* XXX READ_BUF_SIZE */
+ if ((buf = buf_dynamic(PKG_DEF_SIZE, READ_BUF_SIZE)) == NULL)
+ fatal("send_ls_ack");
+
+ dst.sin6_family = AF_INET6;
+ dst.sin6_len = sizeof(struct sockaddr_in6);
+ dst.sin6_addr = addr; /* XXX */
+
+ /* OSPF header */
+ if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_ACK))
+ goto fail;
+
+ /* LS ack(s) */
+ if (buf_add(buf, data, len))
+ goto fail;
+
+ /* calculate checksum */
+ if (upd_ospf_hdr(buf, iface))
+ goto fail;
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_ls_ack");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_ls_ack(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct lsa_hdr lsa_hdr;
+
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_INIT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_XSTRT:
+ case NBR_STA_SNAP:
+ log_debug("recv_ls_ack: packet ignored in state %s, "
+ "neighbor ID %s", nbr_state_name(nbr->state),
+ inet_ntoa(nbr->id));
+ break;
+ case NBR_STA_XCHNG:
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ while (len >= sizeof(lsa_hdr)) {
+ memcpy(&lsa_hdr, buf, sizeof(lsa_hdr));
+
+ if (lsa_hdr_check(nbr, &lsa_hdr)) {
+ /* try both list in case of DROTHER */
+ if (nbr->iface->state & IF_STA_DROTHER)
+ (void)ls_retrans_list_del(
+ nbr->iface->self, &lsa_hdr);
+ (void)ls_retrans_list_del(nbr, &lsa_hdr);
+ }
+
+ buf += sizeof(lsa_hdr);
+ len -= sizeof(lsa_hdr);
+ }
+ if (len > 0) {
+ log_warnx("recv_ls_ack: bad packet size, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ return;
+ }
+ break;
+ default:
+ fatalx("recv_ls_ack: unknown neighbor state");
+ }
+}
+
+int
+lsa_hdr_check(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
+{
+ /* invalid age */
+ if ((ntohs(lsa_hdr->age) < 1) || (ntohs(lsa_hdr->age) > MAX_AGE)) {
+ log_debug("lsa_hdr_check: invalid age, neighbor ID %s",
+ inet_ntoa(nbr->id));
+ return (0);
+ }
+
+ /* invalid type */
+ switch (lsa_hdr->type) {
+ case LSA_TYPE_ROUTER:
+ case LSA_TYPE_NETWORK:
+ case LSA_TYPE_SUM_NETWORK:
+ case LSA_TYPE_SUM_ROUTER:
+ case LSA_TYPE_EXTERNAL:
+ break;
+ default:
+ log_debug("lsa_hdr_check: invalid LSA type %d, neighbor ID %s",
+ lsa_hdr->type, inet_ntoa(nbr->id));
+ return (0);
+ }
+
+ /* invalid sequence number */
+ if (ntohl(lsa_hdr->seq_num) == RESV_SEQ_NUM) {
+ log_debug("ls_hdr_check: invalid seq num, neighbor ID %s",
+ inet_ntoa(nbr->id));
+ return (0);
+ }
+
+ return (1);
+}
+
+/* link state ack list */
+void
+ls_ack_list_add(struct iface *iface, struct lsa_hdr *lsa)
+{
+ struct lsa_entry *le;
+
+ if (lsa == NULL)
+ fatalx("ls_ack_list_add: no LSA header");
+
+ if ((le = calloc(1, sizeof(*le))) == NULL)
+ fatal("ls_ack_list_add");
+
+ if (ls_ack_list_empty(iface))
+ start_ls_ack_tx_timer(iface);
+
+ TAILQ_INSERT_TAIL(&iface->ls_ack_list, le, entry);
+ le->le_lsa = lsa;
+ iface->ls_ack_cnt++;
+
+ /* reschedule now if we have enough for a full packet */
+ if (iface->ls_ack_cnt >
+ ((iface->mtu - PACKET_HDR) / sizeof(struct lsa_hdr))) {
+ start_ls_ack_tx_timer_now(iface);
+ }
+
+}
+
+void
+ls_ack_list_free(struct iface *iface, struct lsa_entry *le)
+{
+ TAILQ_REMOVE(&iface->ls_ack_list, le, entry);
+ free(le->le_lsa);
+ free(le);
+
+ iface->ls_ack_cnt--;
+}
+
+void
+ls_ack_list_clr(struct iface *iface)
+{
+ struct lsa_entry *le;
+
+ while ((le = TAILQ_FIRST(&iface->ls_ack_list)) != NULL) {
+ TAILQ_REMOVE(&iface->ls_ack_list, le, entry);
+ free(le->le_lsa);
+ free(le);
+ }
+ iface->ls_ack_cnt = 0;
+}
+
+int
+ls_ack_list_empty(struct iface *iface)
+{
+ return (TAILQ_EMPTY(&iface->ls_ack_list));
+}
+
+/* timers */
+/* ARGSUSED */
+void
+ls_ack_tx_timer(int fd, short event, void *arg)
+{
+ struct in6_addr addr;
+ struct iface *iface = arg;
+ struct lsa_hdr *lsa_hdr;
+ struct lsa_entry *le, *nle;
+ struct nbr *nbr;
+ char *buf;
+ char *ptr;
+ int cnt = 0;
+
+ if ((buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("ls_ack_tx_timer");
+
+ while (!ls_ack_list_empty(iface)) {
+ ptr = buf;
+ cnt = 0;
+ for (le = TAILQ_FIRST(&iface->ls_ack_list); le != NULL &&
+ (ptr - buf < iface->mtu - PACKET_HDR); le = nle) {
+ nle = TAILQ_NEXT(le, entry);
+ memcpy(ptr, le->le_lsa, sizeof(struct lsa_hdr));
+ ptr += sizeof(*lsa_hdr);
+ ls_ack_list_free(iface, le);
+ cnt++;
+ }
+
+ /* send LS ack(s) but first set correct destination */
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_pton(AF_INET6, AllSPFRouters, &addr);
+ send_ls_ack(iface, addr, buf, ptr - buf);
+ break;
+ case IF_TYPE_BROADCAST:
+ if (iface->state & IF_STA_DRORBDR)
+ inet_pton(AF_INET6, AllSPFRouters, &addr);
+ else
+ inet_pton(AF_INET6, AllDRouters, &addr);
+
+ send_ls_ack(iface, addr, buf, ptr - buf);
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr == iface->self)
+ continue;
+ if (!(nbr->state & NBR_STA_FLOOD))
+ continue;
+ send_ls_ack(iface, nbr->addr, buf, ptr - buf);
+ }
+ break;
+ default:
+ fatalx("lsa_ack_tx_timer: unknown interface type");
+ }
+ }
+
+ free(buf);
+}
+
+void
+start_ls_ack_tx_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = iface->rxmt_interval / 2;
+
+ if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1)
+ fatal("start_ls_ack_tx_timer");
+}
+
+void
+start_ls_ack_tx_timer_now(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1)
+ fatal("start_ls_ack_tx_timer_now");
+}
+
+void
+stop_ls_ack_tx_timer(struct iface *iface)
+{
+ if (evtimer_del(&iface->lsack_tx_timer) == -1)
+ fatal("stop_ls_ack_tx_timer");
+}
diff --git a/usr.sbin/ospf6d/lsreq.c b/usr.sbin/ospf6d/lsreq.c
new file mode 100644
index 00000000000..76c92129b65
--- /dev/null
+++ b/usr.sbin/ospf6d/lsreq.c
@@ -0,0 +1,251 @@
+/* $OpenBSD: lsreq.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2007 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include "ospf6d.h"
+#include "ospf6.h"
+#include "log.h"
+#include "ospfe.h"
+
+extern struct imsgbuf *ibuf_rde;
+
+/* link state request packet handling */
+int
+send_ls_req(struct nbr *nbr)
+{
+ struct sockaddr_in6 dst;
+ struct ls_req_hdr ls_req_hdr;
+ struct lsa_entry *le, *nle;
+ struct buf *buf;
+ int ret;
+
+ if ((buf = buf_open(nbr->iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_ls_req");
+
+ /* set destination */
+ dst.sin6_family = AF_INET6;
+ dst.sin6_len = sizeof(struct sockaddr_in6);
+
+ switch (nbr->iface->type) {
+ case IF_TYPE_POINTOPOINT:
+//XXX inet_aton(AllSPFRouters, &dst.sin_addr);
+ inet_pton(AF_INET6, AllSPFRouters, &dst.sin6_addr);
+ break;
+ case IF_TYPE_BROADCAST:
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+//XXX dst.sin_addr.s_addr = nbr->addr.s_addr;
+ dst.sin6_addr = nbr->addr;
+ break;
+ default:
+ fatalx("send_ls_req: unknown interface type");
+ }
+
+ /* OSPF header */
+ if (gen_ospf_hdr(buf, nbr->iface, PACKET_TYPE_LS_REQUEST))
+ goto fail;
+
+ /* LSA header(s), keep space for a possible md5 sum */
+ for (le = TAILQ_FIRST(&nbr->ls_req_list); le != NULL &&
+ buf->wpos + sizeof(struct ls_req_hdr) < buf->max -
+ MD5_DIGEST_LENGTH; le = nle) {
+ nbr->ls_req = nle = TAILQ_NEXT(le, entry);
+ ls_req_hdr.type = htonl(le->le_lsa->type);
+ ls_req_hdr.ls_id = le->le_lsa->ls_id;
+ ls_req_hdr.adv_rtr = le->le_lsa->adv_rtr;
+ if (buf_add(buf, &ls_req_hdr, sizeof(ls_req_hdr)))
+ goto fail;
+ }
+
+ /* calculate checksum */
+ if (upd_ospf_hdr(buf, nbr->iface))
+ goto fail;
+
+ ret = send_packet(nbr->iface, buf->buf, buf->wpos, &dst);
+
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_ls_req");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_ls_req(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_INIT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_XSTRT:
+ case NBR_STA_SNAP:
+ log_debug("recv_ls_req: packet ignored in state %s, "
+ "neighbor ID %s", nbr_state_name(nbr->state),
+ inet_ntoa(nbr->id));
+ break;
+ case NBR_STA_XCHNG:
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ imsg_compose(ibuf_rde, IMSG_LS_REQ, nbr->peerid, 0, buf, len);
+ break;
+ default:
+ fatalx("recv_ls_req: unknown neighbor state");
+ }
+}
+
+/* link state request list */
+void
+ls_req_list_add(struct nbr *nbr, struct lsa_hdr *lsa)
+{
+ struct lsa_entry *le;
+
+ if (lsa == NULL)
+ fatalx("ls_req_list_add: no LSA header");
+
+ if ((le = calloc(1, sizeof(*le))) == NULL)
+ fatal("ls_req_list_add");
+
+ TAILQ_INSERT_TAIL(&nbr->ls_req_list, le, entry);
+ le->le_lsa = lsa;
+ nbr->ls_req_cnt++;
+}
+
+struct lsa_entry *
+ls_req_list_get(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
+{
+ struct lsa_entry *le;
+
+ TAILQ_FOREACH(le, &nbr->ls_req_list, entry) {
+ if ((lsa_hdr->type == le->le_lsa->type) &&
+ (lsa_hdr->ls_id == le->le_lsa->ls_id) &&
+ (lsa_hdr->adv_rtr == le->le_lsa->adv_rtr))
+ return (le);
+ }
+ return (NULL);
+}
+
+void
+ls_req_list_free(struct nbr *nbr, struct lsa_entry *le)
+{
+ if (nbr->ls_req == le) {
+ nbr->ls_req = TAILQ_NEXT(le, entry);
+ }
+
+ TAILQ_REMOVE(&nbr->ls_req_list, le, entry);
+ free(le->le_lsa);
+ free(le);
+ nbr->ls_req_cnt--;
+
+ /* received all requested LSA(s), send a new LS req */
+ if (nbr->ls_req != NULL &&
+ nbr->ls_req == TAILQ_FIRST(&nbr->ls_req_list)) {
+ start_ls_req_tx_timer(nbr);
+ }
+
+ if (ls_req_list_empty(nbr) && nbr->dd_pending == 0)
+ nbr_fsm(nbr, NBR_EVT_LOAD_DONE);
+}
+
+void
+ls_req_list_clr(struct nbr *nbr)
+{
+ struct lsa_entry *le;
+
+ while ((le = TAILQ_FIRST(&nbr->ls_req_list)) != NULL) {
+ TAILQ_REMOVE(&nbr->ls_req_list, le, entry);
+ free(le->le_lsa);
+ free(le);
+ }
+
+ nbr->ls_req_cnt = 0;
+ nbr->ls_req = NULL;
+}
+
+int
+ls_req_list_empty(struct nbr *nbr)
+{
+ return (TAILQ_EMPTY(&nbr->ls_req_list));
+}
+
+/* timers */
+/* ARGSUSED */
+void
+ls_req_tx_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+ struct timeval tv;
+
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_INIT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_SNAP:
+ case NBR_STA_XSTRT:
+ case NBR_STA_XCHNG:
+ return;
+ case NBR_STA_LOAD:
+ send_ls_req(nbr);
+ break;
+ case NBR_STA_FULL:
+ return;
+ default:
+ log_debug("ls_req_tx_timer: unknown neighbor state, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ break;
+ }
+
+ /* reschedule lsreq_tx_timer */
+ if (nbr->state == NBR_STA_LOAD) {
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->rxmt_interval;
+ if (evtimer_add(&nbr->lsreq_tx_timer, &tv) == -1)
+ fatal("ls_req_tx_timer");
+ }
+}
+
+void
+start_ls_req_tx_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ if (nbr == nbr->iface->self)
+ return;
+
+ timerclear(&tv);
+ if (evtimer_add(&nbr->lsreq_tx_timer, &tv) == -1)
+ fatal("start_ls_req_tx_timer");
+}
+
+void
+stop_ls_req_tx_timer(struct nbr *nbr)
+{
+ if (nbr == nbr->iface->self)
+ return;
+
+ if (evtimer_del(&nbr->lsreq_tx_timer) == -1)
+ fatal("stop_ls_req_tx_timer");
+}
diff --git a/usr.sbin/ospf6d/lsupdate.c b/usr.sbin/ospf6d/lsupdate.c
new file mode 100644
index 00000000000..9dcca18409a
--- /dev/null
+++ b/usr.sbin/ospf6d/lsupdate.c
@@ -0,0 +1,600 @@
+/* $OpenBSD: lsupdate.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2007 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/hash.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ospf6.h"
+#include "ospf6d.h"
+#include "log.h"
+#include "ospfe.h"
+#include "rde.h"
+
+extern struct ospfd_conf *oeconf;
+extern struct imsgbuf *ibuf_rde;
+
+struct buf *prepare_ls_update(struct iface *);
+int add_ls_update(struct buf *, struct iface *, void *, int, u_int16_t);
+int send_ls_update(struct buf *, struct iface *, struct in6_addr, u_int32_t);
+
+void ls_retrans_list_insert(struct nbr *, struct lsa_entry *);
+void ls_retrans_list_remove(struct nbr *, struct lsa_entry *);
+
+/* link state update packet handling */
+int
+lsa_flood(struct iface *iface, struct nbr *originator, struct lsa_hdr *lsa_hdr,
+ void *data)
+{
+ struct nbr *nbr;
+ struct lsa_entry *le = NULL;
+ int queued = 0, dont_ack = 0;
+ int r;
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr == iface->self)
+ continue;
+ if (!(nbr->state & NBR_STA_FLOOD))
+ continue;
+
+ if (iface->state & IF_STA_DROTHER && !queued)
+ while ((le = ls_retrans_list_get(iface->self, lsa_hdr)))
+ ls_retrans_list_free(iface->self, le);
+
+ while ((le = ls_retrans_list_get(nbr, lsa_hdr)))
+ ls_retrans_list_free(nbr, le);
+
+ if (!(nbr->state & NBR_STA_FULL) &&
+ (le = ls_req_list_get(nbr, lsa_hdr)) != NULL) {
+ r = lsa_newer(lsa_hdr, le->le_lsa);
+ if (r > 0) {
+ /* to flood LSA is newer than requested */
+ ls_req_list_free(nbr, le);
+ /* new needs to be flooded */
+ } else if (r < 0) {
+ /* to flood LSA is older than requested */
+ continue;
+ } else {
+ /* LSA are equal */
+ ls_req_list_free(nbr, le);
+ continue;
+ }
+ }
+
+ if (nbr == originator) {
+ dont_ack++;
+ continue;
+ }
+
+ /* non DR or BDR router keep all lsa in one retrans list */
+ if (iface->state & IF_STA_DROTHER) {
+ if (!queued)
+ ls_retrans_list_add(iface->self, data,
+ iface->rxmt_interval, 0);
+ queued = 1;
+ } else {
+ ls_retrans_list_add(nbr, data, iface->rxmt_interval, 0);
+ queued = 1;
+ }
+ }
+
+ if (!queued)
+ return (0);
+
+ if (iface == originator->iface && iface->self != originator) {
+ if (iface->dr == originator || iface->bdr == originator)
+ return (0);
+ if (iface->state & IF_STA_BACKUP)
+ return (0);
+ dont_ack++;
+ }
+
+ /*
+ * initial flood needs to be queued separately, timeout is zero
+ * and oneshot has to be set because the retransimssion queues
+ * are already loaded.
+ */
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ ls_retrans_list_add(iface->self, data, 0, 1);
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr == iface->self)
+ continue;
+ if (!(nbr->state & NBR_STA_FLOOD))
+ continue;
+ if (!TAILQ_EMPTY(&nbr->ls_retrans_list)) {
+ le = TAILQ_LAST(&nbr->ls_retrans_list,
+ lsa_head);
+ if (lsa_hdr->type != le->le_lsa->type ||
+ lsa_hdr->ls_id != le->le_lsa->ls_id ||
+ lsa_hdr->adv_rtr != le->le_lsa->adv_rtr)
+ continue;
+ }
+ ls_retrans_list_add(nbr, data, 0, 1);
+ }
+ break;
+ default:
+ fatalx("lsa_flood: unknown interface type");
+ }
+
+ return (dont_ack == 2);
+}
+
+struct buf *
+prepare_ls_update(struct iface *iface)
+{
+ struct buf *buf;
+
+ if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL)
+ fatal("send_ls_update");
+
+ /* OSPF header */
+ if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_UPDATE))
+ goto fail;
+
+ /* reserve space for number of lsa field */
+ if (buf_reserve(buf, sizeof(u_int32_t)) == NULL)
+ goto fail;
+
+ return (buf);
+fail:
+ log_warn("prepare_ls_update");
+ buf_free(buf);
+ return (NULL);
+}
+
+int
+add_ls_update(struct buf *buf, struct iface *iface, void *data, int len,
+ u_int16_t older)
+{
+ size_t pos;
+ u_int16_t age;
+
+ if (buf->wpos + len >= buf->max - MD5_DIGEST_LENGTH)
+ return (0);
+
+ pos = buf->wpos;
+ if (buf_add(buf, data, len)) {
+ log_warn("add_ls_update");
+ return (0);
+ }
+
+ /* age LSA before sending it out */
+ memcpy(&age, data, sizeof(age));
+ age = ntohs(age);
+ if ((age += older + iface->transmit_delay) >= MAX_AGE)
+ age = MAX_AGE;
+ age = htons(age);
+ memcpy(buf_seek(buf, pos, sizeof(age)), &age, sizeof(age));
+
+ return (1);
+}
+
+int
+send_ls_update(struct buf *buf, struct iface *iface, struct in6_addr addr,
+ u_int32_t nlsa)
+{
+ struct sockaddr_in6 dst;
+ int ret;
+
+ nlsa = htonl(nlsa);
+ memcpy(buf_seek(buf, sizeof(struct ospf_hdr), sizeof(nlsa)),
+ &nlsa, sizeof(nlsa));
+ /* calculate checksum */
+ if (upd_ospf_hdr(buf, iface))
+ goto fail;
+
+ /* set destination */
+ dst.sin6_family = AF_INET6;
+ dst.sin6_len = sizeof(struct sockaddr_in6);
+ dst.sin6_addr = addr;
+
+ ret = send_packet(iface, buf->buf, buf->wpos, &dst);
+
+ buf_free(buf);
+ return (ret);
+fail:
+ log_warn("send_ls_update");
+ buf_free(buf);
+ return (-1);
+}
+
+void
+recv_ls_update(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct lsa_hdr lsa;
+ u_int32_t nlsa;
+
+ if (len < sizeof(nlsa)) {
+ log_warnx("recv_ls_update: bad packet size, neighbor ID %s",
+ inet_ntoa(nbr->id));
+ return;
+ }
+ memcpy(&nlsa, buf, sizeof(nlsa));
+ nlsa = ntohl(nlsa);
+ buf += sizeof(nlsa);
+ len -= sizeof(nlsa);
+
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_INIT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_XSTRT:
+ case NBR_STA_SNAP:
+ log_debug("recv_ls_update: packet ignored in state %s, "
+ "neighbor ID %s", nbr_state_name(nbr->state),
+ inet_ntoa(nbr->id));
+ break;
+ case NBR_STA_XCHNG:
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ for (; nlsa > 0 && len > 0; nlsa--) {
+ if (len < sizeof(lsa)) {
+ log_warnx("recv_ls_update: bad packet size, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ return;
+ }
+ memcpy(&lsa, buf, sizeof(lsa));
+ if (len < ntohs(lsa.len)) {
+ log_warnx("recv_ls_update: bad packet size, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ return;
+ }
+ imsg_compose(ibuf_rde, IMSG_LS_UPD, nbr->peerid, 0,
+ buf, ntohs(lsa.len));
+ buf += ntohs(lsa.len);
+ len -= ntohs(lsa.len);
+ }
+ if (nlsa > 0 || len > 0) {
+ log_warnx("recv_ls_update: bad packet size, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ return;
+ }
+ break;
+ default:
+ fatalx("recv_ls_update: unknown neighbor state");
+ }
+}
+
+/* link state retransmit list */
+void
+ls_retrans_list_add(struct nbr *nbr, struct lsa_hdr *lsa,
+ unsigned short timeout, unsigned short oneshot)
+{
+ struct timeval tv;
+ struct lsa_entry *le;
+ struct lsa_ref *ref;
+
+ if ((ref = lsa_cache_get(lsa)) == NULL)
+ fatalx("King Bula sez: somebody forgot to lsa_cache_add");
+
+ if ((le = calloc(1, sizeof(*le))) == NULL)
+ fatal("ls_retrans_list_add");
+
+ le->le_ref = ref;
+ le->le_when = timeout;
+ le->le_oneshot = oneshot;
+
+ ls_retrans_list_insert(nbr, le);
+
+ if (!evtimer_pending(&nbr->ls_retrans_timer, NULL)) {
+ timerclear(&tv);
+ tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when;
+
+ if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
+ fatal("ls_retrans_list_add");
+ }
+}
+
+int
+ls_retrans_list_del(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
+{
+ struct lsa_entry *le;
+
+ if ((le = ls_retrans_list_get(nbr, lsa_hdr)) == NULL)
+ return (-1);
+ if (lsa_hdr->seq_num == le->le_ref->hdr.seq_num &&
+ lsa_hdr->ls_chksum == le->le_ref->hdr.ls_chksum) {
+ ls_retrans_list_free(nbr, le);
+ return (0);
+ }
+
+ return (-1);
+}
+
+struct lsa_entry *
+ls_retrans_list_get(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
+{
+ struct lsa_entry *le;
+
+ TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) {
+ if ((lsa_hdr->type == le->le_ref->hdr.type) &&
+ (lsa_hdr->ls_id == le->le_ref->hdr.ls_id) &&
+ (lsa_hdr->adv_rtr == le->le_ref->hdr.adv_rtr))
+ return (le);
+ }
+ return (NULL);
+}
+
+void
+ls_retrans_list_insert(struct nbr *nbr, struct lsa_entry *new)
+{
+ struct lsa_entry *le;
+ unsigned short when = new->le_when;
+
+ TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) {
+ if (when < le->le_when) {
+ new->le_when = when;
+ TAILQ_INSERT_BEFORE(le, new, entry);
+ nbr->ls_ret_cnt++;
+ return;
+ }
+ when -= le->le_when;
+ }
+ new->le_when = when;
+ TAILQ_INSERT_TAIL(&nbr->ls_retrans_list, new, entry);
+ nbr->ls_ret_cnt++;
+}
+
+void
+ls_retrans_list_remove(struct nbr *nbr, struct lsa_entry *le)
+{
+ struct timeval tv;
+ struct lsa_entry *next = TAILQ_NEXT(le, entry);
+ int reset = 0;
+
+ /* adjust timeout of next entry */
+ if (next)
+ next->le_when += le->le_when;
+
+ if (TAILQ_FIRST(&nbr->ls_retrans_list) == le &&
+ evtimer_pending(&nbr->ls_retrans_timer, NULL))
+ reset = 1;
+
+ TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
+ nbr->ls_ret_cnt--;
+
+ if (reset && TAILQ_FIRST(&nbr->ls_retrans_list)) {
+ if (evtimer_del(&nbr->ls_retrans_timer) == -1)
+ fatal("ls_retrans_list_remove");
+
+ timerclear(&tv);
+ tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when;
+
+ if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
+ fatal("ls_retrans_list_remove");
+ }
+}
+
+void
+ls_retrans_list_free(struct nbr *nbr, struct lsa_entry *le)
+{
+ ls_retrans_list_remove(nbr, le);
+
+ lsa_cache_put(le->le_ref, nbr);
+ free(le);
+}
+
+void
+ls_retrans_list_clr(struct nbr *nbr)
+{
+ struct lsa_entry *le;
+
+ while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL)
+ ls_retrans_list_free(nbr, le);
+
+ nbr->ls_ret_cnt = 0;
+}
+
+/* ARGSUSED */
+void
+ls_retrans_timer(int fd, short event, void *bula)
+{
+ struct timeval tv;
+ struct timespec tp;
+ struct in6_addr addr;
+ struct nbr *nbr = bula;
+ struct lsa_entry *le;
+ struct buf *buf;
+ time_t now;
+ int d;
+ u_int32_t nlsa = 0;
+
+ if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL)
+ le->le_when = 0; /* timer fired */
+ else
+ return; /* queue empty, nothing to do */
+
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ now = tp.tv_sec;
+
+ if (nbr->iface->self == nbr) {
+ /*
+ * oneshot needs to be set for lsa queued for flooding,
+ * if oneshot is not set then the lsa needs to be converted
+ * because the router switched lately to DR or BDR
+ */
+ if (le->le_oneshot && nbr->iface->state & IF_STA_DRORBDR)
+ inet_pton(AF_INET6, AllSPFRouters, &addr);
+ else if (nbr->iface->state & IF_STA_DRORBDR) {
+ /*
+ * old retransmission needs to be converted into
+ * flood by rerunning the lsa_flood.
+ */
+ lsa_flood(nbr->iface, nbr, &le->le_ref->hdr,
+ le->le_ref->data);
+ ls_retrans_list_free(nbr, le);
+ /* ls_retrans_list_free retriggers the timer */
+ return;
+ } else
+ inet_pton(AF_INET6, AllDRouters, &addr);
+ } else
+ memcpy(&addr, &nbr->addr, sizeof(addr));
+
+ if ((buf = prepare_ls_update(nbr->iface)) == NULL) {
+ le->le_when = 1;
+ goto done;
+ }
+
+ while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL &&
+ le->le_when == 0) {
+ d = now - le->le_ref->stamp;
+ if (d < 0)
+ d = 0;
+ else if (d > MAX_AGE)
+ d = MAX_AGE;
+
+ if (add_ls_update(buf, nbr->iface, le->le_ref->data,
+ le->le_ref->len, d) == 0)
+ break;
+ nlsa++;
+ if (le->le_oneshot)
+ ls_retrans_list_free(nbr, le);
+ else {
+ TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
+ nbr->ls_ret_cnt--;
+ le->le_when = nbr->iface->rxmt_interval;
+ ls_retrans_list_insert(nbr, le);
+ }
+ }
+ send_ls_update(buf, nbr->iface, addr, nlsa);
+
+done:
+ if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) {
+ timerclear(&tv);
+ tv.tv_sec = le->le_when;
+
+ if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
+ fatal("ls_retrans_timer");
+ }
+}
+
+LIST_HEAD(lsa_cache_head, lsa_ref);
+
+struct lsa_cache {
+ struct lsa_cache_head *hashtbl;
+ u_int32_t hashmask;
+} lsacache;
+
+struct lsa_ref *lsa_cache_look(struct lsa_hdr *);
+
+void
+lsa_cache_init(u_int32_t hashsize)
+{
+ u_int32_t hs, i;
+
+ for (hs = 1; hs < hashsize; hs <<= 1)
+ ;
+ lsacache.hashtbl = calloc(hs, sizeof(struct lsa_cache_head));
+ if (lsacache.hashtbl == NULL)
+ fatal("lsa_cache_init");
+
+ for (i = 0; i < hs; i++)
+ LIST_INIT(&lsacache.hashtbl[i]);
+
+ lsacache.hashmask = hs - 1;
+}
+
+struct lsa_ref *
+lsa_cache_add(void *data, u_int16_t len)
+{
+ struct lsa_cache_head *head;
+ struct lsa_ref *ref, *old;
+ struct timespec tp;
+
+ if ((ref = calloc(1, sizeof(*ref))) == NULL)
+ fatal("lsa_cache_add");
+ memcpy(&ref->hdr, data, sizeof(ref->hdr));
+
+ if ((old = lsa_cache_look(&ref->hdr))) {
+ free(ref);
+ old->refcnt++;
+ return (old);
+ }
+
+ if ((ref->data = malloc(len)) == NULL)
+ fatal("lsa_cache_add");
+ memcpy(ref->data, data, len);
+
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ ref->stamp = tp.tv_sec;
+ ref->len = len;
+ ref->refcnt = 1;
+
+ head = &lsacache.hashtbl[hash32_buf(&ref->hdr, sizeof(ref->hdr),
+ HASHINIT) & lsacache.hashmask];
+ LIST_INSERT_HEAD(head, ref, entry);
+ return (ref);
+}
+
+struct lsa_ref *
+lsa_cache_get(struct lsa_hdr *lsa_hdr)
+{
+ struct lsa_ref *ref;
+
+ ref = lsa_cache_look(lsa_hdr);
+ if (ref)
+ ref->refcnt++;
+
+ return (ref);
+}
+
+void
+lsa_cache_put(struct lsa_ref *ref, struct nbr *nbr)
+{
+ if (--ref->refcnt > 0)
+ return;
+
+ if (ntohs(ref->hdr.age) >= MAX_AGE)
+ ospfe_imsg_compose_rde(IMSG_LS_MAXAGE, nbr->peerid, 0,
+ ref->data, sizeof(struct lsa_hdr));
+
+ free(ref->data);
+ LIST_REMOVE(ref, entry);
+ free(ref);
+}
+
+struct lsa_ref *
+lsa_cache_look(struct lsa_hdr *lsa_hdr)
+{
+ struct lsa_cache_head *head;
+ struct lsa_ref *ref;
+
+ head = &lsacache.hashtbl[hash32_buf(lsa_hdr, sizeof(*lsa_hdr),
+ HASHINIT) & lsacache.hashmask];
+
+ LIST_FOREACH(ref, head, entry) {
+ if (memcmp(&ref->hdr, lsa_hdr, sizeof(*lsa_hdr)) == 0)
+ /* found match */
+ return (ref);
+ }
+
+ return (NULL);
+}
diff --git a/usr.sbin/ospf6d/name2id.c b/usr.sbin/ospf6d/name2id.c
new file mode 100644
index 00000000000..4d614ec867e
--- /dev/null
+++ b/usr.sbin/ospf6d/name2id.c
@@ -0,0 +1,228 @@
+/* $OpenBSD: name2id.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 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 MIND, 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 <net/route.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ospf6d.h"
+
+#define IDVAL_MAX 50000
+
+u_int16_t _name2id(struct n2id_labels *, const char *);
+const char *_id2name(struct n2id_labels *, u_int16_t);
+u_int32_t _id2tag(struct n2id_labels *, u_int16_t);
+u_int16_t _tag2id(struct n2id_labels *, u_int32_t);
+void _tag(struct n2id_labels *, u_int16_t, u_int32_t);
+void _unref(struct n2id_labels *, u_int16_t);
+void _ref(struct n2id_labels *, u_int16_t);
+
+struct n2id_labels rt_labels = TAILQ_HEAD_INITIALIZER(rt_labels);
+
+u_int16_t
+rtlabel_name2id(const char *name)
+{
+ return (_name2id(&rt_labels, name));
+}
+
+const char *
+rtlabel_id2name(u_int16_t id)
+{
+ return (_id2name(&rt_labels, id));
+}
+
+u_int32_t
+rtlabel_id2tag(u_int16_t id)
+{
+ return (_id2tag(&rt_labels, id));
+}
+
+u_int16_t
+rtlabel_tag2id(u_int32_t tag)
+{
+ return (_tag2id(&rt_labels, tag));
+}
+
+void
+rtlabel_tag(u_int16_t id, u_int32_t tag)
+{
+ _tag(&rt_labels, id, tag);
+}
+
+void
+rtlabel_unref(u_int16_t id)
+{
+ _unref(&rt_labels, id);
+}
+
+/*
+void
+rtlabel_ref(u_int16_t id)
+{
+ _ref(&rt_labels, id);
+}
+*/
+
+u_int16_t
+_name2id(struct n2id_labels *head, const char *name)
+{
+ struct n2id_label *label, *p = NULL;
+ u_int16_t new_id = 1;
+
+ if (!name[0]) {
+ errno = EINVAL;
+ return (0);
+ }
+
+ TAILQ_FOREACH(label, head, entry)
+ if (strcmp(name, label->name) == 0) {
+ label->ref++;
+ return (label->id);
+ }
+
+ /*
+ * to avoid fragmentation, we do a linear search from the beginning
+ * and take the first free slot we find. if there is none or the list
+ * is empty, append a new entry at the end.
+ */
+
+ if (!TAILQ_EMPTY(head))
+ for (p = TAILQ_FIRST(head); p != NULL &&
+ p->id == new_id; p = TAILQ_NEXT(p, entry))
+ new_id = p->id + 1;
+
+ if (new_id > IDVAL_MAX) {
+ errno = ERANGE;
+ return (0);
+ }
+
+ if ((label = calloc(1, sizeof(struct n2id_label))) == NULL)
+ return (0);
+ if ((label->name = strdup(name)) == NULL) {
+ free(label);
+ return (0);
+ }
+ label->id = new_id;
+ label->ref++;
+
+ if (p != NULL) /* insert new entry before p */
+ TAILQ_INSERT_BEFORE(p, label, entry);
+ else /* either list empty or no free slot in between */
+ TAILQ_INSERT_TAIL(head, label, entry);
+
+ return (label->id);
+}
+
+const char *
+_id2name(struct n2id_labels *head, u_int16_t id)
+{
+ struct n2id_label *label;
+
+ if (id == 0)
+ return ("");
+
+ TAILQ_FOREACH(label, head, entry)
+ if (label->id == id)
+ return (label->name);
+
+ return ("");
+}
+
+u_int32_t
+_id2tag(struct n2id_labels *head, u_int16_t id)
+{
+ struct n2id_label *label;
+
+ if (id == 0)
+ return (0);
+
+ TAILQ_FOREACH(label, head, entry)
+ if (label->id == id)
+ return (label->ext_tag);
+
+ return (0);
+}
+
+u_int16_t
+_tag2id(struct n2id_labels *head, u_int32_t tag)
+{
+ struct n2id_label *label;
+
+ if (tag == 0)
+ return (0);
+
+ TAILQ_FOREACH(label, head, entry)
+ if (label->ext_tag == tag)
+ return (label->id);
+
+ return (0);
+}
+
+void
+_tag(struct n2id_labels *head, u_int16_t id, u_int32_t tag)
+{
+ struct n2id_label *label;
+
+ if (id == 0)
+ return;
+
+ TAILQ_FOREACH(label, head, entry)
+ if (label->id == id)
+ label->ext_tag = tag;
+}
+
+void
+_unref(struct n2id_labels *head, u_int16_t id)
+{
+ struct n2id_label *p, *next;
+
+ if (id == 0)
+ return;
+
+ for (p = TAILQ_FIRST(head); p != NULL; p = next) {
+ next = TAILQ_NEXT(p, entry);
+ if (id == p->id) {
+ if (--p->ref == 0) {
+ TAILQ_REMOVE(head, p, entry);
+ free(p->name);
+ free(p);
+ }
+ break;
+ }
+ }
+}
+
+void
+_ref(struct n2id_labels *head, u_int16_t id)
+{
+ struct n2id_label *label;
+
+ if (id == 0)
+ return;
+
+ TAILQ_FOREACH(label, head, entry)
+ if (label->id == id) {
+ ++label->ref;
+ break;
+ }
+}
diff --git a/usr.sbin/ospf6d/neighbor.c b/usr.sbin/ospf6d/neighbor.c
new file mode 100644
index 00000000000..0b154db78f5
--- /dev/null
+++ b/usr.sbin/ospf6d/neighbor.c
@@ -0,0 +1,705 @@
+/* $OpenBSD: neighbor.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2007 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 "ospf6d.h"
+#include "ospf6.h"
+#include "ospfe.h"
+#include "log.h"
+#include "rde.h"
+
+int nbr_adj_ok(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_ACTIVE, NBR_EVT_HELLO_RCVD, NBR_ACT_RST_ITIMER, 0},
+ {NBR_STA_BIDIR, NBR_EVT_2_WAY_RCVD, NBR_ACT_NOTHING, 0},
+ {NBR_STA_INIT, NBR_EVT_1_WAY_RCVD, NBR_ACT_NOTHING, 0},
+ {NBR_STA_DOWN, NBR_EVT_HELLO_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_INIT},
+ {NBR_STA_ATTEMPT, NBR_EVT_HELLO_RCVD, NBR_ACT_RST_ITIMER, NBR_STA_INIT},
+ {NBR_STA_INIT, NBR_EVT_2_WAY_RCVD, NBR_ACT_EVAL, 0},
+ {NBR_STA_XSTRT, NBR_EVT_NEG_DONE, NBR_ACT_SNAP, NBR_STA_SNAP},
+ {NBR_STA_SNAP, NBR_EVT_SNAP_DONE, NBR_ACT_SNAP_DONE, NBR_STA_XCHNG},
+ {NBR_STA_XCHNG, NBR_EVT_XCHNG_DONE, NBR_ACT_XCHNG_DONE, 0},
+ {NBR_STA_LOAD, NBR_EVT_LOAD_DONE, NBR_ACT_NOTHING, NBR_STA_FULL},
+ {NBR_STA_2_WAY, NBR_EVT_ADJ_OK, NBR_ACT_EVAL, 0},
+ {NBR_STA_ADJFORM, NBR_EVT_ADJ_OK, NBR_ACT_ADJ_OK, 0},
+ {NBR_STA_PRELIM, NBR_EVT_ADJ_OK, NBR_ACT_HELLO_CHK, 0},
+ {NBR_STA_ADJFORM, NBR_EVT_ADJTMOUT, NBR_ACT_RESTRT_DD, 0},
+ {NBR_STA_FLOOD, NBR_EVT_SEQ_NUM_MIS, NBR_ACT_RESTRT_DD, NBR_STA_XSTRT},
+ {NBR_STA_FLOOD, NBR_EVT_BAD_LS_REQ, NBR_ACT_RESTRT_DD, NBR_STA_XSTRT},
+ {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN},
+ {NBR_STA_ANY, NBR_EVT_LL_DOWN, NBR_ACT_DEL, NBR_STA_DOWN},
+ {NBR_STA_ANY, NBR_EVT_ITIMER, NBR_ACT_DEL, NBR_STA_DOWN},
+ {NBR_STA_BIDIR, NBR_EVT_1_WAY_RCVD, NBR_ACT_CLR_LST, NBR_STA_INIT},
+ {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0},
+};
+
+const char * const nbr_event_names[] = {
+ "NOTHING",
+ "HELLO_RECEIVED",
+ "2_WAY_RECEIVED",
+ "NEGOTIATION_DONE",
+ "SNAPSHOT_DONE",
+ "EXCHANGE_DONE",
+ "BAD_LS_REQ",
+ "LOADING_DONE",
+ "ADJ_OK",
+ "SEQ_NUM_MISMATCH",
+ "1_WAY_RECEIVED",
+ "KILL_NBR",
+ "INACTIVITY_TIMER",
+ "LL_DOWN",
+ "ADJ_TIMEOUT"
+};
+
+const char * const nbr_action_names[] = {
+ "NOTHING",
+ "RESET_INACTIVITY_TIMER",
+ "START_INACTIVITY_TIMER",
+ "EVAL",
+ "SNAPSHOT",
+ "SNAPSHOT_DONE",
+ "EXCHANGE_DONE",
+ "ADJ_OK",
+ "RESET_DD",
+ "DELETE",
+ "CLEAR_LISTS",
+ "HELLO_CHK"
+};
+
+int
+nbr_fsm(struct nbr *nbr, enum nbr_event event)
+{
+ struct timeval now;
+ int old_state;
+ int new_state = 0;
+ int i, ret = 0;
+
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ old_state = nbr->state;
+ for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
+ if ((nbr_fsm_tbl[i].state & old_state) &&
+ (nbr_fsm_tbl[i].event == event)) {
+ new_state = nbr_fsm_tbl[i].new_state;
+ break;
+ }
+
+ if (nbr_fsm_tbl[i].state == -1) {
+ /* 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_names[event],
+ nbr_state_name(old_state));
+ return (0);
+ }
+
+ switch (nbr_fsm_tbl[i].action) {
+ case NBR_ACT_RST_ITIMER:
+ ret = nbr_act_reset_itimer(nbr);
+ break;
+ case NBR_ACT_STRT_ITIMER:
+ ret = nbr_act_start_itimer(nbr);
+ break;
+ case NBR_ACT_EVAL:
+ ret = nbr_act_eval(nbr);
+ break;
+ case NBR_ACT_SNAP:
+ ret = nbr_act_snapshot(nbr);
+ break;
+ case NBR_ACT_SNAP_DONE:
+ /* start db exchange */
+ start_db_tx_timer(nbr);
+ break;
+ case NBR_ACT_XCHNG_DONE:
+ ret = nbr_act_exchange_done(nbr);
+ break;
+ case NBR_ACT_ADJ_OK:
+ ret = nbr_act_adj_ok(nbr);
+ break;
+ case NBR_ACT_RESTRT_DD:
+ ret = nbr_act_restart_dd(nbr);
+ break;
+ case NBR_ACT_DEL:
+ ret = nbr_act_delete(nbr);
+ break;
+ case NBR_ACT_CLR_LST:
+ ret = nbr_act_clear_lists(nbr);
+ break;
+ case NBR_ACT_HELLO_CHK:
+ ret = nbr_act_hello_check(nbr);
+ break;
+ case NBR_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ if (ret) {
+ log_warnx("nbr_fsm: error changing state for neighbor ID %s, "
+ "event %s, state %s", inet_ntoa(nbr->id),
+ nbr_event_names[event], nbr_state_name(old_state));
+ return (-1);
+ }
+
+ if (new_state != 0)
+ nbr->state = new_state;
+
+ if (old_state != nbr->state) {
+ nbr->stats.sta_chng++;
+ /* state change inform RDE */
+ ospfe_imsg_compose_rde(IMSG_NEIGHBOR_CHANGE,
+ nbr->peerid, 0, &new_state, sizeof(new_state));
+
+ if (old_state & NBR_STA_FULL || nbr->state & NBR_STA_FULL) {
+ /*
+ * neighbor changed from/to FULL
+ * originate new rtr and net LSA
+ */
+ area_track(nbr->iface->area, nbr->state);
+ orig_rtr_lsa(nbr->iface->area);
+ if (nbr->iface->state & IF_STA_DR)
+ orig_net_lsa(nbr->iface);
+
+ gettimeofday(&now, NULL);
+ nbr->uptime = now.tv_sec;
+ }
+
+ /* bidirectional communication lost */
+ if (old_state & ~NBR_STA_PRELIM && nbr->state & NBR_STA_PRELIM)
+ if_fsm(nbr->iface, IF_EVT_NBR_CHNG);
+
+ log_debug("nbr_fsm: event %s resulted in action %s and "
+ "changing state for neighbor ID %s from %s to %s",
+ nbr_event_names[event],
+ nbr_action_names[nbr_fsm_tbl[i].action],
+ inet_ntoa(nbr->id), nbr_state_name(old_state),
+ nbr_state_name(nbr->state));
+
+ if (nbr->iface->type == IF_TYPE_VIRTUALLINK) {
+ orig_rtr_lsa(nbr->iface->area);
+ }
+ }
+
+ return (ret);
+}
+
+void
+nbr_init(u_int32_t hashsize)
+{
+ struct nbr_head *head;
+ struct nbr *nbr;
+ 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;
+
+ /* allocate a dummy neighbor used for self originated AS ext routes */
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal("nbr_init");
+
+ nbr->id.s_addr = ospfe_router_id();
+ nbr->state = NBR_STA_DOWN;
+ nbr->peerid = NBR_IDSELF;
+ head = NBR_HASH(nbr->peerid);
+ LIST_INSERT_HEAD(head, nbr, hash);
+
+ TAILQ_INIT(&nbr->ls_retrans_list);
+ TAILQ_INIT(&nbr->db_sum_list);
+ TAILQ_INIT(&nbr->ls_req_list);
+}
+
+struct nbr *
+nbr_new(u_int32_t nbr_id, struct iface *iface, int self)
+{
+ struct nbr_head *head;
+ struct nbr *nbr;
+ struct rde_nbr rn;
+
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal("nbr_new");
+
+ nbr->state = NBR_STA_DOWN;
+ nbr->master = 1;
+ nbr->dd_seq_num = arc4random(); /* RFC: some unique value */
+ 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->ls_retrans_list);
+ TAILQ_INIT(&nbr->db_sum_list);
+ TAILQ_INIT(&nbr->ls_req_list);
+
+ nbr->ls_req = NULL;
+
+ if (self) {
+ nbr->state = NBR_STA_FULL;
+//XXX nbr->addr.s_addr = iface->addr.s_addr;
+ nbr->addr = iface->addr;
+ nbr->priority = iface->priority;
+ }
+
+ /* set event structures */
+ evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr);
+ evtimer_set(&nbr->db_tx_timer, db_tx_timer, nbr);
+ evtimer_set(&nbr->lsreq_tx_timer, ls_req_tx_timer, nbr);
+ evtimer_set(&nbr->ls_retrans_timer, ls_retrans_timer, nbr);
+ evtimer_set(&nbr->adj_timer, nbr_adj_timer, nbr);
+
+ bzero(&rn, sizeof(rn));
+ rn.id.s_addr = nbr->id.s_addr;
+ rn.area_id.s_addr = nbr->iface->area->id.s_addr;
+ rn.state = nbr->state;
+ rn.self = self;
+ ospfe_imsg_compose_rde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rn,
+ sizeof(rn));
+
+ return (nbr);
+}
+
+void
+nbr_del(struct nbr *nbr)
+{
+ ospfe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0);
+
+ if (evtimer_pending(&nbr->inactivity_timer, NULL))
+ evtimer_del(&nbr->inactivity_timer);
+ if (evtimer_pending(&nbr->db_tx_timer, NULL))
+ evtimer_del(&nbr->db_tx_timer);
+ if (evtimer_pending(&nbr->lsreq_tx_timer, NULL))
+ evtimer_del(&nbr->lsreq_tx_timer);
+ if (evtimer_pending(&nbr->ls_retrans_timer, NULL))
+ evtimer_del(&nbr->ls_retrans_timer);
+ if (evtimer_pending(&nbr->adj_timer, NULL))
+ evtimer_del(&nbr->adj_timer);
+
+ /* clear lists */
+ ls_retrans_list_clr(nbr);
+ db_sum_list_clr(nbr);
+ ls_req_list_clr(nbr);
+
+ 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_id(struct iface *iface, u_int32_t rtr_id)
+{
+ struct nbr *nbr = NULL;
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->id.s_addr == rtr_id)
+ return (nbr);
+ }
+
+ return (NULL);
+}
+
+/* timers */
+/* ARGSUSED */
+void
+nbr_itimer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ if (nbr->state == NBR_STA_DOWN)
+ nbr_del(nbr);
+ else
+ nbr_fsm(nbr, NBR_EVT_ITIMER);
+}
+
+void
+nbr_start_itimer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->dead_interval;
+
+ if (evtimer_add(&nbr->inactivity_timer, &tv) == -1)
+ fatal("nbr_start_itimer");
+}
+
+void
+nbr_stop_itimer(struct nbr *nbr)
+{
+ if (evtimer_del(&nbr->inactivity_timer) == -1)
+ fatal("nbr_stop_itimer");
+}
+
+void
+nbr_reset_itimer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->dead_interval;
+
+ if (evtimer_add(&nbr->inactivity_timer, &tv) == -1)
+ fatal("nbr_reset_itimer");
+}
+
+/* ARGSUSED */
+void
+nbr_adj_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ if (nbr->state == NBR_STA_2_WAY)
+ return ;
+
+ if (nbr->state & NBR_STA_ACTIVE && nbr->state != NBR_STA_FULL) {
+ log_warnx("nbr_adj_timer: failed to form adjacency with %s",
+ inet_ntoa(nbr->id));
+ nbr_fsm(nbr, NBR_EVT_ADJTMOUT);
+ }
+}
+
+void
+nbr_start_adj_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = DEFAULT_ADJ_TMOUT;
+
+ if (evtimer_add(&nbr->adj_timer, &tv) == -1)
+ fatal("nbr_start_adj_timer");
+}
+
+/* actions */
+int
+nbr_act_reset_itimer(struct nbr *nbr)
+{
+ nbr_reset_itimer(nbr);
+
+ return (0);
+}
+
+int
+nbr_act_start_itimer(struct nbr *nbr)
+{
+ nbr_start_itimer(nbr);
+
+ return (0);
+}
+
+int
+nbr_adj_ok(struct nbr *nbr)
+{
+ struct iface *iface = nbr->iface;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ case IF_TYPE_POINTOMULTIPOINT:
+ /* always ok */
+ break;
+ case IF_TYPE_BROADCAST:
+ case IF_TYPE_NBMA:
+ /*
+ * if neighbor is dr, bdr or router self is dr or bdr
+ * start forming adjacency
+ */
+ if (iface->dr == nbr || iface->bdr == nbr ||
+ iface->state & IF_STA_DRORBDR)
+ break;
+ return (0);
+ default:
+ fatalx("nbr_act_ok: unknown interface type");
+ }
+ return (1);
+}
+
+int
+nbr_act_eval(struct nbr *nbr)
+{
+ if (!nbr_adj_ok(nbr)) {
+ nbr->state = NBR_STA_2_WAY;
+ return (0);
+ }
+
+ nbr->state = NBR_STA_XSTRT;
+ nbr->master = 1;
+ nbr->dd_seq_num++; /* as per RFC */
+ nbr->dd_pending = 0;
+ /* initial db negotiation */
+ start_db_tx_timer(nbr);
+
+ nbr_start_adj_timer(nbr);
+
+ return (0);
+}
+
+int
+nbr_act_snapshot(struct nbr *nbr)
+{
+ stop_db_tx_timer(nbr);
+
+ ospfe_imsg_compose_rde(IMSG_DB_SNAPSHOT, nbr->peerid, 0, NULL, 0);
+
+ return (0);
+}
+
+int
+nbr_act_exchange_done(struct nbr *nbr)
+{
+ if (nbr->master)
+ stop_db_tx_timer(nbr);
+
+ if (ls_req_list_empty(nbr) && nbr->state == NBR_STA_XCHNG &&
+ nbr->dd_pending == 0) {
+ nbr->state = NBR_STA_FULL;
+ return (0);
+ }
+
+ nbr->state = NBR_STA_LOAD;
+
+ if (!ls_req_list_empty(nbr))
+ start_ls_req_tx_timer(nbr);
+
+ return (0);
+}
+
+int
+nbr_act_adj_ok(struct nbr *nbr)
+{
+ if (nbr_adj_ok(nbr)) {
+ if (nbr->state == NBR_STA_2_WAY)
+ return (nbr_act_eval(nbr));
+ } else {
+ nbr->state = NBR_STA_2_WAY;
+ return (nbr_act_clear_lists(nbr));
+ }
+
+ return (0);
+}
+
+int
+nbr_act_restart_dd(struct nbr *nbr)
+{
+ nbr_act_clear_lists(nbr);
+
+ if (!nbr_adj_ok(nbr)) {
+ nbr->state = NBR_STA_2_WAY;
+ return (0);
+ }
+
+ nbr->state = NBR_STA_XSTRT;
+ nbr->master = 1;
+ nbr->dd_seq_num += arc4random() & 0xffff;
+ nbr->dd_pending = 0;
+
+ /* initial db negotiation */
+ start_db_tx_timer(nbr);
+
+ nbr_start_adj_timer(nbr);
+
+ return (0);
+}
+
+int
+nbr_act_delete(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ /* stop timers */
+ nbr_stop_itimer(nbr);
+
+ /* clear dr and bdr */
+ nbr->dr.s_addr = 0;
+ nbr->bdr.s_addr = 0;
+ nbr->crypt_seq_num = 0;
+
+ /* schedule kill timer */
+ timerclear(&tv);
+ tv.tv_sec = DEFAULT_NBR_TMOUT;
+
+ if (evtimer_add(&nbr->inactivity_timer, &tv)) {
+ log_warnx("nbr_act_delete: error scheduling neighbor ID %s "
+ "for removal", inet_ntoa(nbr->id));
+ }
+
+ return (nbr_act_clear_lists(nbr));
+}
+
+int
+nbr_act_clear_lists(struct nbr *nbr)
+{
+ /* stop timers */
+ stop_db_tx_timer(nbr);
+ stop_ls_req_tx_timer(nbr);
+
+ /* clear lists */
+ ls_retrans_list_clr(nbr);
+ db_sum_list_clr(nbr);
+ ls_req_list_clr(nbr);
+
+ return (0);
+}
+
+int
+nbr_act_hello_check(struct nbr *nbr)
+{
+ log_debug("nbr_act_hello_check: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return (-1);
+}
+
+struct ctl_nbr *
+nbr_to_ctl(struct nbr *nbr)
+{
+ static struct ctl_nbr nctl;
+ struct timeval tv, now, res;
+ struct lsa_entry *le;
+
+ 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));
+ memcpy(&nctl.dr, &nbr->dr, sizeof(nctl.dr));
+ memcpy(&nctl.bdr, &nbr->bdr, sizeof(nctl.bdr));
+ memcpy(&nctl.area, &nbr->iface->area->id, sizeof(nctl.area));
+
+ /* this list is 99% of the time empty so that's OK for now */
+ nctl.db_sum_lst_cnt = 0;
+ TAILQ_FOREACH(le, &nbr->db_sum_list, entry)
+ nctl.db_sum_lst_cnt++;
+
+ nctl.ls_req_lst_cnt = nbr->ls_req_cnt;
+ nctl.ls_retrans_lst_cnt = nbr->ls_ret_cnt;
+
+ nctl.nbr_state = nbr->state;
+
+ /*
+ * We need to trick a bit to show the remote iface state.
+ * The idea is to print DR, BDR or DROther dependent on
+ * the type of the neighbor.
+ */
+ if (nbr->iface->dr == nbr)
+ nctl.iface_state = IF_STA_DR;
+ else if (nbr->iface->bdr == nbr)
+ nctl.iface_state = IF_STA_BACKUP;
+ else if (nbr->iface->state & IF_STA_MULTI)
+ nctl.iface_state = IF_STA_DROTHER;
+ else
+ nctl.iface_state = nbr->iface->state;
+
+ nctl.state_chng_cnt = nbr->stats.sta_chng;
+
+ nctl.priority = nbr->priority;
+ nctl.options = nbr->options;
+
+ gettimeofday(&now, NULL);
+ if (evtimer_pending(&nbr->inactivity_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ if (nbr->state & NBR_STA_DOWN)
+ nctl.dead_timer = DEFAULT_NBR_TMOUT - res.tv_sec;
+ else
+ nctl.dead_timer = res.tv_sec;
+ } else
+ nctl.dead_timer = 0;
+
+ if (nbr->state == NBR_STA_FULL) {
+ nctl.uptime = now.tv_sec - nbr->uptime;
+ } else
+ nctl.uptime = 0;
+
+ return (&nctl);
+}
+
+struct lsa_hdr *
+lsa_hdr_new(void)
+{
+ struct lsa_hdr *lsa_hdr = NULL;
+
+ if ((lsa_hdr = calloc(1, sizeof(*lsa_hdr))) == NULL)
+ fatal("lsa_hdr_new");
+
+ return (lsa_hdr);
+}
diff --git a/usr.sbin/ospf6d/ospf6.h b/usr.sbin/ospf6d/ospf6.h
new file mode 100644
index 00000000000..cd92a93f534
--- /dev/null
+++ b/usr.sbin/ospf6d/ospf6.h
@@ -0,0 +1,240 @@
+/* $OpenBSD: ospf6.h,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005, 2007 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.
+ */
+
+/* OSPF protocol definitions */
+
+#ifndef _OSPF_H_
+#define _OSPF_H_
+
+#include <netinet/in.h>
+
+/* misc */
+#define OSPF6_VERSION 3
+#define IPPROTO_OSPF 89
+#define AllSPFRouters "ff02::5"
+#define AllDRouters "ff02::6"
+
+#define PACKET_HDR 100 /* XXX used to calculate the IP payload */
+
+#define DEFAULT_METRIC 10
+#define DEFAULT_REDIST_METRIC 100
+#define MIN_METRIC 1
+#define MAX_METRIC 65535 /* sum & as-ext lsa use 24bit metrics */
+
+#define DEFAULT_PRIORITY 1
+#define MIN_PRIORITY 0
+#define MAX_PRIORITY 255
+
+#define DEFAULT_HELLO_INTERVAL 10
+#define MIN_HELLO_INTERVAL 1
+#define MAX_HELLO_INTERVAL 65535
+
+#define DEFAULT_RTR_DEAD_TIME 40
+#define MIN_RTR_DEAD_TIME 2
+#define MAX_RTR_DEAD_TIME 2147483647
+
+#define DEFAULT_RXMT_INTERVAL 5
+#define MIN_RXMT_INTERVAL 5
+#define MAX_RXMT_INTERVAL 3600
+
+#define DEFAULT_TRANSMIT_DELAY 1
+#define MIN_TRANSMIT_DELAY 1
+#define MAX_TRANSMIT_DELAY 3600
+
+#define DEFAULT_ADJ_TMOUT 60
+
+#define DEFAULT_NBR_TMOUT 86400 /* 24 hours */
+
+#define DEFAULT_SPF_DELAY 1
+#define MIN_SPF_DELAY 1
+#define MAX_SPF_DELAY 10
+
+#define DEFAULT_SPF_HOLDTIME 5
+#define MIN_SPF_HOLDTIME 1
+#define MAX_SPF_HOLDTIME 5
+
+#define MIN_MD_ID 0
+#define MAX_MD_ID 255
+
+#define DEFAULT_INSTANCE_ID 0
+#define MIN_INSTANCE_ID 0
+#define MAX_INSTANCE_ID 0
+
+/* OSPF compatibility flags */
+#define OSPF_OPTION_V6 0x01
+#define OSPF_OPTION_E 0x02
+#define OSPF_OPTION_MC 0x04
+#define OSPF_OPTION_N 0x08
+#define OSPF_OPTION_R 0x10
+#define OSPF_OPTION_DC 0x20
+
+/* OSPF packet types */
+#define PACKET_TYPE_HELLO 1
+#define PACKET_TYPE_DD 2
+#define PACKET_TYPE_LS_REQUEST 3
+#define PACKET_TYPE_LS_UPDATE 4
+#define PACKET_TYPE_LS_ACK 5
+
+/* LSA */
+#define LS_REFRESH_TIME 1800
+#define MIN_LS_INTERVAL 5
+#define MIN_LS_ARRIVAL 1
+#define DEFAULT_AGE 0
+#define MAX_AGE 3600
+#define CHECK_AGE 300
+#define MAX_AGE_DIFF 900
+#define LS_INFINITY 0xffffff
+#define RESV_SEQ_NUM 0x80000000 /* reserved and "unused" */
+#define INIT_SEQ_NUM 0x80000001
+#define MAX_SEQ_NUM 0x7fffffff
+
+/* OSPF header */
+struct crypt {
+ u_int16_t dummy;
+ u_int8_t keyid;
+ u_int8_t len;
+ u_int32_t seq_num;
+};
+
+struct ospf_hdr {
+ u_int8_t version;
+ u_int8_t type;
+ u_int16_t len;
+ u_int32_t rtr_id;
+ u_int32_t area_id;
+ u_int16_t chksum;
+ u_int8_t instance;
+ u_int8_t zero; /* must be zero */
+};
+
+/* Hello header (type 1) */
+struct hello_hdr {
+ u_int32_t iface_id;
+ u_int8_t rtr_priority;
+ u_int8_t opts1;
+ u_int8_t opts2;
+ u_int8_t opts3;
+ u_int16_t hello_interval;
+ u_int16_t rtr_dead_interval;
+ u_int32_t d_rtr;
+ u_int32_t bd_rtr;
+};
+
+/* Database Description header (type 2) */
+struct db_dscrp_hdr {
+ u_int32_t opts;
+ u_int16_t iface_mtu;
+ u_int8_t zero; /* must be zero */
+ u_int8_t bits;
+ u_int32_t dd_seq_num;
+};
+
+#define OSPF_DBD_MS 0x01
+#define OSPF_DBD_M 0x02
+#define OSPF_DBD_I 0x04
+
+/* Link State Request header (type 3) */
+struct ls_req_hdr {
+ u_int16_t zero;
+ u_int16_t type;
+ u_int32_t ls_id;
+ u_int32_t adv_rtr;
+};
+
+/* Link State Update header (type 4) */
+struct ls_upd_hdr {
+ u_int32_t num_lsa;
+};
+
+#define LSA_TYPE_ROUTER 1
+#define LSA_TYPE_NETWORK 2
+#define LSA_TYPE_SUM_NETWORK 3
+#define LSA_TYPE_SUM_ROUTER 4
+#define LSA_TYPE_EXTERNAL 5
+
+#define LINK_TYPE_POINTTOPOINT 1
+#define LINK_TYPE_TRANSIT_NET 2
+#define LINK_TYPE_STUB_NET 3
+#define LINK_TYPE_VIRTUAL 4
+
+/* LSA headers */
+#define LSA_METRIC_MASK 0x00ffffff /* only for sum & as-ext */
+#define LSA_ASEXT_E_FLAG 0x80000000
+
+#define OSPF_RTR_B 0x01
+#define OSPF_RTR_E 0x02
+#define OSPF_RTR_V 0x04
+
+struct lsa_rtr {
+ u_int8_t flags;
+ u_int8_t dummy;
+ u_int16_t nlinks;
+};
+
+struct lsa_rtr_link {
+ u_int32_t id;
+ u_int32_t data;
+ u_int8_t type;
+ u_int8_t num_tos;
+ u_int16_t metric;
+};
+
+struct lsa_net {
+ u_int32_t mask;
+ u_int32_t att_rtr[1];
+};
+
+struct lsa_net_link {
+ u_int32_t att_rtr;
+};
+
+struct lsa_sum {
+ u_int32_t mask;
+ u_int32_t metric; /* only lower 24 bit */
+};
+
+struct lsa_asext {
+ u_int32_t mask;
+ u_int32_t metric; /* lower 24 bit plus E bit */
+ u_int32_t fw_addr;
+ u_int32_t ext_tag;
+};
+
+struct lsa_hdr {
+ u_int16_t age;
+ u_int16_t type;
+ u_int32_t ls_id;
+ u_int32_t adv_rtr;
+ u_int32_t seq_num;
+ u_int16_t ls_chksum;
+ u_int16_t len;
+};
+
+#define LS_CKSUM_OFFSET ((u_int16_t)(&((struct lsa_hdr *)0)->ls_chksum))
+
+struct lsa {
+ struct lsa_hdr hdr;
+ union {
+ struct lsa_rtr rtr;
+ struct lsa_net net;
+ struct lsa_sum sum;
+ struct lsa_asext asext;
+ } data;
+};
+
+#endif /* !_OSPF_H_ */
diff --git a/usr.sbin/ospf6d/ospf6d.8 b/usr.sbin/ospf6d/ospf6d.8
new file mode 100644
index 00000000000..026739491d6
--- /dev/null
+++ b/usr.sbin/ospf6d/ospf6d.8
@@ -0,0 +1,160 @@
+.\" $OpenBSD: ospf6d.8,v 1.1 2007/10/08 10:44:50 norby Exp $
+.\"
+.\" Copyright (c) 2004, 2005, 2007 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 $Mdocdate: October 8 2007 $
+.Dt OSPF6D 8
+.Os
+.Sh NAME
+.Nm ospf6d
+.Nd "Open Shortest Path First daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnv
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is an Open Shortest Path First
+.Pq OSPF
+daemon which manages routing tables.
+.Pp
+This implementation supports OSPF version 3, thus it is only capable of
+maintaining IPv6 routing tables.
+ .Pp
+OSPF is an interior gateway protocol designed to superceed RIP.
+The OSPF daemon maintains a Link State Database
+.Pq LSDB
+consisting of routers and networks.
+.Pp
+Dijkstra's shortest path first algorithm is used to compute the Route
+Information Base using the LSDB as input.
+The Forwarding Information Base a.k.a. the kernel route table is updated
+with information from the RIB.
+.Pp
+OSPF routers communicate via two multicast groups: ff02::5 all Shortest
+Path First routers and ff02::6 all Designated Routers.
+The IP protocol number 89 is reserved for OSPF, furthermore OSPF packets
+omit the use of TCP and UDP.
+.Pp
+OSPF has several advantages over RIP.
+For example every router has a complete network topology.
+Response to changes in the network are faster.
+Furthermore fail detection is much improved.
+.Pp
+All routers in an OSPF network spend most of their time keeping each others
+LSDBs in sync.
+All routers must have the same information in the LSDB at all times.
+Every time the LSDB is updated the RIB is updated; if needed the FIB is
+also updated.
+.Pp
+OSPF aware routers discover each other via OSPF hello packets.
+.Pp
+In a multiaccess network such as Ethernet it is unfeasible for all routers
+to syncronize with all other routers in the network.
+In such networks a Designated Router
+.Pq DR
+and a Backup Designated Router
+.Pq BDR
+is elected.
+The DR's responsibility is to syncronize with all routers; the BDR will
+not do much until the DR fails.
+The first router in a network is automatically elected DR, the second
+router BDR.
+All routers have a FULL adjacency with the DR and the BDR.
+A router not elected either DR or BDR will have 2-WAY adjacency with all
+routers but the DR and BDR.
+Routers that form 2-WAY adjacency recognize that they know each other.
+In case a DR or BDR should fail another router is elected and all routers
+change state on the newly elected router and syncronize to it.
+.Pp
+When routers are connected via point to point links, the DR and BDR
+election is skipped since only two routers are connected.
+.Pp
+To limit the impact changes in the network have on the LSDB it is possible
+to segement OSPF network into areas.
+Area 0.0.0.0 a.k.a. the backbone area must always be present.
+Routers can be configured as Area Border Router
+.Pq ABR ,
+being part of multiple areas.
+Every area must have direct access to the backbone area.
+ABRs not directly connected to the backbone area need to establish a
+virtual link to a router in the backbone area.
+Virtual links are currently not available in
+.Nm .
+.Pp
+AS Border Routers
+.Pq ASBR
+are connected to an OSPF network and other external networks, BGP, RIP,
+or statically routed.
+.Pp
+.Nm
+is usually started at boot time, and can be enabled by
+setting the following in
+.Pa /etc/rc.conf.local :
+.Pp
+.Dl ospf6d_flags=\&"\&"
+.Pp
+See
+.Xr rc 8
+and
+.Xr rc.conf 8
+for more information on the boot process
+and enabling daemons.
+.Pp
+A running
+.Nm
+can be controlled with the
+.Xr ospf6ctl 8
+utility.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground and log to
+.Em stderr .
+.It Fl f Ar file
+Specify an alternative configuration file.
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
+.It Fl v
+Produce more verbose output.
+.El
+.Sh FILES
+.Bl -tag -width "/var/run/ospf6d.sockXX" -compact
+.It /etc/ospf6d.conf
+Default
+.Nm
+configuration file.
+.It /var/run/ospf6d.sock
+Unix-domain socket used for communication with
+.Xr ospf6ctl 8 .
+.El
+.Sh SEE ALSO
+.Xr ospf6d.conf 5 ,
+.Xr ospf6ctl 8
+.Rs
+.%R RFC 2740
+.%T "OSPF for IPv6"
+.%D December 1999
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 4.2 .
diff --git a/usr.sbin/ospf6d/ospf6d.c b/usr.sbin/ospf6d/ospf6d.c
new file mode 100644
index 00000000000..c8a5d576f11
--- /dev/null
+++ b/usr.sbin/ospf6d/ospf6d.c
@@ -0,0 +1,826 @@
+/* $OpenBSD: ospf6d.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2007 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <event.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "ospf6d.h"
+#include "ospf6.h"
+#include "ospfe.h"
+#include "control.h"
+#include "log.h"
+#include "rde.h"
+
+void main_sig_handler(int, short, void *);
+__dead void usage(void);
+void ospfd_shutdown(void);
+int check_child(pid_t, const char *);
+
+void main_dispatch_ospfe(int, short, void *);
+void main_dispatch_rde(int, short, void *);
+
+int check_file_secrecy(int, const char *);
+void ospf_redistribute_default(int);
+
+int ospf_reload(void);
+int ospf_sendboth(enum imsg_type, void *, u_int16_t);
+int merge_interfaces(struct area *, struct area *);
+struct iface *iface_lookup(struct area *, struct iface *);
+
+int pipe_parent2ospfe[2];
+int pipe_parent2rde[2];
+int pipe_ospfe2rde[2];
+
+struct ospfd_conf *ospfd_conf = NULL;
+struct imsgbuf *ibuf_ospfe;
+struct imsgbuf *ibuf_rde;
+char *conffile;
+
+pid_t ospfe_pid = 0;
+pid_t rde_pid = 0;
+
+/* ARGSUSED */
+void
+main_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * signal handler rules don't apply, libevent decouples for us
+ */
+
+ int die = 0;
+
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ die = 1;
+ /* FALLTHROUGH */
+ case SIGCHLD:
+ if (check_child(ospfe_pid, "ospf engine")) {
+ ospfe_pid = 0;
+ die = 1;
+ }
+ if (check_child(rde_pid, "route decision engine")) {
+ rde_pid = 0;
+ die = 1;
+ }
+ if (die)
+ ospfd_shutdown();
+ break;
+ case SIGHUP:
+ if (ospf_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_debug("configuration reloaded");
+ break;
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-cdnv] [-f file]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ struct area *a;
+ int ch, opts = 0;
+ int debug = 0;
+ int ipforwarding;
+ int mib[4];
+ size_t len;
+
+ conffile = CONF_FILE;
+ ospfd_process = PROC_MAIN;
+
+ while ((ch = getopt(argc, argv, "cdf:nv")) != -1) {
+ switch (ch) {
+ case 'c':
+ opts |= OSPFD_OPT_FORCE_DEMOTE;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'n':
+ opts |= OSPFD_OPT_NOACTION;
+ break;
+ case 'v':
+ if (opts & OSPFD_OPT_VERBOSE)
+ opts |= OSPFD_OPT_VERBOSE2;
+ opts |= OSPFD_OPT_VERBOSE;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ /* start logging */
+ log_init(debug);
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET6;
+ mib[2] = IPPROTO_IPV6;
+ mib[3] = IPCTL_FORWARDING;
+ len = sizeof(ipforwarding);
+ if (sysctl(mib, 4, &ipforwarding, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+
+ if (ipforwarding != 1) {
+ log_warnx("WARNING: IPv6 forwarding NOT enabled, "
+ "running as stub router");
+ opts |= OSPFD_OPT_STUB_ROUTER;
+ }
+
+ /* fetch interfaces early */
+ kif_init();
+
+ /* parse config file */
+ if ((ospfd_conf = parse_config(conffile, opts)) == NULL )
+ exit(1);
+
+ if (ospfd_conf->opts & OSPFD_OPT_NOACTION) {
+ if (ospfd_conf->opts & OSPFD_OPT_VERBOSE)
+ print_config(ospfd_conf);
+ else
+ fprintf(stderr, "configuration OK\n");
+ exit(0);
+ }
+
+ /* check for root privileges */
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ /* check for ospfd user */
+ if (getpwnam(OSPF6D_USER) == NULL)
+ errx(1, "unknown user %s", OSPF6D_USER);
+
+ if (!debug)
+ daemon(1, 0);
+
+ log_info("startup");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
+ pipe_parent2ospfe) == -1)
+ fatal("socketpair");
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2rde) == -1)
+ fatal("socketpair");
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_ospfe2rde) == -1)
+ fatal("socketpair");
+ session_socket_blockmode(pipe_parent2ospfe[0], BM_NONBLOCK);
+ session_socket_blockmode(pipe_parent2ospfe[1], BM_NONBLOCK);
+ session_socket_blockmode(pipe_parent2rde[0], BM_NONBLOCK);
+ session_socket_blockmode(pipe_parent2rde[1], BM_NONBLOCK);
+ session_socket_blockmode(pipe_ospfe2rde[0], BM_NONBLOCK);
+ session_socket_blockmode(pipe_ospfe2rde[1], BM_NONBLOCK);
+
+ /* start children */
+ rde_pid = rde(ospfd_conf, pipe_parent2rde, pipe_ospfe2rde,
+ pipe_parent2ospfe);
+ ospfe_pid = ospfe(ospfd_conf, pipe_parent2ospfe, pipe_ospfe2rde,
+ pipe_parent2rde);
+
+ /* show who we are */
+ setproctitle("parent");
+
+ event_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* setup pipes to children */
+ close(pipe_parent2ospfe[1]);
+ close(pipe_parent2rde[1]);
+ close(pipe_ospfe2rde[0]);
+ close(pipe_ospfe2rde[1]);
+
+ if ((ibuf_ospfe = malloc(sizeof(struct imsgbuf))) == NULL ||
+ (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_ospfe, pipe_parent2ospfe[0], main_dispatch_ospfe);
+ imsg_init(ibuf_rde, pipe_parent2rde[0], main_dispatch_rde);
+
+ /* setup event handler */
+ ibuf_ospfe->events = EV_READ;
+ event_set(&ibuf_ospfe->ev, ibuf_ospfe->fd, ibuf_ospfe->events,
+ ibuf_ospfe->handler, ibuf_ospfe);
+ event_add(&ibuf_ospfe->ev, NULL);
+
+ ibuf_rde->events = EV_READ;
+ event_set(&ibuf_rde->ev, ibuf_rde->fd, ibuf_rde->events,
+ ibuf_rde->handler, ibuf_rde);
+ event_add(&ibuf_rde->ev, NULL);
+
+ if (kr_init(!(ospfd_conf->flags & OSPFD_FLAG_NO_FIB_UPDATE)) == -1)
+ fatalx("kr_init failed");
+
+ /* remove unneded stuff from config */
+ while ((a = LIST_FIRST(&ospfd_conf->area_list)) != NULL) {
+ LIST_REMOVE(a, entry);
+ area_del(a);
+ }
+
+ /* redistribute default */
+ ospf_redistribute_default(IMSG_NETWORK_ADD);
+
+ event_dispatch();
+
+ ospfd_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+void
+ospfd_shutdown(void)
+{
+ pid_t pid;
+
+ if (ospfe_pid)
+ kill(ospfe_pid, SIGTERM);
+
+ if (rde_pid)
+ kill(rde_pid, SIGTERM);
+
+ control_cleanup();
+ kr_shutdown();
+ carp_demote_shutdown();
+
+ do {
+ if ((pid = wait(NULL)) == -1 &&
+ errno != EINTR && errno != ECHILD)
+ fatal("wait");
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ msgbuf_clear(&ibuf_ospfe->w);
+ free(ibuf_ospfe);
+ msgbuf_clear(&ibuf_rde->w);
+ free(ibuf_rde);
+ free(ospfd_conf);
+
+ log_info("terminating");
+ exit(0);
+}
+
+int
+check_child(pid_t pid, const char *pname)
+{
+ int status;
+
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ if (WIFEXITED(status)) {
+ log_warnx("lost child: %s exited", pname);
+ return (1);
+ }
+ if (WIFSIGNALED(status)) {
+ log_warnx("lost child: %s terminated; signal %d",
+ pname, WTERMSIG(status));
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* imsg handling */
+/* ARGSUSED */
+void
+main_dispatch_ospfe(int fd, short event, void *bula)
+{
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ struct demote_msg dmsg;
+ ssize_t n;
+ int shut = 0;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_RELOAD:
+ if (ospf_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_debug("configuration reloaded");
+ break;
+ case IMSG_CTL_FIB_COUPLE:
+ kr_fib_couple();
+ break;
+ case IMSG_CTL_FIB_DECOUPLE:
+ kr_fib_decouple();
+ break;
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_KROUTE_ADDR:
+ kr_show_route(&imsg);
+ break;
+ case IMSG_CTL_IFINFO:
+ if (imsg.hdr.len == IMSG_HEADER_SIZE)
+ kr_ifinfo(NULL, imsg.hdr.pid);
+ else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ)
+ kr_ifinfo(imsg.data, imsg.hdr.pid);
+ else
+ log_warnx("IFINFO request with wrong len");
+ break;
+ case IMSG_DEMOTE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(dmsg))
+ fatalx("invalid size of OE request");
+ memcpy(&dmsg, imsg.data, sizeof(dmsg));
+ carp_demote_set(dmsg.demote_group, dmsg.level);
+ break;
+ default:
+ log_debug("main_dispatch_ospfe: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(ibuf);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ }
+}
+
+/* ARGSUSED */
+void
+main_dispatch_rde(int fd, short event, void *bula)
+{
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ ssize_t n;
+ int shut = 0;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_KROUTE_CHANGE:
+ if (kr_change(imsg.data))
+ log_warn("main_dispatch_rde: error changing "
+ "route");
+ break;
+ case IMSG_KROUTE_DELETE:
+ if (kr_delete(imsg.data))
+ log_warn("main_dispatch_rde: error deleting "
+ "route");
+ break;
+ default:
+ log_debug("main_dispatch_rde: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(ibuf);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+main_imsg_compose_ospfe(int type, pid_t pid, void *data, u_int16_t datalen)
+{
+ imsg_compose(ibuf_ospfe, type, 0, pid, data, datalen);
+}
+
+void
+main_imsg_compose_rde(int type, pid_t pid, void *data, u_int16_t datalen)
+{
+ imsg_compose(ibuf_rde, type, 0, pid, data, datalen);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ warn("cannot stat %s", fname);
+ return (-1);
+ }
+
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+
+ if (st.st_mode & (S_IRWXG | S_IRWXO)) {
+ warnx("%s: group/world readable/writeable", fname);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* this needs to be added here so that ospfctl can be used without libevent */
+void
+imsg_event_add(struct imsgbuf *ibuf)
+{
+ ibuf->events = EV_READ;
+ if (ibuf->w.queued)
+ ibuf->events |= EV_WRITE;
+
+ event_del(&ibuf->ev);
+ event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf);
+ event_add(&ibuf->ev, NULL);
+}
+
+int
+ospf_redistribute(struct kroute *kr, u_int32_t *metric)
+{
+ struct redistribute *r;
+
+ /* stub area router? */
+ if ((ospfd_conf->options & OSPF_OPTION_E) == 0)
+ return (0);
+
+ /* only allow 0.0.0.0/0 via REDISTRIBUTE_DEFAULT */
+ if (kr->prefix.s_addr == INADDR_ANY && kr->prefixlen == 0)
+ return (0);
+
+ SIMPLEQ_FOREACH(r, &ospfd_conf->redist_list, entry) {
+ switch (r->type & ~REDIST_NO) {
+ case REDIST_LABEL:
+ if (kr->rtlabel == r->label) {
+ *metric = r->metric;
+ return (r->type & REDIST_NO ? 0 : 1);
+ }
+ break;
+ case REDIST_STATIC:
+ /*
+ * Dynamic routes are not redistributable. Placed here
+ * so that link local addresses can be redistributed
+ * via a rtlabel.
+ */
+ if (kr->flags & F_DYNAMIC)
+ continue;
+ if (kr->flags & F_STATIC) {
+ *metric = r->metric;
+ return (r->type & REDIST_NO ? 0 : 1);
+ }
+ break;
+ case REDIST_CONNECTED:
+ if (kr->flags & F_DYNAMIC)
+ continue;
+ if (kr->flags & F_CONNECTED) {
+ *metric = r->metric;
+ return (r->type & REDIST_NO ? 0 : 1);
+ }
+ break;
+ case REDIST_ADDR:
+ if (kr->flags & F_DYNAMIC)
+ continue;
+ if ((kr->prefix.s_addr & r->mask.s_addr) ==
+ (r->addr.s_addr & r->mask.s_addr) &&
+ kr->prefixlen >= mask2prefixlen(r->mask.s_addr)) {
+ *metric = r->metric;
+ return (r->type & REDIST_NO? 0 : 1);
+ }
+ break;
+ }
+ }
+
+ return (0);
+}
+
+void
+ospf_redistribute_default(int type)
+{
+ struct rroute rr;
+
+ if (!(ospfd_conf->redistribute & REDISTRIBUTE_DEFAULT))
+ return;
+
+ bzero(&rr, sizeof(rr));
+ rr.metric = ospfd_conf->defaultmetric;
+ main_imsg_compose_rde(type, 0, &rr, sizeof(struct rroute));
+}
+
+int
+ospf_reload(void)
+{
+ struct area *area;
+ struct iface *iface;
+ struct ospfd_conf *xconf;
+
+ if ((xconf = parse_config(conffile, ospfd_conf->opts)) == NULL)
+ return (-1);
+
+ /* send config to childs */
+ if (ospf_sendboth(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1)
+ return (-1);
+
+ /* send interfaces */
+ LIST_FOREACH(area, &xconf->area_list, entry) {
+ if (ospf_sendboth(IMSG_RECONF_AREA, area, sizeof(*area)) == -1)
+ return (-1);
+
+ LIST_FOREACH(iface, &area->iface_list, entry)
+ if (ospf_sendboth(IMSG_RECONF_IFACE, iface,
+ sizeof(*iface)) == -1)
+ return (-1);
+ }
+
+ if (ospf_sendboth(IMSG_RECONF_END, NULL, 0) == -1)
+ return (-1);
+
+ merge_config(ospfd_conf, xconf);
+ /* update redistribute lists */
+ kr_reload();
+ return (0);
+}
+
+int
+ospf_sendboth(enum imsg_type type, void *buf, u_int16_t len)
+{
+ if (imsg_compose(ibuf_ospfe, type, 0, 0, buf, len) == -1)
+ return (-1);
+ if (imsg_compose(ibuf_rde, type, 0, 0, buf, len) == -1)
+ return (-1);
+ return (0);
+}
+
+void
+merge_config(struct ospfd_conf *conf, struct ospfd_conf *xconf)
+{
+ struct area *a, *xa, *na;
+ struct iface *iface;
+ struct redistribute *r;
+
+ /* change of rtr_id needs a restart */
+ conf->flags = xconf->flags;
+ conf->spf_delay = xconf->spf_delay;
+ conf->spf_hold_time = xconf->spf_hold_time;
+ conf->redistribute = xconf->redistribute;
+
+ if (ospfd_process == PROC_MAIN) {
+ /* main process does neither use areas nor interfaces */
+ while ((r = SIMPLEQ_FIRST(&conf->redist_list)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&conf->redist_list, entry);
+ free(r);
+ }
+ while ((r = SIMPLEQ_FIRST(&xconf->redist_list)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&xconf->redist_list, entry);
+ SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry);
+ }
+ goto done;
+ }
+
+ /* merge areas and interfaces */
+ for (a = LIST_FIRST(&conf->area_list); a != NULL; a = na) {
+ na = LIST_NEXT(a, entry);
+ /* find deleted areas */
+ if ((xa = area_find(xconf, a->id)) == NULL) {
+ if (ospfd_process == PROC_OSPF_ENGINE) {
+ LIST_FOREACH(iface, &a->iface_list, entry)
+ if_fsm(iface, IF_EVT_DOWN);
+ }
+ LIST_REMOVE(a, entry);
+ area_del(a);
+ }
+ }
+
+ for (xa = LIST_FIRST(&xconf->area_list); xa != NULL; xa = na) {
+ na = LIST_NEXT(xa, entry);
+ if ((a = area_find(conf, xa->id)) == NULL) {
+ LIST_REMOVE(xa, entry);
+ LIST_INSERT_HEAD(&conf->area_list, xa, entry);
+ if (ospfd_process == PROC_OSPF_ENGINE) {
+ /* start interfaces */
+ ospfe_demote_area(xa, 0);
+ LIST_FOREACH(iface, &xa->iface_list, entry) {
+ if_init(conf, iface);
+ if (if_fsm(iface, IF_EVT_UP)) {
+ log_debug("error starting "
+ "interface %s",
+ iface->name);
+ }
+ }
+ }
+ /* no need to merge interfaces */
+ continue;
+ }
+ /*
+ * stub is not yet used but switching between stub and normal
+ * will be another painful job.
+ */
+ a->stub = xa->stub;
+ a->stub_default_cost = xa->stub_default_cost;
+ if (ospfd_process == PROC_RDE_ENGINE)
+ a->dirty = 1; /* force SPF tree recalculation */
+
+ /* merge interfaces */
+ if (merge_interfaces(a, xa) &&
+ ospfd_process == PROC_OSPF_ENGINE)
+ a->dirty = 1; /* force rtr LSA update */
+ }
+
+ if (ospfd_process == PROC_OSPF_ENGINE) {
+ LIST_FOREACH(a, &conf->area_list, entry) {
+ LIST_FOREACH(iface, &a->iface_list, entry) {
+ if (iface->state == IF_STA_NEW) {
+ iface->state = IF_STA_DOWN;
+ if_init(conf, iface);
+ if (if_fsm(iface, IF_EVT_UP)) {
+ log_debug("error starting "
+ "interface %s",
+ iface->name);
+ }
+ }
+ }
+ if (a->dirty) {
+ a->dirty = 0;
+ orig_rtr_lsa(a);
+ }
+ }
+ }
+
+done:
+ while ((a = LIST_FIRST(&xconf->area_list)) != NULL) {
+ LIST_REMOVE(a, entry);
+ area_del(a);
+ }
+ free(xconf);
+}
+
+int
+merge_interfaces(struct area *a, struct area *xa)
+{
+ struct iface *i, *xi, *ni;
+ int dirty = 0;
+
+ /* problems:
+ * - new interfaces (easy)
+ * - deleted interfaces (needs to be done via fsm?)
+ * - changing passive (painful?)
+ */
+ for (i = LIST_FIRST(&a->iface_list); i != NULL; i = ni) {
+ ni = LIST_NEXT(i, entry);
+ if (iface_lookup(xa, i) == NULL) {
+ log_debug("merge_config: proc %d area %s removing "
+ "interface %s", ospfd_process, inet_ntoa(a->id),
+ i->name);
+ if (ospfd_process == PROC_OSPF_ENGINE)
+ if_fsm(i, IF_EVT_DOWN);
+ LIST_REMOVE(i, entry);
+ if_del(i);
+ }
+ }
+
+ for (xi = LIST_FIRST(&xa->iface_list); xi != NULL; xi = ni) {
+ ni = LIST_NEXT(xi, entry);
+ if ((i = iface_lookup(a, xi)) == NULL) {
+ /* new interface but delay initialisation */
+ log_debug("merge_config: proc %d area %s adding "
+ "interface %s", ospfd_process, inet_ntoa(a->id),
+ xi->name);
+ LIST_REMOVE(xi, entry);
+ LIST_INSERT_HEAD(&a->iface_list, xi, entry);
+ xi->area = a;
+ if (ospfd_process == PROC_OSPF_ENGINE)
+ xi->state = IF_STA_NEW;
+ continue;
+ }
+ log_debug("merge_config: proc %d area %s merging interface %s",
+ ospfd_process, inet_ntoa(a->id), i->name);
+ i->addr = xi->addr;
+ i->dst = xi->dst;
+ i->mask = xi->mask;
+ i->abr_id = xi->abr_id;
+ i->baudrate = xi->baudrate;
+ i->dead_interval = xi->dead_interval;
+ i->mtu = xi->mtu;
+ i->transmit_delay = xi->transmit_delay;
+ i->hello_interval = xi->hello_interval;
+ i->rxmt_interval = xi->rxmt_interval;
+ if (i->metric != xi->metric)
+ dirty = 1;
+ i->metric = xi->metric;
+ i->priority = xi->priority;
+ i->flags = xi->flags; /* needed? */
+ i->type = xi->type; /* needed? */
+ i->media_type = xi->media_type; /* needed? */
+ i->linkstate = xi->linkstate; /* needed? */
+
+ if (i->passive != xi->passive) {
+ /* need to restart interface to cope with this change */
+ if (ospfd_process == PROC_OSPF_ENGINE)
+ if_fsm(i, IF_EVT_DOWN);
+ i->passive = xi->passive;
+ if (ospfd_process == PROC_OSPF_ENGINE)
+ if_fsm(i, IF_EVT_UP);
+ }
+ }
+ return (dirty);
+}
+
+struct iface *
+iface_lookup(struct area *area, struct iface *iface)
+{
+ struct iface *i;
+
+ LIST_FOREACH(i, &area->iface_list, entry)
+ if (i->ifindex == iface->ifindex)
+ return (i);
+ return (NULL);
+}
diff --git a/usr.sbin/ospf6d/ospf6d.conf.5 b/usr.sbin/ospf6d/ospf6d.conf.5
new file mode 100644
index 00000000000..5404d7f9312
--- /dev/null
+++ b/usr.sbin/ospf6d/ospf6d.conf.5
@@ -0,0 +1,311 @@
+.\" $OpenBSD: ospf6d.conf.5,v 1.1 2007/10/08 10:44:50 norby Exp $
+.\"
+.\" Copyright (c) 2005 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 $Mdocdate: October 8 2007 $
+.Dt OSPF6D.CONF 5
+.Os
+.Sh NAME
+.Nm ospf6d.conf
+.Nd Open Shortest Path First daemon configuration file
+.Sh DESCRIPTION
+The
+.Xr ospf6d 8
+daemon implements the Open Shortest Path First protocol version 3 as described
+in RFC 2740.
+.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 ospf6d 8 .
+A number of global settings can be overruled in specific areas or interfaces.
+.It Sy Areas
+An OSPF router must be a member of at least one area.
+Areas are used to group interfaces, simplifying configuration.
+.El
+.Sh MACROS
+Macros can be defined that will later be expanded in context.
+Macro names must start with a letter, and may contain letters, digits,
+and underscores.
+Macro names may not be reserved words (for example,
+.Ic area ,
+.Ic interface ,
+or
+.Ic hello-interval ) .
+Macros are not expanded inside quotes.
+.Pp
+For example:
+.Bd -literal -offset indent
+hi="5"
+area 0.0.0.0 {
+ interface em0 {
+ hello-interval $hi
+ }
+}
+.Ed
+.Pp
+The same can be accomplished by specifying the hello-interval
+globally or within the area declaration.
+.Sh GLOBAL CONFIGURATION
+All interface related settings can be configured globally, per area and per
+interface.
+The only settings that can be set globally and not overruled are listed below.
+.Pp
+.Bl -tag -width Ds -compact
+.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 .
+Setting
+.Ic fib-update
+to
+.Ic \&no
+will implicitly set the
+.Ic stub Ic router
+option to ensure that no trafic tries to transit via this router.
+.Pp
+.It Xo
+.Op Ic no
+.Ic redistribute
+.Sm off
+.Po Ic static Ns \&| Ns Ic connected Ns \&| Ns
+.Ic default Pc
+.Sm on
+.Op Ic set ...\&
+.Xc
+.It Xo
+.Op Ic no
+.Ic redistribute Ar prefix Op Ic set ...\&
+.Xc
+.It Xo
+.Op Ic no
+.Ic redistribute rtlabel Ar label Op Ic set ...\&
+.Xc
+If set to
+.Ic connected ,
+routes to directly attached networks will be announced over OSPF.
+If set to
+.Ic static ,
+static routes will be announced over OSPF.
+If set to
+.Ic default ,
+a default route pointing to this router will be announced over OSPF.
+It is possible to specify a network range with
+.Ar prefix ;
+networks need to be part of that range to be redistributed.
+Additionally it is possible to redistribute based on route labels
+using the
+.Ic rtlabel
+keyword.
+By default no additional routes will be announced over OSPF.
+.Pp
+.Ic redistribute
+statements are evaluated in sequential order, from first to last.
+The first matching rule decides if a route should be redistributed or not.
+Matching rules starting with
+.Ic no
+will force the route to be not announced.
+The only exception is
+.Ic default ,
+which will be set no matter what, and additionally
+.Ic no
+cannot be used together with it.
+.Pp
+It is possible to set the route
+.Ic metric
+and
+.Ic type
+for each redistribute rule.
+.Ic type
+is either 1 or 2.
+The default value for
+.Ic type
+is 1 and for
+.Ic metric
+is 100.
+Setting more then one option needs curly brackets:
+.Bd -literal -offset indent
+redistribute static set { metric 300 type 2 }
+.Ed
+.Pp
+.It Xo
+.Ic rfc1583compat
+.Pq Ic yes Ns \&| Ns Ic no
+.Xc
+If set to
+.Ic yes ,
+decisions regarding AS-external routes are evaluated according to RFC 1583.
+The default is
+.Ic no .
+.Pp
+.It Ic router-id Ar address
+Set the router ID; if not specified, the lowest IP address of the router
+will be used.
+.Pp
+.It Ic rtlabel Ar label Ic external-tag Ar number
+Map route labels to external route tags and vice versa.
+The external route tag is a non-negative 32-bit number attached to
+AS-external OSPF LSAs.
+.Pp
+.It Ic spf-delay Ar seconds
+Set SPF delay in seconds.
+The delay between receiving an update to the link
+state database and starting the shortest path first calculation.
+The default value is 1; valid range is 1\-10 seconds.
+.Pp
+.It Ic spf-holdtime Ar seconds
+Set the SPF holdtime in seconds.
+The minimum time between two consecutive
+shortest path first calculations.
+The default value is 5 seconds; the valid range is 1\-5 seconds.
+.Pp
+.It Xo
+.Ic stub Ic router
+.Pq Ic yes Ns \&| Ns Ic no
+.Xc
+If set to
+.Ic yes ,
+all interfaces with active neighbors will have a metric of infinity.
+This ensures that the other routers prefer routes around this router while
+still being able to reach directly connected IP prefixes.
+The
+.Ic stub Ic router
+option is automatically enabled if either the
+.Xr sysctl 8
+variable
+.Va net.inet.ip.forwarding
+is set to a value different to 1 or if the FIB is not coupled.
+.El
+.Sh AREAS
+Areas are used for grouping interfaces.
+All interface specific parameters can
+be configured per area, overruling the global settings.
+.Bl -tag -width Ds
+.It Ic area Ar address
+Specify an area section, grouping one or more interfaces.
+.Bd -literal -offset indent
+area 0.0.0.0 {
+ interface em0
+ interface em1 {
+ metric 10
+ }
+}
+.Ed
+.El
+.Pp
+Area specific parameters are listed below.
+.Bl -tag -width Ds
+.It Ic demote Ar group Op Ar count
+Increase the
+.Xr carp 4
+demotion counter by
+.Ar count
+on the given interface group, usually
+.Ar carp ,
+when no neighbor in the area is in an active state.
+The demotion counter will be decreased when one neighbor in that
+area is in an active state.
+The default value for
+.Ar count
+is 1.
+.Pp
+For more information on interface groups,
+see the
+.Ic group
+keyword in
+.Xr ifconfig 8 .
+.El
+.Sh INTERFACES
+Each interface can have several parameters configured individually, otherwise
+they are inherited.
+An interface is specified by its name.
+If multiple networks are configured an additional IP address can be supplied.
+By default the first IP address is used.
+.Bd -literal -offset indent
+interface em0 {
+ ...
+}
+interface fxp0:192.168.1.3
+.Ed
+.Pp
+Interface specific parameters are listed below.
+.Bl -tag -width Ds
+.It Ic demote Ar group
+Increase the
+.Xr carp 4
+demotion counter by 1 on the given interface group, usually
+.Ar carp ,
+when the interface state is going down.
+The demotion counter will be decreased when the interface
+state is active again.
+.It Ic hello-interval Ar seconds
+Set the hello interval.
+The default value is 10; valid range is 1\-65535 seconds.
+.It Ic metric Ar cost
+Set the interface metric a.k.a. cost.
+The default value is 10; valid range is 1\-65535.
+.It Ic passive
+Prevent transmission and reception of OSPF packets on this interface.
+The specified interface will be announced as a stub network.
+.It Ic retransmit-interval Ar seconds
+Set retransmit interval.
+The default value is 5 seconds; valid range is 5\-3600 seconds.
+.It Ic router-dead-time Ar seconds
+Set the router dead time, a.k.a. neighbor inactivity timer.
+The default value is 40 seconds; valid range is 2\-2147483647 seconds.
+When a neighbor has been
+inactive for router-dead-time its state is set to DOWN.
+Neighbors
+that have been inactive for more than 24 hours are completely removed.
+.It Ic router-priority Ar priority
+Set the router priority.
+The default value is 1; valid range is 0\-255.
+If set
+to 0 the router is not eligible as a Designated Router or Backup Designated
+Router.
+.It Ic transmit-delay Ar seconds
+Set the transmit delay.
+The default value is 1; valid range is 1\-3600 seconds.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/ospf6d.conf" -compact
+.It Pa /etc/ospf6d.conf
+.Xr ospf6d 8
+configuration file
+.El
+.Sh SEE ALSO
+.Xr ospf6ctl 8 ,
+.Xr ospf6d 8 ,
+.Xr rc.conf.local 8
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 4.2 .
diff --git a/usr.sbin/ospf6d/ospf6d.h b/usr.sbin/ospf6d/ospf6d.h
new file mode 100644
index 00000000000..7da73e70a57
--- /dev/null
+++ b/usr.sbin/ospf6d/ospf6d.h
@@ -0,0 +1,618 @@
+/* $OpenBSD: ospf6d.h,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2007 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 _OSPF6D_H_
+#define _OSPF6D_H_
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+#include <md5.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <event.h>
+
+#include "ospf6.h"
+
+#define CONF_FILE "/etc/ospf6d.conf"
+#define OSPF6D_SOCKET "/var/run/ospf6d.sock"
+#define OSPF6D_USER "_ospf6d"
+
+#define NBR_HASHSIZE 128
+#define LSA_HASHSIZE 512
+
+#define NBR_IDSELF 1
+#define NBR_CNTSTART (NBR_IDSELF + 1)
+
+#define READ_BUF_SIZE 65535
+#define PKG_DEF_SIZE 512 /* compromise */
+#define RT_BUF_SIZE 16384
+#define MAX_RTSOCK_BUF 128 * 1024
+
+#define OSPFD_FLAG_NO_FIB_UPDATE 0x0001
+#define OSPFD_FLAG_STUB_ROUTER 0x0002
+
+#define F_OSPFD_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_REDISTRIBUTED 0x0100
+
+#define REDISTRIBUTE_ON 0x01
+#define REDISTRIBUTE_DEFAULT 0x02
+
+/* 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_RELOAD,
+ IMSG_CTL_SHOW_INTERFACE,
+ IMSG_CTL_SHOW_DATABASE,
+ IMSG_CTL_SHOW_DB_EXT,
+ IMSG_CTL_SHOW_DB_NET,
+ IMSG_CTL_SHOW_DB_RTR,
+ IMSG_CTL_SHOW_DB_SELF,
+ IMSG_CTL_SHOW_DB_SUM,
+ IMSG_CTL_SHOW_DB_ASBR,
+ IMSG_CTL_SHOW_NBR,
+ IMSG_CTL_SHOW_RIB,
+ IMSG_CTL_SHOW_SUM,
+ IMSG_CTL_SHOW_SUM_AREA,
+ IMSG_CTL_FIB_COUPLE,
+ IMSG_CTL_FIB_DECOUPLE,
+ IMSG_CTL_AREA,
+ IMSG_CTL_KROUTE,
+ IMSG_CTL_KROUTE_ADDR,
+ IMSG_CTL_IFINFO,
+ IMSG_CTL_END,
+ IMSG_KROUTE_CHANGE,
+ IMSG_KROUTE_DELETE,
+ IMSG_KROUTE_GET,
+ IMSG_IFINFO,
+ IMSG_NEIGHBOR_UP,
+ IMSG_NEIGHBOR_DOWN,
+ IMSG_NEIGHBOR_CHANGE,
+ IMSG_NETWORK_ADD,
+ IMSG_NETWORK_DEL,
+ IMSG_DD,
+ IMSG_DD_END,
+ IMSG_DB_SNAPSHOT,
+ IMSG_DB_END,
+ IMSG_LS_REQ,
+ IMSG_LS_UPD,
+ IMSG_LS_ACK,
+ IMSG_LS_FLOOD,
+ IMSG_LS_BADREQ,
+ IMSG_LS_MAXAGE,
+ IMSG_ABR_UP,
+ IMSG_ABR_DOWN,
+ IMSG_RECONF_CONF,
+ IMSG_RECONF_AREA,
+ IMSG_RECONF_IFACE,
+ IMSG_RECONF_END,
+ IMSG_DEMOTE
+};
+
+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;
+};
+
+/* area */
+struct vertex;
+struct rde_nbr;
+RB_HEAD(lsa_tree, vertex);
+
+struct area {
+ LIST_ENTRY(area) entry;
+ struct in_addr id;
+ struct lsa_tree lsa_tree;
+
+ LIST_HEAD(, iface) iface_list;
+ LIST_HEAD(, rde_nbr) nbr_list;
+/* list addr_range_list; */
+ char demote_group[IFNAMSIZ];
+ u_int32_t stub_default_cost;
+ u_int32_t num_spf_calc;
+ int active;
+ u_int8_t transit;
+ u_int8_t stub;
+ u_int8_t dirty;
+ u_int8_t demote_level;
+};
+
+/* interface states */
+#define IF_STA_NEW 0x00 /* dummy state for reload */
+#define IF_STA_DOWN 0x01
+#define IF_STA_LOOPBACK 0x02
+#define IF_STA_WAITING 0x04
+#define IF_STA_POINTTOPOINT 0x08
+#define IF_STA_DROTHER 0x10
+#define IF_STA_BACKUP 0x20
+#define IF_STA_DR 0x40
+#define IF_STA_DRORBDR (IF_STA_DR | IF_STA_BACKUP)
+#define IF_STA_MULTI (IF_STA_DROTHER | IF_STA_BACKUP | IF_STA_DR)
+#define IF_STA_ANY 0x7f
+
+/* interface events */
+enum iface_event {
+ IF_EVT_NOTHING,
+ IF_EVT_UP,
+ IF_EVT_WTIMER,
+ IF_EVT_BACKUP_SEEN,
+ IF_EVT_NBR_CHNG,
+ IF_EVT_LOOP,
+ IF_EVT_UNLOOP,
+ IF_EVT_DOWN
+};
+
+/* interface actions */
+enum iface_action {
+ IF_ACT_NOTHING,
+ IF_ACT_STRT,
+ IF_ACT_ELECT,
+ IF_ACT_RST
+};
+
+/* interface types */
+enum iface_type {
+ IF_TYPE_POINTOPOINT,
+ IF_TYPE_BROADCAST,
+ IF_TYPE_NBMA,
+ IF_TYPE_POINTOMULTIPOINT,
+ IF_TYPE_VIRTUALLINK
+};
+
+/* neighbor states */
+#define NBR_STA_DOWN 0x0001
+#define NBR_STA_ATTEMPT 0x0002
+#define NBR_STA_INIT 0x0004
+#define NBR_STA_2_WAY 0x0008
+#define NBR_STA_XSTRT 0x0010
+#define NBR_STA_SNAP 0x0020
+#define NBR_STA_XCHNG 0x0040
+#define NBR_STA_LOAD 0x0080
+#define NBR_STA_FULL 0x0100
+#define NBR_STA_ACTIVE (~NBR_STA_DOWN)
+#define NBR_STA_FLOOD (NBR_STA_XCHNG | NBR_STA_LOAD | NBR_STA_FULL)
+#define NBR_STA_ADJFORM (NBR_STA_XSTRT | NBR_STA_SNAP | NBR_STA_FLOOD)
+#define NBR_STA_BIDIR (NBR_STA_2_WAY | NBR_STA_ADJFORM)
+#define NBR_STA_PRELIM (NBR_STA_DOWN | NBR_STA_ATTEMPT | NBR_STA_INIT)
+#define NBR_STA_ANY 0xffff
+
+/* neighbor events */
+enum nbr_event {
+ NBR_EVT_NOTHING,
+ NBR_EVT_HELLO_RCVD,
+ NBR_EVT_2_WAY_RCVD,
+ NBR_EVT_NEG_DONE,
+ NBR_EVT_SNAP_DONE,
+ NBR_EVT_XCHNG_DONE,
+ NBR_EVT_BAD_LS_REQ,
+ NBR_EVT_LOAD_DONE,
+ NBR_EVT_ADJ_OK,
+ NBR_EVT_SEQ_NUM_MIS,
+ NBR_EVT_1_WAY_RCVD,
+ NBR_EVT_KILL_NBR,
+ NBR_EVT_ITIMER,
+ NBR_EVT_LL_DOWN,
+ NBR_EVT_ADJTMOUT
+};
+
+/* neighbor actions */
+enum nbr_action {
+ NBR_ACT_NOTHING,
+ NBR_ACT_RST_ITIMER,
+ NBR_ACT_STRT_ITIMER,
+ NBR_ACT_EVAL,
+ NBR_ACT_SNAP,
+ NBR_ACT_SNAP_DONE,
+ NBR_ACT_XCHNG_DONE,
+ NBR_ACT_ADJ_OK,
+ NBR_ACT_RESTRT_DD,
+ NBR_ACT_DEL,
+ NBR_ACT_CLR_LST,
+ NBR_ACT_HELLO_CHK
+};
+
+/* spf states */
+enum spf_state {
+ SPF_IDLE,
+ SPF_DELAY,
+ SPF_HOLD,
+ SPF_HOLDQUEUE
+};
+
+enum dst_type {
+ DT_NET,
+ DT_RTR
+};
+
+enum path_type {
+ PT_INTRA_AREA,
+ PT_INTER_AREA,
+ PT_TYPE1_EXT,
+ PT_TYPE2_EXT
+};
+
+enum rib_type {
+ RIB_NET = 1,
+ RIB_RTR,
+ RIB_EXT
+};
+
+/* lsa list used in RDE and OE */
+TAILQ_HEAD(lsa_head, lsa_entry);
+
+struct iface {
+ LIST_ENTRY(iface) entry;
+ struct event hello_timer;
+ struct event wait_timer;
+ struct event lsack_tx_timer;
+
+ LIST_HEAD(, nbr) nbr_list;
+ struct lsa_head ls_ack_list;
+
+ char name[IF_NAMESIZE];
+ char demote_group[IFNAMSIZ];
+ struct in6_addr addr;
+ struct in6_addr dst;
+ struct in6_addr mask;
+ struct in_addr abr_id;
+ struct nbr *dr; /* designated router */
+ struct nbr *bdr; /* backup designated router */
+ struct nbr *self;
+ struct area *area;
+
+ u_int64_t baudrate;
+ u_int32_t dead_interval;
+ u_int32_t ls_ack_cnt;
+ u_int32_t crypt_seq_num;
+ time_t uptime;
+ unsigned int ifindex;
+ int fd;
+ int state;
+ int mtu;
+ u_int16_t flags;
+ u_int16_t transmit_delay;
+ u_int16_t hello_interval;
+ u_int16_t rxmt_interval;
+ u_int16_t metric;
+ enum iface_type type;
+ u_int8_t media_type;
+ u_int8_t linkstate;
+ u_int8_t priority;
+ u_int8_t passive;
+};
+
+/* ospf_conf */
+enum {
+ PROC_MAIN,
+ PROC_OSPF_ENGINE,
+ PROC_RDE_ENGINE
+} ospfd_process;
+
+#define REDIST_CONNECTED 0x01
+#define REDIST_STATIC 0x02
+#define REDIST_LABEL 0x04
+#define REDIST_ADDR 0x08
+#define REDIST_NO 0x10
+
+struct redistribute {
+ SIMPLEQ_ENTRY(redistribute) entry;
+ struct in_addr addr;
+ struct in_addr mask;
+ u_int32_t metric;
+ u_int16_t label;
+ u_int16_t type;
+};
+
+struct ospfd_conf {
+ struct event ev;
+ struct in_addr rtr_id;
+ LIST_HEAD(, area) area_list;
+ LIST_HEAD(, vertex) cand_list;
+ SIMPLEQ_HEAD(, redistribute) redist_list;
+
+ u_int32_t defaultmetric;
+ u_int32_t opts;
+#define OSPFD_OPT_VERBOSE 0x00000001
+#define OSPFD_OPT_VERBOSE2 0x00000002
+#define OSPFD_OPT_NOACTION 0x00000004
+#define OSPFD_OPT_STUB_ROUTER 0x00000008
+#define OSPFD_OPT_FORCE_DEMOTE 0x00000010
+ u_int32_t spf_delay;
+ u_int32_t spf_hold_time;
+ time_t uptime;
+ int spf_state;
+ int ospf_socket;
+ int flags;
+ int options; /* OSPF options */
+ u_int8_t border;
+ u_int8_t redistribute;
+};
+
+/* kroute */
+struct kroute {
+ struct in_addr prefix;
+ struct in_addr nexthop;
+ u_int16_t flags;
+ u_int16_t rtlabel;
+ u_int32_t ext_tag;
+ u_short ifindex;
+ u_int8_t prefixlen;
+};
+
+struct rroute {
+ struct kroute kr;
+ u_int32_t metric;
+};
+
+struct kif_addr {
+ TAILQ_ENTRY(kif_addr) entry;
+ struct in6_addr addr;
+ struct in6_addr mask;
+ struct in6_addr dstbrd;
+};
+
+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 */
+};
+
+/* name2id */
+struct n2id_label {
+ TAILQ_ENTRY(n2id_label) entry;
+ char *name;
+ u_int16_t id;
+ u_int32_t ext_tag;
+ int ref;
+};
+
+TAILQ_HEAD(n2id_labels, n2id_label);
+extern struct n2id_labels rt_labels;
+
+/* control data structures */
+struct ctl_iface {
+ char name[IF_NAMESIZE];
+ struct in6_addr addr;
+ struct in6_addr mask;
+ struct in_addr area;
+ struct in_addr rtr_id;
+ struct in_addr dr_id;
+ struct in6_addr dr_addr;
+ struct in_addr bdr_id;
+ struct in6_addr bdr_addr;
+ time_t hello_timer;
+ time_t uptime;
+ u_int32_t baudrate;
+ u_int32_t dead_interval;
+ unsigned int ifindex;
+ int state;
+ int mtu;
+ int nbr_cnt;
+ int adj_cnt;
+ u_int16_t transmit_delay;
+ u_int16_t hello_interval;
+ u_int16_t flags;
+ u_int16_t metric;
+ u_int16_t rxmt_interval;
+ enum iface_type type;
+ u_int8_t linkstate;
+ u_int8_t mediatype;
+ u_int8_t priority;
+ u_int8_t passive;
+};
+
+struct ctl_nbr {
+ char name[IF_NAMESIZE];
+ struct in_addr id;
+ struct in6_addr addr;
+ struct in_addr dr;
+ struct in_addr bdr;
+ struct in_addr area;
+ time_t dead_timer;
+ time_t uptime;
+ u_int32_t db_sum_lst_cnt;
+ u_int32_t ls_req_lst_cnt;
+ u_int32_t ls_retrans_lst_cnt;
+ u_int32_t state_chng_cnt;
+ int nbr_state;
+ int iface_state;
+ u_int8_t priority;
+ u_int8_t options;
+};
+
+struct ctl_rt {
+ struct in_addr prefix;
+ struct in_addr nexthop;
+ struct in_addr area;
+ struct in_addr adv_rtr;
+ time_t uptime;
+ u_int32_t cost;
+ u_int32_t cost2;
+ enum path_type p_type;
+ enum dst_type d_type;
+ u_int8_t flags;
+ u_int8_t prefixlen;
+};
+
+struct ctl_sum {
+ struct in_addr rtr_id;
+ u_int32_t spf_delay;
+ u_int32_t spf_hold_time;
+ u_int32_t num_ext_lsa;
+ u_int32_t num_area;
+ time_t uptime;
+};
+
+struct ctl_sum_area {
+ struct in_addr area;
+ u_int32_t num_iface;
+ u_int32_t num_adj_nbr;
+ u_int32_t num_spf_calc;
+ u_int32_t num_lsa;
+};
+
+struct demote_msg {
+ char demote_group[IF_NAMESIZE];
+ int level;
+};
+
+/* area.c */
+struct area *area_new(void);
+int area_del(struct area *);
+struct area *area_find(struct ospfd_conf *, struct in_addr);
+void area_track(struct area *, int);
+int area_border_router(struct ospfd_conf *);
+
+/* 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 *);
+
+/* carp.c */
+int carp_demote_init(char *, int);
+void carp_demote_shutdown(void);
+int carp_demote_get(char *);
+int carp_demote_set(char *, int);
+
+/* parse.y */
+struct ospfd_conf *parse_config(char *, int);
+int cmdline_symset(char *);
+
+/* imsg.c */
+void imsg_init(struct imsgbuf *, int, void (*)(int, short, void *));
+ssize_t imsg_read(struct imsgbuf *);
+ssize_t imsg_get(struct imsgbuf *, struct imsg *);
+int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t,
+ void *, u_int16_t);
+struct buf *imsg_create(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t,
+ u_int16_t);
+int imsg_add(struct buf *, void *, u_int16_t);
+int imsg_close(struct imsgbuf *, struct buf *);
+void imsg_free(struct imsg *);
+void imsg_event_add(struct imsgbuf *); /* needs to be provided externally */
+
+/* in_cksum.c */
+u_int16_t in_cksum(void *, size_t);
+
+/* iso_cksum.c */
+u_int16_t iso_cksum(void *, u_int16_t, u_int16_t);
+
+/* kroute.c */
+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 *, struct in6_addr *, struct kif_addr **);
+void kr_reload(void);
+
+u_int8_t mask2prefixlen(in_addr_t);
+in_addr_t prefixlen2mask(u_int8_t);
+
+/* log.h */
+const char *nbr_state_name(int);
+const char *if_state_name(int);
+const char *if_type_name(enum iface_type);
+const char *dst_type_name(enum dst_type);
+const char *path_type_name(enum path_type);
+
+/* name2id.c */
+u_int16_t rtlabel_name2id(const char *);
+const char *rtlabel_id2name(u_int16_t);
+void rtlabel_unref(u_int16_t);
+u_int32_t rtlabel_id2tag(u_int16_t);
+u_int16_t rtlabel_tag2id(u_int32_t);
+void rtlabel_tag(u_int16_t, u_int32_t);
+
+/* ospfd.c */
+void main_imsg_compose_ospfe(int, pid_t, void *, u_int16_t);
+void main_imsg_compose_rde(int, pid_t, void *, u_int16_t);
+int ospf_redistribute(struct kroute *, u_int32_t *);
+void merge_config(struct ospfd_conf *, struct ospfd_conf *);
+
+/* printconf.c */
+void print_config(struct ospfd_conf *);
+
+#endif /* _OSPF6D_H_ */
diff --git a/usr.sbin/ospf6d/ospfe.c b/usr.sbin/ospf6d/ospfe.c
new file mode 100644
index 00000000000..9b09c98ea6e
--- /dev/null
+++ b/usr.sbin/ospf6d/ospfe.c
@@ -0,0 +1,1094 @@
+/* $OpenBSD: ospfe.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * 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 "ospf6.h"
+#include "ospf6d.h"
+#include "ospfe.h"
+#include "rde.h"
+#include "control.h"
+#include "log.h"
+
+void ospfe_sig_handler(int, short, void *);
+void ospfe_shutdown(void);
+void orig_rtr_lsa_all(struct area *);
+struct iface *find_vlink(struct abr_rtr *);
+
+struct ospfd_conf *oeconf = NULL, *nconf;
+struct imsgbuf *ibuf_main;
+struct imsgbuf *ibuf_rde;
+int oe_nofib;
+
+/* ARGSUSED */
+void
+ospfe_sig_handler(int sig, short event, void *bula)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ ospfe_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+/* ospf engine */
+pid_t
+ospfe(struct ospfd_conf *xconf, int pipe_parent2ospfe[2], int pipe_ospfe2rde[2],
+ int pipe_parent2rde[2])
+{
+ struct area *area;
+ struct iface *iface;
+ struct redistribute *r;
+ 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 ospfd control socket outside chroot */
+ if (control_init() == -1)
+ fatalx("control socket setup failed");
+
+ /* create the raw ip socket */
+ if ((xconf->ospf_socket = socket(AF_INET6, SOCK_RAW,
+ IPPROTO_OSPF)) == -1)
+ fatal("error creating raw socket");
+
+ /* set some defaults */
+ if (if_set_mcast_loop(xconf->ospf_socket) == -1)
+ fatal("if_set_mcast_loop");
+ if (if_set_ip_hdrincl(xconf->ospf_socket) == -1)
+ fatal("if_set_ip_hdrincl");
+ if (if_set_recvif(xconf->ospf_socket, 1) == -1)
+ fatal("if_set_recvif");
+ if_set_recvbuf(xconf->ospf_socket);
+
+ oeconf = xconf;
+ if (oeconf->flags & OSPFD_FLAG_NO_FIB_UPDATE)
+ oe_nofib = 1;
+
+ if ((pw = getpwnam(OSPF6D_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("ospf engine");
+ ospfd_process = PROC_OSPF_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);
+ lsa_cache_init(LSA_HASHSIZE);
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, ospfe_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, ospfe_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ /* setup pipes */
+ close(pipe_parent2ospfe[0]);
+ close(pipe_ospfe2rde[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_ospfe2rde[0], ospfe_dispatch_rde);
+ imsg_init(ibuf_main, pipe_parent2ospfe[1], ospfe_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->ospf_socket, EV_READ|EV_PERSIST,
+ recv_packet, oeconf);
+ event_add(&oeconf->ev, NULL);
+
+ /* remove unneeded config stuff */
+ while ((r = SIMPLEQ_FIRST(&oeconf->redist_list)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&oeconf->redist_list, entry);
+ free(r);
+ }
+
+ /* listen on ospfd control socket */
+ TAILQ_INIT(&ctl_conns);
+ control_listen();
+
+ if ((pkt_ptr = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("ospfe");
+
+ /* start interfaces */
+ LIST_FOREACH(area, &oeconf->area_list, entry) {
+ ospfe_demote_area(area, 0);
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if_init(xconf, iface);
+ log_debug("fire up the barbeque on %s", iface->name);
+ if (if_fsm(iface, IF_EVT_UP)) {
+ log_debug("error starting interface %s",
+ iface->name);
+ }
+ }
+ }
+
+ event_dispatch();
+
+ ospfe_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+void
+ospfe_shutdown(void)
+{
+ struct area *area;
+ struct iface *iface;
+
+ /* stop all interfaces and remove all areas */
+ while ((area = LIST_FIRST(&oeconf->area_list)) != NULL) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if (if_fsm(iface, IF_EVT_DOWN)) {
+ log_debug("error stopping interface %s",
+ iface->name);
+ }
+ }
+ LIST_REMOVE(area, entry);
+ area_del(area);
+ }
+
+ close(oeconf->ospf_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("ospf engine exiting");
+ _exit(0);
+}
+
+/* imesg */
+int
+ospfe_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
+ospfe_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
+ospfe_dispatch_main(int fd, short event, void *bula)
+{
+ static struct area *narea;
+ static struct iface *niface;
+ struct imsg imsg;
+ struct imsgbuf *ibuf = bula;
+ struct area *area = NULL;
+ struct iface *iface = NULL;
+ struct kif *kif;
+ int n, link_ok, stub_changed, shut = 0;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ 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("ospfe_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) &&
+ (LINK_STATE_IS_UP(kif->link_state) ||
+ (kif->link_state == LINK_STATE_UNKNOWN &&
+ kif->media_type != IFT_CARP));
+
+ LIST_FOREACH(area, &oeconf->area_list, entry) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if (kif->ifindex == iface->ifindex &&
+ iface->type !=
+ IF_TYPE_VIRTUALLINK) {
+ 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_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct ospfd_conf))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct ospfd_conf));
+
+ LIST_INIT(&nconf->area_list);
+ LIST_INIT(&nconf->cand_list);
+ break;
+ case IMSG_RECONF_AREA:
+ if ((narea = area_new()) == NULL)
+ fatal(NULL);
+ memcpy(narea, imsg.data, sizeof(struct area));
+
+ LIST_INIT(&narea->iface_list);
+ LIST_INIT(&narea->nbr_list);
+ RB_INIT(&narea->lsa_tree);
+
+ LIST_INSERT_HEAD(&nconf->area_list, narea, entry);
+ break;
+ case IMSG_RECONF_IFACE:
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ LIST_INIT(&niface->nbr_list);
+ TAILQ_INIT(&niface->ls_ack_list);
+
+ niface->area = narea;
+ LIST_INSERT_HEAD(&narea->iface_list, niface, entry);
+ break;
+ case IMSG_RECONF_END:
+ if ((oeconf->flags & OSPFD_FLAG_STUB_ROUTER) !=
+ (nconf->flags & OSPFD_FLAG_STUB_ROUTER))
+ stub_changed = 1;
+ else
+ stub_changed = 0;
+ merge_config(oeconf, nconf);
+ nconf = NULL;
+ if (stub_changed)
+ orig_rtr_lsa_all(NULL);
+ break;
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_KROUTE_ADDR:
+ case IMSG_CTL_IFINFO:
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("ospfe_dispatch_main: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(ibuf);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ }
+}
+
+/* ARGSUSED */
+void
+ospfe_dispatch_rde(int fd, short event, void *bula)
+{
+ struct lsa_hdr lsa_hdr;
+ struct imsgbuf *ibuf = bula;
+ struct nbr *nbr;
+ struct lsa_hdr *lhp;
+ struct lsa_ref *ref;
+ struct area *area;
+ struct iface *iface;
+ struct lsa_entry *le;
+ struct imsg imsg;
+ struct abr_rtr ar;
+ int n, noack = 0, shut = 0;
+ u_int16_t l, age;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ 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("ospfe_dispatch_rde: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_DD:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ /* put these on my ls_req_list for retrieval */
+ lhp = lsa_hdr_new();
+ memcpy(lhp, imsg.data, sizeof(*lhp));
+ ls_req_list_add(nbr, lhp);
+ break;
+ case IMSG_DD_END:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ nbr->dd_pending--;
+ if (nbr->dd_pending == 0 && nbr->state & NBR_STA_LOAD) {
+ if (ls_req_list_empty(nbr))
+ nbr_fsm(nbr, NBR_EVT_LOAD_DONE);
+ else
+ start_ls_req_tx_timer(nbr);
+ }
+ break;
+ case IMSG_DB_SNAPSHOT:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ /* add LSA header to the neighbor db_sum_list */
+ lhp = lsa_hdr_new();
+ memcpy(lhp, imsg.data, sizeof(*lhp));
+ db_sum_list_add(nbr, lhp);
+ break;
+ case IMSG_DB_END:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ /* snapshot done, start tx of dd packets */
+ nbr_fsm(nbr, NBR_EVT_SNAP_DONE);
+ break;
+ case IMSG_LS_FLOOD:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ l = imsg.hdr.len - IMSG_HEADER_SIZE;
+ if (l < sizeof(lsa_hdr))
+ fatalx("ospfe_dispatch_rde: "
+ "bad imsg size");
+ memcpy(&lsa_hdr, imsg.data, sizeof(lsa_hdr));
+
+ ref = lsa_cache_add(imsg.data, l);
+
+ if (lsa_hdr.type == LSA_TYPE_EXTERNAL) {
+ /*
+ * flood on all areas but stub areas and
+ * virtual links
+ */
+ LIST_FOREACH(area, &oeconf->area_list, entry) {
+ if (area->stub)
+ continue;
+ LIST_FOREACH(iface, &area->iface_list,
+ entry) {
+ noack += lsa_flood(iface, nbr,
+ &lsa_hdr, imsg.data);
+ }
+ }
+ } else {
+ /*
+ * flood on all area interfaces on
+ * area 0.0.0.0 include also virtual links.
+ */
+ area = nbr->iface->area;
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ noack += lsa_flood(iface, nbr,
+ &lsa_hdr, imsg.data);
+ }
+ /* XXX virtual links */
+ }
+
+ /* remove from ls_req_list */
+ le = ls_req_list_get(nbr, &lsa_hdr);
+ if (!(nbr->state & NBR_STA_FULL) && le != NULL) {
+ ls_req_list_free(nbr, le);
+ /*
+ * XXX no need to ack requested lsa
+ * the problem is that the RFC is very
+ * unclear about this.
+ */
+ noack = 1;
+ }
+
+ if (!noack && nbr->iface != NULL &&
+ nbr->iface->self != nbr) {
+ if (!(nbr->iface->state & IF_STA_BACKUP) ||
+ nbr->iface->dr == nbr) {
+ /* delayed ack */
+ lhp = lsa_hdr_new();
+ memcpy(lhp, &lsa_hdr, sizeof(*lhp));
+ ls_ack_list_add(nbr->iface, lhp);
+ }
+ }
+
+ lsa_cache_put(ref, nbr);
+ break;
+ case IMSG_LS_UPD:
+ /*
+ * IMSG_LS_UPD is used in three cases:
+ * 1. as response to ls requests
+ * 2. as response to ls updates where the DB
+ * is newer then the sent LSA
+ * 3. in EXSTART when the LSA has age MaxAge
+ */
+ l = imsg.hdr.len - IMSG_HEADER_SIZE;
+ if (l < sizeof(lsa_hdr))
+ fatalx("ospfe_dispatch_rde: "
+ "bad imsg size");
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ if (nbr->iface->self == nbr)
+ break;
+
+ memcpy(&age, imsg.data, sizeof(age));
+ ref = lsa_cache_add(imsg.data, l);
+ if (ntohs(age) >= MAX_AGE)
+ /* add to retransmit list */
+ ls_retrans_list_add(nbr, imsg.data, 0, 0);
+ else
+ ls_retrans_list_add(nbr, imsg.data, 0, 1);
+
+ lsa_cache_put(ref, nbr);
+ break;
+ case IMSG_LS_ACK:
+ /*
+ * IMSG_LS_ACK is used in two cases:
+ * 1. LSA was a duplicate
+ * 2. LS age is MaxAge and there is no current
+ * instance in the DB plus no neighbor in state
+ * Exchange or Loading
+ */
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ if (nbr->iface->self == nbr)
+ break;
+
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lsa_hdr))
+ fatalx("ospfe_dispatch_rde: bad imsg size");
+ memcpy(&lsa_hdr, imsg.data, sizeof(lsa_hdr));
+
+ /* for case one check for implied acks */
+ if (nbr->iface->state & IF_STA_DROTHER)
+ if (ls_retrans_list_del(nbr->iface->self,
+ &lsa_hdr) == 0)
+ break;
+ if (ls_retrans_list_del(nbr, &lsa_hdr) == 0)
+ break;
+
+ /* send a direct acknowledgement */
+ send_ls_ack(nbr->iface, nbr->addr, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ break;
+ case IMSG_LS_BADREQ:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ if (nbr->iface->self == nbr)
+ fatalx("ospfe_dispatch_rde: "
+ "dummy neighbor got BADREQ");
+
+ nbr_fsm(nbr, NBR_EVT_BAD_LS_REQ);
+ break;
+ case IMSG_ABR_UP:
+ memcpy(&ar, imsg.data, sizeof(ar));
+
+ if ((iface = find_vlink(&ar)) != NULL &&
+ iface->state == IF_STA_DOWN)
+ if (if_fsm(iface, IF_EVT_UP)) {
+ log_debug("error starting interface %s",
+ iface->name);
+ }
+ break;
+ case IMSG_ABR_DOWN:
+ memcpy(&ar, imsg.data, sizeof(ar));
+
+ if ((iface = find_vlink(&ar)) != NULL &&
+ iface->state == IF_STA_POINTTOPOINT)
+ if (if_fsm(iface, IF_EVT_DOWN)) {
+ log_debug("error stopping interface %s",
+ iface->name);
+ }
+ break;
+ case IMSG_CTL_AREA:
+ case IMSG_CTL_END:
+ case IMSG_CTL_SHOW_DATABASE:
+ case IMSG_CTL_SHOW_DB_EXT:
+ case IMSG_CTL_SHOW_DB_NET:
+ case IMSG_CTL_SHOW_DB_RTR:
+ case IMSG_CTL_SHOW_DB_SELF:
+ case IMSG_CTL_SHOW_DB_SUM:
+ case IMSG_CTL_SHOW_DB_ASBR:
+ case IMSG_CTL_SHOW_RIB:
+ case IMSG_CTL_SHOW_SUM:
+ case IMSG_CTL_SHOW_SUM_AREA:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("ospfe_dispatch_rde: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(ibuf);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ }
+}
+
+struct iface *
+find_vlink(struct abr_rtr *ar)
+{
+ struct area *area;
+ struct iface *iface = NULL;
+
+ LIST_FOREACH(area, &oeconf->area_list, entry)
+ LIST_FOREACH(iface, &area->iface_list, entry)
+ if (iface->abr_id.s_addr == ar->abr_id.s_addr &&
+ iface->type == IF_TYPE_VIRTUALLINK &&
+//XXX iface->area->id.s_addr == ar->area.s_addr) {
+ iface->area->id.s_addr == ar->area.s_addr) {
+//XXX iface->dst.s_addr = ar->dst_ip.s_addr;
+ iface->dst = ar->dst_ip;
+//XXX iface->addr.s_addr = ar->addr.s_addr;
+ iface->addr = ar->addr;
+ iface->metric = ar->metric;
+
+ return (iface);
+ }
+
+ return (iface);
+}
+
+void
+orig_rtr_lsa_all(struct area *area)
+{
+ struct area *a;
+
+ /*
+ * update all router LSA in all areas except area itself,
+ * as this update is already running.
+ */
+ LIST_FOREACH(a, &oeconf->area_list, entry)
+ if (a != area)
+ orig_rtr_lsa(a);
+}
+
+void
+orig_rtr_lsa(struct area *area)
+{
+ struct lsa_hdr lsa_hdr;
+ struct lsa_rtr lsa_rtr;
+ struct lsa_rtr_link rtr_link;
+ struct iface *iface;
+ struct buf *buf;
+ struct nbr *nbr, *self = NULL;
+ u_int16_t num_links = 0;
+ u_int16_t chksum;
+ u_int8_t border, virtual = 0;
+
+ log_debug("orig_rtr_lsa: area %s", inet_ntoa(area->id));
+
+ /* XXX READ_BUF_SIZE */
+ if ((buf = buf_dynamic(sizeof(lsa_hdr), READ_BUF_SIZE)) == NULL)
+ fatal("orig_rtr_lsa");
+
+ /* reserve space for LSA header and LSA Router header */
+ if (buf_reserve(buf, sizeof(lsa_hdr)) == NULL)
+ fatal("orig_rtr_lsa: buf_reserve failed");
+
+ if (buf_reserve(buf, sizeof(lsa_rtr)) == NULL)
+ fatal("orig_rtr_lsa: buf_reserve failed");
+
+ /* links */
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if (self == NULL && iface->self != NULL)
+ self = iface->self;
+
+ bzero(&rtr_link, sizeof(rtr_link));
+
+ if (iface->state & IF_STA_LOOPBACK) {
+//XXX rtr_link.id = iface->addr.s_addr;
+ rtr_link.data = 0xffffffff;
+ rtr_link.type = LINK_TYPE_STUB_NET;
+ rtr_link.metric = htons(iface->metric);
+ num_links++;
+ if (buf_add(buf, &rtr_link, sizeof(rtr_link)))
+ fatalx("orig_rtr_lsa: buf_add failed");
+ continue;
+ }
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ LIST_FOREACH(nbr, &iface->nbr_list, entry)
+ if (nbr != iface->self &&
+ nbr->state & NBR_STA_FULL)
+ break;
+ if (nbr) {
+ log_debug("orig_rtr_lsa: point-to-point, "
+ "interface %s", iface->name);
+ rtr_link.id = nbr->id.s_addr;
+//XXX rtr_link.data = iface->addr.s_addr;
+ rtr_link.type = LINK_TYPE_POINTTOPOINT;
+ /* RFC 3137: stub router support */
+ if (oeconf->flags & OSPFD_FLAG_STUB_ROUTER ||
+ oe_nofib)
+ rtr_link.metric = 0xffff;
+ else
+ rtr_link.metric = htons(iface->metric);
+ num_links++;
+ if (buf_add(buf, &rtr_link, sizeof(rtr_link)))
+ fatalx("orig_rtr_lsa: buf_add failed");
+ }
+ if (iface->state & IF_STA_POINTTOPOINT) {
+ log_debug("orig_rtr_lsa: stub net, "
+ "interface %s", iface->name);
+ bzero(&rtr_link, sizeof(rtr_link));
+ if (nbr) {
+//XXX rtr_link.id = nbr->addr.s_addr;
+ rtr_link.data = 0xffffffff;
+ } else {
+//XXX rtr_link.id = iface->addr.s_addr;
+//XXX rtr_link.data = iface->mask.s_addr;
+ }
+ rtr_link.type = LINK_TYPE_STUB_NET;
+ rtr_link.metric = htons(iface->metric);
+ num_links++;
+ if (buf_add(buf, &rtr_link, sizeof(rtr_link)))
+ fatalx("orig_rtr_lsa: buf_add failed");
+ }
+ continue;
+ case IF_TYPE_BROADCAST:
+ case IF_TYPE_NBMA:
+ if ((iface->state & IF_STA_MULTI)) {
+ if (iface->dr == iface->self) {
+ LIST_FOREACH(nbr, &iface->nbr_list,
+ entry)
+ if (nbr != iface->self &&
+ nbr->state & NBR_STA_FULL)
+ break;
+ } else
+ nbr = iface->dr;
+
+ if (nbr && nbr->state & NBR_STA_FULL) {
+ log_debug("orig_rtr_lsa: transit net, "
+ "interface %s", iface->name);
+
+//XXX rtr_link.id = iface->dr->addr.s_addr;
+//XXX rtr_link.data = iface->addr.s_addr;
+ rtr_link.type = LINK_TYPE_TRANSIT_NET;
+ break;
+ }
+ }
+
+ if ((iface->flags & IFF_UP) == 0 ||
+ iface->linkstate == LINK_STATE_DOWN ||
+ (!LINK_STATE_IS_UP(iface->linkstate) &&
+ iface->media_type == IFT_CARP))
+ continue;
+
+ log_debug("orig_rtr_lsa: stub net, "
+ "interface %s", iface->name);
+
+/*XXX rtr_link.id =
+ iface->addr.s_addr & iface->mask.s_addr;
+ rtr_link.data = iface->mask.s_addr;
+XXX*/
+ rtr_link.type = LINK_TYPE_STUB_NET;
+ break;
+ case IF_TYPE_VIRTUALLINK:
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr != iface->self &&
+ nbr->state & NBR_STA_FULL)
+ break;
+ }
+ if (nbr) {
+ rtr_link.id = nbr->id.s_addr;
+//XXX rtr_link.data = iface->addr.s_addr;
+ rtr_link.type = LINK_TYPE_VIRTUAL;
+ /* RFC 3137: stub router support */
+ if (oeconf->flags & OSPFD_FLAG_STUB_ROUTER ||
+ oe_nofib)
+ rtr_link.metric = 0xffff;
+ else
+ rtr_link.metric = htons(iface->metric);
+ num_links++;
+ virtual = 1;
+ if (buf_add(buf, &rtr_link, sizeof(rtr_link)))
+ fatalx("orig_rtr_lsa: buf_add failed");
+
+ log_debug("orig_rtr_lsa: virtual link, "
+ "interface %s", iface->name);
+ }
+ continue;
+ case IF_TYPE_POINTOMULTIPOINT:
+ log_debug("orig_rtr_lsa: stub net, "
+ "interface %s", iface->name);
+//XXX rtr_link.id = iface->addr.s_addr;
+ rtr_link.data = 0xffffffff;
+ rtr_link.type = LINK_TYPE_STUB_NET;
+ rtr_link.metric = htons(iface->metric);
+ num_links++;
+ if (buf_add(buf, &rtr_link, sizeof(rtr_link)))
+ fatalx("orig_rtr_lsa: buf_add failed");
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr != iface->self &&
+ nbr->state & NBR_STA_FULL) {
+ bzero(&rtr_link, sizeof(rtr_link));
+ log_debug("orig_rtr_lsa: "
+ "point-to-multipoint, interface %s",
+ iface->name);
+//XXX rtr_link.id = nbr->addr.s_addr;
+//XXX rtr_link.data = iface->addr.s_addr;
+ rtr_link.type = LINK_TYPE_POINTTOPOINT;
+ /* RFC 3137: stub router support */
+ if (oe_nofib || oeconf->flags &
+ OSPFD_FLAG_STUB_ROUTER)
+ rtr_link.metric = 0xffff;
+ else
+ rtr_link.metric =
+ htons(iface->metric);
+ num_links++;
+ if (buf_add(buf, &rtr_link,
+ sizeof(rtr_link)))
+ fatalx("orig_rtr_lsa: "
+ "buf_add failed");
+ }
+ }
+ continue;
+ default:
+ fatalx("orig_rtr_lsa: unknown interface type");
+ }
+
+ rtr_link.num_tos = 0;
+ /* RFC 3137: stub router support */
+ if ((oeconf->flags & OSPFD_FLAG_STUB_ROUTER || oe_nofib) &&
+ rtr_link.type != LINK_TYPE_STUB_NET)
+ rtr_link.metric = 0xffff;
+ else
+ rtr_link.metric = htons(iface->metric);
+ num_links++;
+ if (buf_add(buf, &rtr_link, sizeof(rtr_link)))
+ fatalx("orig_rtr_lsa: buf_add failed");
+ }
+
+ /* LSA router header */
+ lsa_rtr.flags = 0;
+ /*
+ * Set the E bit as soon as an as-ext lsa may be redistributed, only
+ * setting it in case we redistribute something is not worth the fuss.
+ */
+ if (oeconf->redistribute && (oeconf->options & OSPF_OPTION_E))
+ lsa_rtr.flags |= OSPF_RTR_E;
+
+ border = (area_border_router(oeconf) != 0);
+ if (border != oeconf->border) {
+ oeconf->border = border;
+ orig_rtr_lsa_all(area);
+ }
+
+ if (oeconf->border)
+ lsa_rtr.flags |= OSPF_RTR_B;
+ /* TODO set V flag if a active virtual link ends here and the
+ * area is the tranist area for this link. */
+ if (virtual)
+ lsa_rtr.flags |= OSPF_RTR_V;
+
+ lsa_rtr.dummy = 0;
+ lsa_rtr.nlinks = htons(num_links);
+ memcpy(buf_seek(buf, sizeof(lsa_hdr), sizeof(lsa_rtr)),
+ &lsa_rtr, sizeof(lsa_rtr));
+
+ /* LSA header */
+ lsa_hdr.age = htons(DEFAULT_AGE);
+ lsa_hdr.type = LSA_TYPE_ROUTER;
+ lsa_hdr.ls_id = oeconf->rtr_id.s_addr;
+ lsa_hdr.adv_rtr = oeconf->rtr_id.s_addr;
+ lsa_hdr.seq_num = htonl(INIT_SEQ_NUM);
+ lsa_hdr.len = htons(buf->wpos);
+ lsa_hdr.ls_chksum = 0; /* updated later */
+ memcpy(buf_seek(buf, 0, sizeof(lsa_hdr)), &lsa_hdr, sizeof(lsa_hdr));
+
+ chksum = htons(iso_cksum(buf->buf, buf->wpos, LS_CKSUM_OFFSET));
+ memcpy(buf_seek(buf, LS_CKSUM_OFFSET, sizeof(chksum)),
+ &chksum, sizeof(chksum));
+
+ if (self)
+ imsg_compose(ibuf_rde, IMSG_LS_UPD, self->peerid, 0,
+ buf->buf, buf->wpos);
+ else
+ log_warnx("orig_rtr_lsa: empty area %s",
+ inet_ntoa(area->id));
+
+ buf_free(buf);
+}
+
+void
+orig_net_lsa(struct iface *iface)
+{
+ struct lsa_hdr lsa_hdr;
+ struct nbr *nbr;
+ struct buf *buf;
+ int num_rtr = 0;
+ u_int16_t chksum;
+
+ /* XXX READ_BUF_SIZE */
+ if ((buf = buf_dynamic(sizeof(lsa_hdr), READ_BUF_SIZE)) == NULL)
+ fatal("orig_net_lsa");
+
+ /* reserve space for LSA header and LSA Router header */
+ if (buf_reserve(buf, sizeof(lsa_hdr)) == NULL)
+ fatal("orig_net_lsa: buf_reserve failed");
+
+ /* LSA net mask and then all fully adjacent routers */
+ if (buf_add(buf, &iface->mask, sizeof(iface->mask)))
+ fatal("orig_net_lsa: buf_add failed");
+
+ /* fully adjacent neighbors + self */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry)
+ if (nbr->state & NBR_STA_FULL) {
+ if (buf_add(buf, &nbr->id, sizeof(nbr->id)))
+ fatal("orig_net_lsa: buf_add failed");
+ num_rtr++;
+ }
+
+ if (num_rtr == 1) {
+ /* non transit net therefor no need to generate a net lsa */
+ buf_free(buf);
+ return;
+ }
+
+ /* LSA header */
+ if (iface->state & IF_STA_DR)
+ lsa_hdr.age = htons(DEFAULT_AGE);
+ else
+ lsa_hdr.age = htons(MAX_AGE);
+
+ lsa_hdr.type = LSA_TYPE_NETWORK;
+//XXX lsa_hdr.ls_id = iface->addr.s_addr;
+ lsa_hdr.adv_rtr = oeconf->rtr_id.s_addr;
+ lsa_hdr.seq_num = htonl(INIT_SEQ_NUM);
+ lsa_hdr.len = htons(buf->wpos);
+ lsa_hdr.ls_chksum = 0; /* updated later */
+ memcpy(buf_seek(buf, 0, sizeof(lsa_hdr)), &lsa_hdr, sizeof(lsa_hdr));
+
+ chksum = htons(iso_cksum(buf->buf, buf->wpos, LS_CKSUM_OFFSET));
+ memcpy(buf_seek(buf, LS_CKSUM_OFFSET, sizeof(chksum)),
+ &chksum, sizeof(chksum));
+
+ imsg_compose(ibuf_rde, IMSG_LS_UPD, iface->self->peerid, 0,
+ buf->buf, buf->wpos);
+
+ buf_free(buf);
+}
+
+u_int32_t
+ospfe_router_id(void)
+{
+ return (oeconf->rtr_id.s_addr);
+}
+
+void
+ospfe_fip_update(int type)
+{
+ int old = oe_nofib;
+
+ if (type == IMSG_CTL_FIB_COUPLE)
+ oe_nofib = 0;
+ if (type == IMSG_CTL_FIB_DECOUPLE)
+ oe_nofib = 1;
+ if (old != oe_nofib)
+ orig_rtr_lsa_all(NULL);
+}
+
+void
+ospfe_iface_ctl(struct ctl_conn *c, unsigned int idx)
+{
+ struct area *area;
+ struct iface *iface;
+ struct ctl_iface *ictl;
+
+ LIST_FOREACH(area, &oeconf->area_list, entry)
+ LIST_FOREACH(iface, &area->iface_list, entry)
+ if (idx == 0 || idx == iface->ifindex) {
+ ictl = if_to_ctl(iface);
+ imsg_compose(&c->ibuf, IMSG_CTL_SHOW_INTERFACE,
+ 0, 0, ictl, sizeof(struct ctl_iface));
+ }
+}
+
+void
+ospfe_nbr_ctl(struct ctl_conn *c)
+{
+ struct area *area;
+ struct iface *iface;
+ struct nbr *nbr;
+ struct ctl_nbr *nctl;
+
+ LIST_FOREACH(area, &oeconf->area_list, entry)
+ LIST_FOREACH(iface, &area->iface_list, entry)
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (iface->self != nbr) {
+ 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);
+}
+
+void
+ospfe_demote_area(struct area *area, int active)
+{
+ struct demote_msg dmsg;
+
+ if (ospfd_process != PROC_OSPF_ENGINE ||
+ area->demote_group[0] == '\0')
+ return;
+
+ bzero(&dmsg, sizeof(dmsg));
+ strlcpy(dmsg.demote_group, area->demote_group,
+ sizeof(dmsg.demote_group));
+ dmsg.level = area->demote_level;
+ if (active)
+ dmsg.level = -dmsg.level;
+
+ ospfe_imsg_compose_parent(IMSG_DEMOTE, 0, &dmsg, sizeof(dmsg));
+}
+
+void
+ospfe_demote_iface(struct iface *iface, int active)
+{
+ struct demote_msg dmsg;
+
+ if (ospfd_process != PROC_OSPF_ENGINE ||
+ iface->demote_group[0] == '\0')
+ return;
+
+ bzero(&dmsg, sizeof(dmsg));
+ strlcpy(dmsg.demote_group, iface->demote_group,
+ sizeof(dmsg.demote_group));
+ if (active)
+ dmsg.level = -1;
+ else
+ dmsg.level = 1;
+
+ ospfe_imsg_compose_parent(IMSG_DEMOTE, 0, &dmsg, sizeof(dmsg));
+}
diff --git a/usr.sbin/ospf6d/ospfe.h b/usr.sbin/ospf6d/ospfe.h
new file mode 100644
index 00000000000..ccbe153ae4e
--- /dev/null
+++ b/usr.sbin/ospf6d/ospfe.h
@@ -0,0 +1,233 @@
+/* $OpenBSD: ospfe.h,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * 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 _OSPFE_H_
+#define _OSPFE_H_
+
+#define max(x,y) ((x) > (y) ? (x) : (y))
+
+#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;
+
+struct lsa_entry {
+ TAILQ_ENTRY(lsa_entry) entry;
+ union {
+ struct lsa_hdr *lu_lsa;
+ struct lsa_ref *lu_ref;
+ } le_data;
+ unsigned short le_when;
+ unsigned short le_oneshot;
+};
+#define le_lsa le_data.lu_lsa
+#define le_ref le_data.lu_ref
+
+struct lsa_ref {
+ LIST_ENTRY(lsa_ref) entry;
+ struct lsa_hdr hdr;
+ void *data;
+ time_t stamp;
+ int refcnt;
+ u_int16_t len;
+};
+
+struct nbr_stats {
+ u_int32_t sta_chng;
+};
+
+struct nbr {
+ LIST_ENTRY(nbr) entry, hash;
+ struct event inactivity_timer;
+ struct event db_tx_timer;
+ struct event lsreq_tx_timer;
+ struct event ls_retrans_timer;
+ struct event adj_timer;
+
+ struct nbr_stats stats;
+
+ struct lsa_head ls_retrans_list;
+ struct lsa_head db_sum_list;
+ struct lsa_head ls_req_list;
+
+ struct in6_addr addr; /* ip6 address */
+ struct in_addr id; /* router id */
+ struct in_addr dr; /* designated router */
+ struct in_addr bdr; /* backup designated router */
+
+ struct iface *iface;
+ struct lsa_entry *ls_req;
+ struct lsa_entry *dd_end;
+
+ u_int32_t dd_seq_num;
+ u_int32_t dd_pending;
+ u_int32_t peerid; /* unique ID in DB */
+ u_int32_t ls_req_cnt;
+ u_int32_t ls_ret_cnt;
+ u_int32_t crypt_seq_num;
+
+ time_t uptime;
+ int state;
+ u_int8_t priority;
+ u_int8_t options;
+ u_int8_t last_rx_options;
+ u_int8_t last_rx_bits;
+ u_int8_t master;
+};
+
+/* database.c */
+int send_db_description(struct nbr *);
+void recv_db_description(struct nbr *, char *, u_int16_t);
+void db_sum_list_add(struct nbr *, struct lsa_hdr *);
+int db_sum_list_del(struct nbr *, struct lsa_hdr *);
+void db_sum_list_clr(struct nbr *);
+void db_tx_timer(int, short, void *);
+void start_db_tx_timer(struct nbr *);
+void stop_db_tx_timer(struct nbr *);
+
+/* hello.c */
+int send_hello(struct iface *);
+void recv_hello(struct iface *, struct in6_addr, u_int32_t,
+ char *, u_int16_t);
+
+/* ospfe.c */
+pid_t ospfe(struct ospfd_conf *, int[2], int[2], int[2]);
+void ospfe_dispatch_main(int, short, void *);
+void ospfe_dispatch_rde(int, short, void *);
+int ospfe_imsg_compose_parent(int, pid_t, void *, u_int16_t);
+int ospfe_imsg_compose_rde(int, u_int32_t, pid_t, void *,
+ u_int16_t);
+u_int32_t ospfe_router_id(void);
+void ospfe_fip_update(int);
+void ospfe_iface_ctl(struct ctl_conn *, unsigned int);
+void ospfe_nbr_ctl(struct ctl_conn *);
+void orig_rtr_lsa(struct area *);
+void orig_net_lsa(struct iface *);
+void ospfe_demote_area(struct area *, int);
+void ospfe_demote_iface(struct iface *, int);
+
+/* interface.c */
+int if_fsm(struct iface *, enum iface_event);
+
+struct iface *if_new(struct kif *, struct kif_addr *);
+void if_del(struct iface *);
+void if_init(struct ospfd_conf *, struct iface *);
+
+int if_act_start(struct iface *);
+int if_act_elect(struct iface *);
+int if_act_reset(struct iface *);
+
+struct ctl_iface *if_to_ctl(struct iface *);
+
+int if_join_group(struct iface *, struct in6_addr *);
+int if_leave_group(struct iface *, struct in6_addr *);
+int if_set_mcast(struct iface *);
+int if_set_recvif(int, int);
+void if_set_recvbuf(int);
+int if_set_mcast_loop(int);
+int if_set_ip_hdrincl(int);
+
+/* lsack.c */
+int delay_lsa_ack(struct iface *, struct lsa_hdr *);
+int send_ls_ack(struct iface *, struct in6_addr, void *, size_t);
+void recv_ls_ack(struct nbr *, char *, u_int16_t);
+int lsa_hdr_check(struct nbr *, struct lsa_hdr *);
+void ls_ack_list_add(struct iface *, struct lsa_hdr *);
+void ls_ack_list_free(struct iface *, struct lsa_entry *);
+void ls_ack_list_clr(struct iface *);
+int ls_ack_list_empty(struct iface *);
+void ls_ack_tx_timer(int, short, void *);
+void start_ls_ack_tx_timer(struct iface *);
+void stop_ls_ack_tx_timer(struct iface *);
+
+/* lsreq.c */
+int send_ls_req(struct nbr *);
+void recv_ls_req(struct nbr *, char *, u_int16_t);
+void ls_req_list_add(struct nbr *, struct lsa_hdr *);
+int ls_req_list_del(struct nbr *, struct lsa_hdr *);
+struct lsa_entry *ls_req_list_get(struct nbr *, struct lsa_hdr *);
+void ls_req_list_free(struct nbr *, struct lsa_entry *);
+void ls_req_list_clr(struct nbr *);
+int ls_req_list_empty(struct nbr *);
+void ls_req_tx_timer(int, short, void *);
+void start_ls_req_tx_timer(struct nbr *);
+void stop_ls_req_tx_timer(struct nbr *);
+
+/* lsupdate.c */
+int lsa_flood(struct iface *, struct nbr *, struct lsa_hdr *,
+ void *);
+void recv_ls_update(struct nbr *, char *, u_int16_t);
+
+void ls_retrans_list_add(struct nbr *, struct lsa_hdr *,
+ unsigned short, unsigned short);
+int ls_retrans_list_del(struct nbr *, struct lsa_hdr *);
+struct lsa_entry *ls_retrans_list_get(struct nbr *, struct lsa_hdr *);
+void ls_retrans_list_free(struct nbr *, struct lsa_entry *);
+void ls_retrans_list_clr(struct nbr *);
+void ls_retrans_timer(int, short, void *);
+
+void lsa_cache_init(u_int32_t);
+struct lsa_ref *lsa_cache_add(void *, u_int16_t);
+struct lsa_ref *lsa_cache_get(struct lsa_hdr *);
+void lsa_cache_put(struct lsa_ref *, struct nbr *);
+
+/* neighbor.c */
+void nbr_init(u_int32_t);
+struct nbr *nbr_new(u_int32_t, struct iface *, int);
+void nbr_del(struct nbr *);
+
+struct nbr *nbr_find_id(struct iface *, u_int32_t);
+struct nbr *nbr_find_peerid(u_int32_t);
+
+int nbr_fsm(struct nbr *, enum nbr_event);
+
+void nbr_itimer(int, short, void *);
+void nbr_start_itimer(struct nbr *);
+void nbr_stop_itimer(struct nbr *);
+void nbr_reset_itimer(struct nbr *);
+
+void nbr_adj_timer(int, short, void *);
+void nbr_start_adj_timer(struct nbr *);
+
+int nbr_act_reset_itimer(struct nbr *);
+int nbr_act_start_itimer(struct nbr *);
+int nbr_act_eval(struct nbr *);
+int nbr_act_snapshot(struct nbr *);
+int nbr_act_exchange_done(struct nbr *);
+int nbr_act_adj_ok(struct nbr *);
+int nbr_act_restart_dd(struct nbr *);
+int nbr_act_delete(struct nbr *);
+int nbr_act_clear_lists(struct nbr *);
+int nbr_act_hello_check(struct nbr *);
+
+struct ctl_nbr *nbr_to_ctl(struct nbr *);
+
+struct lsa_hdr *lsa_hdr_new(void);
+
+/* packet.c */
+int gen_ospf_hdr(struct buf *, struct iface *, u_int8_t);
+int upd_ospf_hdr(struct buf *, struct iface *);
+int send_packet(struct iface *, void *, size_t, struct sockaddr_in6 *);
+void recv_packet(int, short, void *);
+
+char *pkt_ptr; /* packet buffer */
+
+#endif /* _OSPFE_H_ */
diff --git a/usr.sbin/ospf6d/packet.c b/usr.sbin/ospf6d/packet.c
new file mode 100644
index 00000000000..2e833c48c27
--- /dev/null
+++ b/usr.sbin/ospf6d/packet.c
@@ -0,0 +1,361 @@
+/* $OpenBSD: packet.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * 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/ip6.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+
+#include <errno.h>
+#include <event.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ospf6d.h"
+#include "ospf6.h"
+#include "log.h"
+#include "ospfe.h"
+
+int ip_hdr_sanity_check(const struct ip6_hdr *, u_int16_t);
+int ospf_hdr_sanity_check(const struct ip6_hdr *,
+ struct ospf_hdr *, u_int16_t, const struct iface *);
+struct iface *find_iface(struct ospfd_conf *, unsigned int, struct in6_addr);
+
+int
+gen_ospf_hdr(struct buf *buf, struct iface *iface, u_int8_t type)
+{
+ struct ospf_hdr ospf_hdr;
+
+ bzero(&ospf_hdr, sizeof(ospf_hdr));
+ ospf_hdr.version = OSPF6_VERSION;
+ ospf_hdr.type = type;
+ ospf_hdr.rtr_id = ospfe_router_id();
+ if (iface->type != IF_TYPE_VIRTUALLINK)
+ ospf_hdr.area_id = iface->area->id.s_addr;
+ ospf_hdr.instance = DEFAULT_INSTANCE_ID;
+ ospf_hdr.zero = 0; /* must be zero */
+
+ return (buf_add(buf, &ospf_hdr, sizeof(ospf_hdr)));
+}
+
+int
+upd_ospf_hdr(struct buf *buf, struct iface *iface)
+{
+ struct ospf_hdr *ospf_hdr;
+
+ if ((ospf_hdr = buf_seek(buf, 0, sizeof(ospf_hdr))) == NULL)
+ fatalx("upd_ospf_hdr: buf_seek failed");
+
+ /* update length */
+ if (buf->wpos > USHRT_MAX)
+ fatalx("upd_ospf_hdr: resulting ospf packet too big");
+ ospf_hdr->len = htons((u_int16_t)buf->wpos);
+
+ ospf_hdr->chksum = in_cksum(buf->buf, buf->wpos); /* XXX */
+
+ return (0);
+}
+
+/* send and receive packets */
+int
+send_packet(struct iface *iface, void *pkt, size_t len,
+ struct sockaddr_in6 *dst)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ /* setup buffer */
+ bzero(&msg, sizeof(msg));
+ iov[0].iov_base = pkt;
+ iov[0].iov_len = len;
+ msg.msg_name = dst;
+ msg.msg_namelen = sizeof(*dst);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ /* set outgoing interface for multicast traffic */
+ if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
+ if (if_set_mcast(iface) == -1) {
+ log_warn("send_packet: error setting multicast "
+ "interface, %s", iface->name);
+ return (-1);
+ }
+
+
+ log_debug("send_packet: iface %d addr %s", iface->ifindex,
+ log_in6addr(&iface->addr));
+ log_debug("send_packet: dest %s", log_in6addr(&dst->sin6_addr));
+ if (sendmsg(iface->fd, &msg, MSG_DONTROUTE) == -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 msghdr msg;
+ struct iovec iov;
+ struct ip6_hdr ip_hdr;
+ struct in6_addr addr;
+ struct ospfd_conf *xconf = bula;
+ struct ospf_hdr *ospf_hdr;
+ struct iface *iface;
+ struct nbr *nbr = NULL;
+ char *buf;
+ struct cmsghdr *cmsg;
+ ssize_t r;
+ u_int16_t len;
+ int l;
+ unsigned int ifindex = 0;
+
+ if (event != EV_READ)
+ return;
+
+ /* setup buffer */
+ bzero(&msg, sizeof(msg));
+ iov.iov_base = buf = pkt_ptr;
+ iov.iov_len = READ_BUF_SIZE;
+ 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 != EAGAIN && errno != EINTR)
+ 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_IPV6 &&
+ cmsg->cmsg_type == IP_RECVIF) {
+ ifindex = ((struct sockaddr_dl *)
+ CMSG_DATA(cmsg))->sdl_index;
+ break;
+ }
+ }
+
+ len = (u_int16_t)r;
+
+ /* IP header sanity checks */
+ if (len < sizeof(ip_hdr)) {
+ log_warnx("recv_packet: bad packet size");
+ return;
+ }
+ memcpy(&ip_hdr, buf, sizeof(ip_hdr));
+ if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1)
+ return;
+ buf += l;
+ len -= l;
+
+ /* find a matching interface */
+ if ((iface = find_iface(xconf, ifindex, ip_hdr.ip6_src)) == NULL) {
+ /* XXX add a counter here */
+ return;
+ }
+#if 1
+ /*
+ * Packet needs to be sent to AllSPFRouters or AllDRouters
+ * or to the address of the interface itself.
+ * AllDRouters is only valid for DR and BDR but this is checked later.
+ */
+ inet_pton(AF_INET6, AllSPFRouters, &addr);
+
+ if (!IN6_ARE_ADDR_EQUAL(&ip_hdr.ip6_dst, &addr)) {
+ inet_pton(AF_INET6, AllDRouters, &addr);
+ if (!IN6_ARE_ADDR_EQUAL(&ip_hdr.ip6_dst, &addr)) {
+ if (!IN6_ARE_ADDR_EQUAL(&ip_hdr.ip6_dst,
+ &iface->addr)) {
+ log_debug("recv_packet: packet sent to wrong "
+ "address %s, interface %s",
+ log_in6addr(&ip_hdr.ip6_dst), iface->name);
+ return;
+ }
+ }
+ }
+#endif
+ /* OSPF header sanity checks */
+ if (len < sizeof(*ospf_hdr)) {
+ log_debug("recv_packet: bad packet size");
+ return;
+ }
+ ospf_hdr = (struct ospf_hdr *)buf;
+
+ if ((l = ospf_hdr_sanity_check(&ip_hdr, ospf_hdr, len, iface)) == -1)
+ return;
+
+ nbr = nbr_find_id(iface, ospf_hdr->rtr_id);
+ if (ospf_hdr->type != PACKET_TYPE_HELLO && nbr == NULL) {
+ log_debug("recv_packet: unknown neighbor ID");
+ return;
+ }
+
+ if (in_cksum(buf, len)) {
+ log_warnx("recv_packet: invalid checksum, "
+ "interface %s", iface->name);
+ return;
+ }
+
+ buf += sizeof(*ospf_hdr);
+ len = l - sizeof(*ospf_hdr);
+
+ /* switch OSPF packet type */
+ switch (ospf_hdr->type) {
+ case PACKET_TYPE_HELLO:
+ inet_pton(AF_INET6, AllDRouters, &addr);
+ if (IN6_ARE_ADDR_EQUAL(&ip_hdr.ip6_dst, &addr)) {
+ log_debug("recv_packet: invalid destination IP "
+ "address");
+ break;
+ }
+
+ recv_hello(iface, ip_hdr.ip6_src, ospf_hdr->rtr_id, buf, len);
+ break;
+ case PACKET_TYPE_DD:
+ recv_db_description(nbr, buf, len);
+ break;
+ case PACKET_TYPE_LS_REQUEST:
+ recv_ls_req(nbr, buf, len);
+ break;
+ case PACKET_TYPE_LS_UPDATE:
+ recv_ls_update(nbr, buf, len);
+ break;
+ case PACKET_TYPE_LS_ACK:
+ recv_ls_ack(nbr, buf, len);
+ break;
+ default:
+ log_debug("recv_packet: unknown OSPF packet type, interface %s",
+ iface->name);
+ }
+}
+
+int
+ip_hdr_sanity_check(const struct ip6_hdr *ip_hdr, u_int16_t len)
+{
+ if (ntohs(ip_hdr->ip6_plen) != len) {
+ log_debug("recv_packet: invalid IP packet length %u",
+ ntohs(ip_hdr->ip6_plen));
+ return (-1);
+ }
+
+ if (ip_hdr->ip6_nxt != IPPROTO_OSPF)
+ /* this is enforced by the socket itself */
+ fatalx("recv_packet: invalid IP proto");
+
+//XXX return (ip_hdr->ip_hl << 2);
+ return (sizeof(struct ip6_hdr));
+}
+
+int
+ospf_hdr_sanity_check(const struct ip6_hdr *ip_hdr, struct ospf_hdr *ospf_hdr,
+ u_int16_t len, const struct iface *iface)
+{
+ struct in6_addr addr;
+ struct in_addr id;
+
+ if (ospf_hdr->version != OSPF6_VERSION) {
+ log_debug("recv_packet: invalid OSPF version %d",
+ ospf_hdr->version);
+ return (-1);
+ }
+
+ if (ntohs(ospf_hdr->len) > len ||
+ len <= sizeof(struct ospf_hdr)) {
+ log_debug("recv_packet: invalid OSPF packet length %d",
+ ntohs(ospf_hdr->len));
+ return (-1);
+ }
+
+ if (iface->type != IF_TYPE_VIRTUALLINK) {
+ if (ospf_hdr->area_id != iface->area->id.s_addr) {
+ id.s_addr = ospf_hdr->area_id;
+ log_debug("recv_packet: invalid area ID %s, "
+ "interface %s", inet_ntoa(id), iface->name);
+ return (-1);
+ }
+ } else {
+ if (ospf_hdr->area_id != 0) {
+ id.s_addr = ospf_hdr->area_id;
+ log_debug("recv_packet: invalid area ID %s, "
+ "interface %s", inet_ntoa(id), iface->name);
+ return (-1);
+ }
+ }
+
+ if (iface->type == IF_TYPE_BROADCAST || iface->type == IF_TYPE_NBMA) {
+ if (inet_pton(AF_INET6, AllDRouters, &addr) == 0)
+ fatalx("recv_packet: inet_pton");
+ if (IN6_ARE_ADDR_EQUAL(&ip_hdr->ip6_dst, &addr) &&
+ (iface->state & IF_STA_DRORBDR) == 0) {
+ log_debug("recv_packet: invalid destination IP in "
+ "state %s, interface %s",
+ if_state_name(iface->state), iface->name);
+ return (-1);
+ }
+ }
+
+ return (ntohs(ospf_hdr->len));
+}
+
+struct iface *
+find_iface(struct ospfd_conf *xconf, unsigned int ifindex, struct in6_addr src)
+{
+ struct area *area = NULL;
+ struct iface *iface = NULL;
+
+ /* returned interface needs to be active */
+ LIST_FOREACH(area, &xconf->area_list, entry) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ switch (iface->type) {
+ case IF_TYPE_VIRTUALLINK:
+ if (IN6_ARE_ADDR_EQUAL(&src, &iface->dst) &&
+ !iface->passive)
+ return (iface);
+ break;
+ case IF_TYPE_POINTOPOINT:
+ if (ifindex == iface->ifindex &&
+ IN6_ARE_ADDR_EQUAL(&iface->dst, &src) &&
+ !iface->passive)
+ return (iface);
+ break;
+ default:
+#if 0
+ if (ifindex == iface->ifindex &&
+ (iface->addr.s_addr & iface->mask.s_addr) ==
+ (src.s_addr & iface->mask.s_addr) &&
+ !iface->passive)
+ return (iface);
+#endif
+ break;
+ }
+ }
+ }
+
+ return (NULL);
+}
diff --git a/usr.sbin/ospf6d/parse.y b/usr.sbin/ospf6d/parse.y
new file mode 100644
index 00000000000..60f8d8ffb37
--- /dev/null
+++ b/usr.sbin/ospf6d/parse.y
@@ -0,0 +1,1061 @@
+/* $OpenBSD: parse.y,v 1.1 2007/10/08 10:44:51 norby Exp $ */
+
+/*
+ * 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 "ospf6.h"
+#include "ospf6d.h"
+#include "ospfe.h"
+#include "log.h"
+
+static struct ospfd_conf *conf;
+static FILE *fin = NULL;
+static int lineno = 1;
+static int errors = 0;
+char *infile;
+
+struct area *area = NULL;
+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 ospfd_conf *xconf);
+int check_file_secrecy(int fd, const char *fname);
+u_int32_t get_rtr_id(void);
+int host(const char *, struct in_addr *, struct in_addr *);
+
+struct config_defaults {
+ u_int32_t dead_interval;
+ u_int16_t transmit_delay;
+ u_int16_t hello_interval;
+ u_int16_t rxmt_interval;
+ u_int16_t metric;
+ u_int8_t priority;
+};
+
+struct config_defaults globaldefs;
+struct config_defaults areadefs;
+struct config_defaults ifacedefs;
+struct config_defaults *defs;
+
+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 *);
+struct area *conf_get_area(struct in_addr);
+struct iface *conf_get_if(struct kif *, struct kif_addr *);
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token AREA INTERFACE ROUTERID FIBUPDATE REDISTRIBUTE RTLABEL
+%token STUB ROUTER SPFDELAY SPFHOLDTIME EXTTAG
+%token METRIC PASSIVE
+%token HELLOINTERVAL TRANSMITDELAY
+%token RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY
+%token SET TYPE
+%token YES NO
+%token DEMOTE
+%token ERROR
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.number> yesno no optlist, optlist_l option demotecount
+%type <v.string> string
+
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar conf_main '\n'
+ | grammar varset '\n'
+ | grammar area '\n'
+ | grammar error '\n' { errors++; }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+ free($1);
+ free($2);
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+yesno : YES { $$ = 1; }
+ | NO { $$ = 0; }
+ ;
+
+no : /* empty */ { $$ = 0; }
+ | NO { $$ = 1; }
+
+varset : STRING '=' string {
+ if (conf->opts & OSPFD_OPT_VERBOSE)
+ printf("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+conf_main : ROUTERID STRING {
+ if (!inet_aton($2, &conf->rtr_id)) {
+ yyerror("error parsing router-id");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | FIBUPDATE yesno {
+ if ($2 == 0)
+ conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE;
+ else
+ conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE;
+ }
+ | no REDISTRIBUTE NUMBER '/' NUMBER optlist {
+ struct redistribute *r;
+
+ if ((r = calloc(1, sizeof(*r))) == NULL)
+ fatal(NULL);
+ r->type = REDIST_ADDR;
+ if ($3 < 0 || $3 > 255 || $5 < 1 || $5 > 32) {
+ yyerror("bad network: %llu/%llu", $3, $5);
+ free(r);
+ YYERROR;
+ }
+ r->addr.s_addr = htonl($3 << IN_CLASSA_NSHIFT);
+ r->mask.s_addr = prefixlen2mask($5);
+
+ if ($1)
+ r->type |= REDIST_NO;
+ r->metric = $6;
+
+ SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry);
+ conf->redistribute |= REDISTRIBUTE_ON;
+ }
+ | no REDISTRIBUTE STRING optlist {
+ struct redistribute *r;
+
+ if (!strcmp($3, "default")) {
+ if (!$1)
+ conf->redistribute |=
+ REDISTRIBUTE_DEFAULT;
+ else
+ conf->redistribute &=
+ ~REDISTRIBUTE_DEFAULT;
+ conf->defaultmetric = $4;
+ } else {
+ if ((r = calloc(1, sizeof(*r))) == NULL)
+ fatal(NULL);
+ if (!strcmp($3, "static"))
+ r->type = REDIST_STATIC;
+ else if (!strcmp($3, "connected"))
+ r->type = REDIST_CONNECTED;
+ else if (host($3, &r->addr, &r->mask))
+ r->type = REDIST_ADDR;
+ else {
+ yyerror("unknown redistribute type");
+ free($3);
+ free(r);
+ YYERROR;
+ }
+
+ if ($1)
+ r->type |= REDIST_NO;
+ r->metric = $4;
+
+ SIMPLEQ_INSERT_TAIL(&conf->redist_list, r,
+ entry);
+ }
+ conf->redistribute |= REDISTRIBUTE_ON;
+ free($3);
+ }
+ | no REDISTRIBUTE RTLABEL STRING optlist {
+ struct redistribute *r;
+
+ if ((r = calloc(1, sizeof(*r))) == NULL)
+ fatal(NULL);
+ r->type = REDIST_LABEL;
+ r->label = rtlabel_name2id($4);
+ if ($1)
+ r->type |= REDIST_NO;
+ r->metric = $5;
+ free($4);
+
+ SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry);
+ conf->redistribute |= REDISTRIBUTE_ON;
+ }
+ | RTLABEL STRING EXTTAG NUMBER {
+ if ($4 < 0 || $4 > UINT_MAX) {
+ yyerror("invalid external route tag");
+ free($2);
+ YYERROR;
+ }
+ rtlabel_tag(rtlabel_name2id($2), $4);
+ free($2);
+ }
+ | SPFDELAY NUMBER {
+ if ($2 < MIN_SPF_DELAY || $2 > MAX_SPF_DELAY) {
+ yyerror("spf-delay out of range "
+ "(%d-%d)", MIN_SPF_DELAY,
+ MAX_SPF_DELAY);
+ YYERROR;
+ }
+ conf->spf_delay = $2;
+ }
+ | SPFHOLDTIME NUMBER {
+ if ($2 < MIN_SPF_HOLDTIME || $2 > MAX_SPF_HOLDTIME) {
+ yyerror("spf-holdtime out of range "
+ "(%d-%d)", MIN_SPF_HOLDTIME,
+ MAX_SPF_HOLDTIME);
+ YYERROR;
+ }
+ conf->spf_hold_time = $2;
+ }
+ | STUB ROUTER yesno {
+ if ($3)
+ conf->flags |= OSPFD_FLAG_STUB_ROUTER;
+ else
+ /* allow to force non stub mode */
+ conf->flags &= ~OSPFD_FLAG_STUB_ROUTER;
+ }
+ | defaults
+ ;
+
+optlist : /* empty */ { $$ = DEFAULT_REDIST_METRIC; }
+ | SET option {
+ $$ = $2;
+ if (($$ & LSA_METRIC_MASK) == 0)
+ $$ |= DEFAULT_REDIST_METRIC;
+ }
+ | SET optnl '{' optnl optlist_l optnl '}' {
+ $$ = $5;
+ if (($$ & LSA_METRIC_MASK) == 0)
+ $$ |= DEFAULT_REDIST_METRIC;
+ }
+ ;
+
+optlist_l : optlist_l comma option {
+ if ($1 & LSA_ASEXT_E_FLAG && $3 & LSA_ASEXT_E_FLAG) {
+ yyerror("redistribute type already defined");
+ YYERROR;
+ }
+ if ($1 & LSA_METRIC_MASK && $3 & LSA_METRIC_MASK) {
+ yyerror("redistribute metric already defined");
+ YYERROR;
+ }
+ $$ = $1 | $3;
+ }
+ | option { $$ = $1; }
+ ;
+
+option : METRIC NUMBER {
+ if ($2 == 0 || $2 > MAX_METRIC) {
+ yyerror("invalid redistribute metric");
+ YYERROR;
+ }
+ $$ = $2;
+ }
+ | TYPE NUMBER {
+ switch ($2) {
+ case 1:
+ $$ = 0;
+ break;
+ case 2:
+ $$ = LSA_ASEXT_E_FLAG;
+ break;
+ default:
+ yyerror("only external type 1 and 2 allowed");
+ YYERROR;
+ }
+ }
+ ;
+
+defaults : METRIC NUMBER {
+ if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
+ yyerror("metric out of range (%d-%d)",
+ MIN_METRIC, MAX_METRIC);
+ YYERROR;
+ }
+ defs->metric = $2;
+ }
+ | ROUTERPRIORITY NUMBER {
+ if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
+ yyerror("router-priority out of range (%d-%d)",
+ MIN_PRIORITY, MAX_PRIORITY);
+ YYERROR;
+ }
+ defs->priority = $2;
+ }
+ | ROUTERDEADTIME NUMBER {
+ if ($2 < MIN_RTR_DEAD_TIME || $2 > MAX_RTR_DEAD_TIME) {
+ yyerror("router-dead-time out of range (%d-%d)",
+ MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME);
+ YYERROR;
+ }
+ defs->dead_interval = $2;
+ }
+ | TRANSMITDELAY NUMBER {
+ if ($2 < MIN_TRANSMIT_DELAY ||
+ $2 > MAX_TRANSMIT_DELAY) {
+ yyerror("transmit-delay out of range (%d-%d)",
+ MIN_TRANSMIT_DELAY, MAX_TRANSMIT_DELAY);
+ YYERROR;
+ }
+ defs->transmit_delay = $2;
+ }
+ | HELLOINTERVAL NUMBER {
+ if ($2 < MIN_HELLO_INTERVAL ||
+ $2 > MAX_HELLO_INTERVAL) {
+ yyerror("hello-interval out of range (%d-%d)",
+ MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
+ YYERROR;
+ }
+ defs->hello_interval = $2;
+ }
+ | RETRANSMITINTERVAL NUMBER {
+ if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
+ yyerror("retransmit-interval out of range "
+ "(%d-%d)", MIN_RXMT_INTERVAL,
+ MAX_RXMT_INTERVAL);
+ YYERROR;
+ }
+ defs->rxmt_interval = $2;
+ }
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl /* one newline or more */
+ ;
+
+comma : ','
+ | /*empty*/
+ ;
+
+area : AREA STRING {
+ struct in_addr id;
+ if (inet_aton($2, &id) == 0) {
+ yyerror("error parsing area");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ area = conf_get_area(id);
+
+ memcpy(&areadefs, defs, sizeof(areadefs));
+ defs = &areadefs;
+ } '{' optnl areaopts_l '}' {
+ area = NULL;
+ defs = &globaldefs;
+ }
+ ;
+
+demotecount : NUMBER { $$ = $1; }
+ | /*empty*/ { $$ = 1; }
+ ;
+
+areaopts_l : areaopts_l areaoptsl nl
+ | areaoptsl optnl
+ ;
+
+areaoptsl : interface
+ | DEMOTE STRING demotecount {
+ if ($3 < 1 || $3 > 255) {
+ yyerror("demote count out of range (1-255)");
+ free($2);
+ YYERROR;
+ }
+ area->demote_level = $3;
+ if (strlcpy(area->demote_group, $2,
+ sizeof(area->demote_group)) >=
+ sizeof(area->demote_group)) {
+ yyerror("demote group name \"%s\" too long");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ if (carp_demote_init(area->demote_group,
+ conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) {
+ yyerror("error initializing group \"%s\"",
+ area->demote_group);
+ YYERROR;
+ }
+ }
+ | defaults
+ ;
+
+interface : INTERFACE STRING {
+ struct kif *kif;
+ struct kif_addr *ka = NULL;
+ char *s;
+ struct in6_addr addr;
+
+ s = strchr($2, ':');
+ if (s) {
+ *s++ = '\0';
+ if (inet_pton(AF_INET6, s, &addr) == 0) {
+ yyerror(
+ "error parsing interface address");
+ free($2);
+ YYERROR;
+ }
+ } else
+ bzero(&addr, sizeof(addr));
+
+ if ((kif = kif_findname($2, &addr, &ka)) == NULL) {
+ yyerror("unknown interface %s", $2);
+ free($2);
+ YYERROR;
+ }
+ if (ka == NULL) {
+ if (s)
+ yyerror("address %s not configured on "
+ "interface %s", s, $2);
+ else
+ yyerror("unnumbered interface %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ iface = conf_get_if(kif, ka);
+ if (iface == NULL)
+ YYERROR;
+ iface->area = area;
+ LIST_INSERT_HEAD(&area->iface_list, iface, entry);
+
+ memcpy(&ifacedefs, defs, sizeof(ifacedefs));
+ defs = &ifacedefs;
+ } interface_block {
+ iface->dead_interval = defs->dead_interval;
+ iface->transmit_delay = defs->transmit_delay;
+ iface->hello_interval = defs->hello_interval;
+ iface->rxmt_interval = defs->rxmt_interval;
+ iface->metric = defs->metric;
+ iface->priority = defs->priority;
+ iface = NULL;
+ /* interface is always part of an area */
+ defs = &areadefs;
+ }
+ ;
+
+interface_block : '{' optnl interfaceopts_l '}'
+ | '{' optnl '}'
+ |
+ ;
+
+interfaceopts_l : interfaceopts_l interfaceoptsl nl
+ | interfaceoptsl optnl
+ ;
+
+interfaceoptsl : PASSIVE { iface->passive = 1; }
+ | DEMOTE STRING {
+ if (strlcpy(iface->demote_group, $2,
+ sizeof(iface->demote_group)) >=
+ sizeof(iface->demote_group)) {
+ yyerror("demote group name \"%s\" too long");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ if (carp_demote_init(iface->demote_group,
+ conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) {
+ yyerror("error initializing group \"%s\"",
+ iface->demote_group);
+ YYERROR;
+ }
+ }
+ | defaults
+ ;
+
+%%
+
+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[] = {
+ {"area", AREA},
+ {"demote", DEMOTE},
+ {"external-tag", EXTTAG},
+ {"fib-update", FIBUPDATE},
+ {"hello-interval", HELLOINTERVAL},
+ {"interface", INTERFACE},
+ {"metric", METRIC},
+ {"no", NO},
+ {"passive", PASSIVE},
+ {"redistribute", REDISTRIBUTE},
+ {"retransmit-interval", RETRANSMITINTERVAL},
+ {"router", ROUTER},
+ {"router-dead-time", ROUTERDEADTIME},
+ {"router-id", ROUTERID},
+ {"router-priority", ROUTERPRIORITY},
+ {"rtlabel", RTLABEL},
+ {"set", SET},
+ {"spf-delay", SPFDELAY},
+ {"spf-holdtime", SPFHOLDTIME},
+ {"stub", STUB},
+ {"transmit-delay", TRANSMITDELAY},
+ {"type", TYPE},
+ {"yes", YES}
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ 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') {
+ c = next;
+ 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_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(fin)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN, LLONG_MAX,
+ &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s", buf,
+ errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#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 ospfd_conf *
+parse_config(char *filename, int opts)
+{
+ struct sym *sym, *next;
+
+ if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL)
+ fatal("parse_config");
+
+ bzero(&globaldefs, sizeof(globaldefs));
+ defs = &globaldefs;
+ defs->dead_interval = DEFAULT_RTR_DEAD_TIME;
+ defs->transmit_delay = DEFAULT_TRANSMIT_DELAY;
+ defs->hello_interval = DEFAULT_HELLO_INTERVAL;
+ defs->rxmt_interval = DEFAULT_RXMT_INTERVAL;
+ defs->metric = DEFAULT_METRIC;
+ defs->priority = DEFAULT_PRIORITY;
+
+ conf->options = OSPF_OPTION_E;
+ conf->spf_delay = DEFAULT_SPF_DELAY;
+ conf->spf_hold_time = DEFAULT_SPF_HOLDTIME;
+ conf->spf_state = SPF_IDLE;
+
+ if ((fin = fopen(filename, "r")) == NULL) {
+ warn("%s", filename);
+ free(conf);
+ return (NULL);
+ }
+ infile = filename;
+
+ conf->opts = opts;
+ if (conf->opts & OSPFD_OPT_STUB_ROUTER)
+ conf->flags |= OSPFD_FLAG_STUB_ROUTER;
+ LIST_INIT(&conf->area_list);
+ LIST_INIT(&conf->cand_list);
+ SIMPLEQ_INIT(&conf->redist_list);
+
+ if (!(conf->opts & OSPFD_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 & OSPFD_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 */
+ if (errors) {
+ clear_config(conf);
+ return (NULL);
+ }
+
+ if (conf->rtr_id.s_addr == 0)
+ conf->rtr_id.s_addr = get_rtr_id();
+
+ 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);
+}
+
+struct area *
+conf_get_area(struct in_addr id)
+{
+ struct area *a;
+
+ a = area_find(conf, id);
+ if (a)
+ return (a);
+ a = area_new();
+ LIST_INSERT_HEAD(&conf->area_list, a, entry);
+
+ a->id.s_addr = id.s_addr;
+
+ return (a);
+}
+
+struct iface *
+conf_get_if(struct kif *kif, struct kif_addr *ka)
+{
+ struct area *a;
+ struct iface *i;
+
+ LIST_FOREACH(a, &conf->area_list, entry)
+ LIST_FOREACH(i, &a->iface_list, entry)
+ if (i->ifindex == kif->ifindex /*&& XXX
+ i->addr.s_addr == ka->addr.s_addr*/) {
+ yyerror("interface %s already configured",
+ kif->ifname);
+ return (NULL);
+ }
+
+ i = if_new(kif, ka);
+
+ return (i);
+}
+
+void
+clear_config(struct ospfd_conf *xconf)
+{
+ struct area *a;
+
+ while ((a = LIST_FIRST(&xconf->area_list)) != NULL) {
+ LIST_REMOVE(a, entry);
+ area_del(a);
+ }
+
+ free(xconf);
+}
+
+u_int32_t
+get_rtr_id(void)
+{
+ struct ifaddrs *ifap, *ifa;
+ u_int32_t ip = 0, cur, localnet;
+
+ localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
+
+ if (getifaddrs(&ifap) == -1)
+ fatal("getifaddrs");
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
+ if ((cur & localnet) == localnet) /* skip 127/8 */
+ continue;
+ if (cur > ip || ip == 0)
+ ip = cur;
+ }
+ freeifaddrs(ifap);
+
+ if (ip == 0)
+ fatal("router-id is 0.0.0.0");
+
+ return (ip);
+}
+
+int
+host(const char *s, struct in_addr *addr, struct in_addr *mask)
+{
+ struct in_addr ina;
+ int bits = 32;
+
+ bzero(&ina, sizeof(struct in_addr));
+ if (strrchr(s, '/') != NULL) {
+ if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)
+ return (0);
+ } else {
+ if (inet_pton(AF_INET, s, &ina) != 1)
+ return (0);
+ }
+
+ addr->s_addr = ina.s_addr;
+ mask->s_addr = prefixlen2mask(bits);
+
+ return (1);
+}
diff --git a/usr.sbin/ospf6d/printconf.c b/usr.sbin/ospf6d/printconf.c
new file mode 100644
index 00000000000..ab00b8884ae
--- /dev/null
+++ b/usr.sbin/ospf6d/printconf.c
@@ -0,0 +1,147 @@
+/* $OpenBSD: printconf.c,v 1.1 2007/10/08 10:44:51 norby Exp $ */
+
+/*
+ * 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/queue.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+
+#include "ospf6.h"
+#include "ospf6d.h"
+#include "ospfe.h"
+
+void print_mainconf(struct ospfd_conf *);
+const char *print_no(u_int16_t);
+void print_redistribute(struct ospfd_conf *);
+void print_rtlabel(struct ospfd_conf *);
+void print_iface(struct iface *);
+
+void
+print_mainconf(struct ospfd_conf *conf)
+{
+ printf("router-id %s\n", inet_ntoa(conf->rtr_id));
+
+ if (conf->flags & OSPFD_FLAG_NO_FIB_UPDATE)
+ printf("fib-update no\n");
+ else
+ printf("fib-update yes\n");
+
+ if (conf->flags & OSPFD_FLAG_STUB_ROUTER)
+ printf("stub router yes\n");
+
+ print_redistribute(conf);
+ print_rtlabel(conf);
+
+ printf("spf-delay %u\n", conf->spf_delay);
+ printf("spf-holdtime %u\n", conf->spf_hold_time);
+}
+
+const char *
+print_no(u_int16_t type)
+{
+ if (type & REDIST_NO)
+ return ("no ");
+ else
+ return ("");
+}
+
+void
+print_redistribute(struct ospfd_conf *conf)
+{
+ struct redistribute *r;
+
+ if (conf->redistribute & REDISTRIBUTE_DEFAULT)
+ printf("redistribute default\n");
+
+ SIMPLEQ_FOREACH(r, &conf->redist_list, entry) {
+ switch (r->type & ~REDIST_NO) {
+ case REDIST_STATIC:
+ printf("%sredistribute static\n", print_no(r->type));
+ break;
+ case REDIST_CONNECTED:
+ printf("%sredistribute connected\n", print_no(r->type));
+ break;
+ case REDIST_LABEL:
+ printf("%sredistribute rtlabel %s\n",
+ print_no(r->type), rtlabel_id2name(r->label));
+ break;
+ case REDIST_ADDR:
+ printf("%sredistribute %s/%d\n",
+ print_no(r->type), inet_ntoa(r->addr),
+ mask2prefixlen(r->mask.s_addr));
+ break;
+ }
+ }
+}
+
+void
+print_rtlabel(struct ospfd_conf *conf)
+{
+ struct n2id_label *label;
+
+ TAILQ_FOREACH(label, &rt_labels, entry)
+ if (label->ext_tag)
+ printf("rtlabel \"%s\" external-tag %u\n",
+ label->name, label->ext_tag);
+}
+
+void
+print_iface(struct iface *iface)
+{
+ printf("\tinterface %s {\n", iface->name);
+
+ printf("\t\thello-interval %d\n", iface->hello_interval);
+ printf("\t\tmetric %d\n", iface->metric);
+
+ if (iface->passive)
+ printf("\t\tpassive\n");
+ if (*iface->demote_group)
+ printf("\t\tdemote %s\n", iface->demote_group);
+
+ printf("\t\tretransmit-interval %d\n", iface->rxmt_interval);
+ printf("\t\trouter-dead-time %d\n", iface->dead_interval);
+ printf("\t\trouter-priority %d\n", iface->priority);
+ printf("\t\ttransmit-delay %d\n", iface->transmit_delay);
+
+ printf("\t}\n");
+}
+
+void
+print_config(struct ospfd_conf *conf)
+{
+ struct area *area;
+ struct iface *iface;
+
+ printf("\n");
+ print_mainconf(conf);
+ printf("\n");
+
+ LIST_FOREACH(area, &conf->area_list, entry) {
+ printf("area %s {\n", inet_ntoa(area->id));
+ if (*area->demote_group)
+ printf("\tdemote %s %d\n", area->demote_group,
+ area->demote_level);
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ print_iface(iface);
+ }
+ printf("}\n\n");
+ }
+}
diff --git a/usr.sbin/ospf6d/rde.c b/usr.sbin/ospf6d/rde.c
new file mode 100644
index 00000000000..61a0ece00b4
--- /dev/null
+++ b/usr.sbin/ospf6d/rde.c
@@ -0,0 +1,1178 @@
+/* $OpenBSD: rde.c,v 1.1 2007/10/08 10:44:51 norby Exp $ */
+
+/*
+ * 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 "ospf6.h"
+#include "ospf6d.h"
+#include "ospfe.h"
+#include "log.h"
+#include "rde.h"
+
+void rde_sig_handler(int sig, short, void *);
+void rde_shutdown(void);
+void rde_dispatch_imsg(int, short, void *);
+void rde_dispatch_parent(int, short, void *);
+
+void rde_send_summary(pid_t);
+void rde_send_summary_area(struct area *, pid_t);
+void rde_nbr_init(u_int32_t);
+void rde_nbr_free(void);
+struct rde_nbr *rde_nbr_find(u_int32_t);
+struct rde_nbr *rde_nbr_new(u_int32_t, struct rde_nbr *);
+void rde_nbr_del(struct rde_nbr *);
+
+void rde_req_list_add(struct rde_nbr *, struct lsa_hdr *);
+int rde_req_list_exists(struct rde_nbr *, struct lsa_hdr *);
+void rde_req_list_del(struct rde_nbr *, struct lsa_hdr *);
+void rde_req_list_free(struct rde_nbr *);
+
+struct lsa *rde_asext_get(struct rroute *);
+struct lsa *rde_asext_put(struct rroute *);
+
+struct lsa *orig_asext_lsa(struct rroute *, u_int16_t);
+struct lsa *orig_sum_lsa(struct rt_node *, u_int8_t, int);
+
+struct ospfd_conf *rdeconf = NULL, *nconf = NULL;
+struct imsgbuf *ibuf_ospfe;
+struct imsgbuf *ibuf_main;
+struct rde_nbr *nbrself;
+struct lsa_tree asext_tree;
+
+/* 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 ospfd_conf *xconf, int pipe_parent2rde[2], int pipe_ospfe2rde[2],
+ int pipe_parent2ospfe[2])
+{
+ struct event ev_sigint, ev_sigterm;
+ struct timeval now;
+ struct passwd *pw;
+ struct redistribute *r;
+ pid_t pid;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ /* NOTREACHED */
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ rdeconf = xconf;
+
+ if ((pw = getpwnam(OSPF6D_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("route decision engine");
+ ospfd_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();
+ rde_nbr_init(NBR_HASHSIZE);
+ lsa_init(&asext_tree);
+
+ /* 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);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ /* setup pipes */
+ close(pipe_ospfe2rde[0]);
+ close(pipe_parent2rde[0]);
+ close(pipe_parent2ospfe[0]);
+ close(pipe_parent2ospfe[1]);
+
+ if ((ibuf_ospfe = malloc(sizeof(struct imsgbuf))) == NULL ||
+ (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_ospfe, pipe_ospfe2rde[1], rde_dispatch_imsg);
+ imsg_init(ibuf_main, pipe_parent2rde[1], rde_dispatch_parent);
+
+ /* setup event handler */
+ ibuf_ospfe->events = EV_READ;
+ event_set(&ibuf_ospfe->ev, ibuf_ospfe->fd, ibuf_ospfe->events,
+ ibuf_ospfe->handler, ibuf_ospfe);
+ event_add(&ibuf_ospfe->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);
+
+ evtimer_set(&rdeconf->ev, spf_timer, rdeconf);
+ cand_list_init();
+ rt_init();
+
+ while ((r = SIMPLEQ_FIRST(&rdeconf->redist_list)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&rdeconf->redist_list, entry);
+ free(r);
+ }
+
+ gettimeofday(&now, NULL);
+ rdeconf->uptime = now.tv_sec;
+
+ event_dispatch();
+
+ rde_shutdown();
+ /* NOTREACHED */
+
+ return (0);
+}
+
+void
+rde_shutdown(void)
+{
+ struct area *a;
+
+ stop_spf_timer(rdeconf);
+ cand_list_clr();
+ rt_clear();
+
+ while ((a = LIST_FIRST(&rdeconf->area_list)) != NULL) {
+ LIST_REMOVE(a, entry);
+ area_del(a);
+ }
+ rde_nbr_free();
+
+ msgbuf_clear(&ibuf_ospfe->w);
+ free(ibuf_ospfe);
+ msgbuf_clear(&ibuf_main->w);
+ free(ibuf_main);
+ free(rdeconf);
+
+ log_info("route decision engine exiting");
+ _exit(0);
+}
+
+int
+rde_imsg_compose_ospfe(int type, u_int32_t peerid, pid_t pid, void *data,
+ u_int16_t datalen)
+{
+ return (imsg_compose(ibuf_ospfe, type, peerid, pid, data, datalen));
+}
+
+/* ARGSUSED */
+void
+rde_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ struct in_addr aid;
+ struct ls_req_hdr req_hdr;
+ struct lsa_hdr lsa_hdr, *db_hdr;
+ struct rde_nbr rn, *nbr;
+ struct timespec tp;
+ struct lsa *lsa;
+ struct area *area;
+ struct vertex *v;
+ char *buf;
+ ssize_t n;
+ time_t now;
+ int r, state, self, shut = 0;
+ u_int16_t l;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ now = tp.tv_sec;
+
+ 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_NEIGHBOR_UP:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rn))
+ fatalx("invalid size of OE request");
+ memcpy(&rn, imsg.data, sizeof(rn));
+
+ if (rde_nbr_find(imsg.hdr.peerid))
+ fatalx("rde_dispatch_imsg: "
+ "neighbor already exists");
+ rde_nbr_new(imsg.hdr.peerid, &rn);
+ break;
+ case IMSG_NEIGHBOR_DOWN:
+ rde_nbr_del(rde_nbr_find(imsg.hdr.peerid));
+ break;
+ case IMSG_NEIGHBOR_CHANGE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(state))
+ fatalx("invalid size of OE request");
+ memcpy(&state, imsg.data, sizeof(state));
+
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ if (state != nbr->state && (nbr->state & NBR_STA_FULL ||
+ state & NBR_STA_FULL))
+ area_track(nbr->area, state);
+
+ nbr->state = state;
+ if (nbr->state & NBR_STA_FULL)
+ rde_req_list_free(nbr);
+ break;
+ case IMSG_DB_SNAPSHOT:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ lsa_snap(nbr->area, imsg.hdr.peerid);
+
+ imsg_compose(ibuf_ospfe, IMSG_DB_END, imsg.hdr.peerid,
+ 0, NULL, 0);
+ break;
+ case IMSG_DD:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ buf = imsg.data;
+ for (l = imsg.hdr.len - IMSG_HEADER_SIZE;
+ l >= sizeof(lsa_hdr); l -= sizeof(lsa_hdr)) {
+ memcpy(&lsa_hdr, buf, sizeof(lsa_hdr));
+ buf += sizeof(lsa_hdr);
+
+ v = lsa_find(nbr->area, lsa_hdr.type,
+ lsa_hdr.ls_id, lsa_hdr.adv_rtr);
+ if (v == NULL)
+ db_hdr = NULL;
+ else
+ db_hdr = &v->lsa->hdr;
+
+ if (lsa_newer(&lsa_hdr, db_hdr) > 0) {
+ /*
+ * only request LSAs that are
+ * newer or missing
+ */
+ rde_req_list_add(nbr, &lsa_hdr);
+ imsg_compose(ibuf_ospfe, IMSG_DD,
+ imsg.hdr.peerid, 0, &lsa_hdr,
+ sizeof(lsa_hdr));
+ }
+ }
+ if (l != 0)
+ log_warnx("rde_dispatch_imsg: peerid %lu, "
+ "trailing garbage in Database Description "
+ "packet", imsg.hdr.peerid);
+
+ imsg_compose(ibuf_ospfe, IMSG_DD_END, imsg.hdr.peerid,
+ 0, NULL, 0);
+ break;
+ case IMSG_LS_REQ:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ buf = imsg.data;
+ for (l = imsg.hdr.len - IMSG_HEADER_SIZE;
+ l >= sizeof(req_hdr); l -= sizeof(req_hdr)) {
+ memcpy(&req_hdr, buf, sizeof(req_hdr));
+ buf += sizeof(req_hdr);
+
+ if ((v = lsa_find(nbr->area,
+ ntohl(req_hdr.type), req_hdr.ls_id,
+ req_hdr.adv_rtr)) == NULL) {
+ imsg_compose(ibuf_ospfe, IMSG_LS_BADREQ,
+ imsg.hdr.peerid, 0, NULL, 0);
+ continue;
+ }
+ imsg_compose(ibuf_ospfe, IMSG_LS_UPD,
+ imsg.hdr.peerid, 0, v->lsa,
+ ntohs(v->lsa->hdr.len));
+ }
+ if (l != 0)
+ log_warnx("rde_dispatch_imsg: peerid %lu, "
+ "trailing garbage in LS Request "
+ "packet", imsg.hdr.peerid);
+ break;
+ case IMSG_LS_UPD:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ lsa = malloc(imsg.hdr.len - IMSG_HEADER_SIZE);
+ if (lsa == NULL)
+ fatal(NULL);
+ memcpy(lsa, imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ if (!lsa_check(nbr, lsa,
+ imsg.hdr.len - IMSG_HEADER_SIZE)) {
+ free(lsa);
+ break;
+ }
+
+ v = lsa_find(nbr->area, lsa->hdr.type, lsa->hdr.ls_id,
+ lsa->hdr.adv_rtr);
+ if (v == NULL)
+ db_hdr = NULL;
+ else
+ db_hdr = &v->lsa->hdr;
+
+ if (nbr->self) {
+ lsa_merge(nbr, lsa, v);
+ /* lsa_merge frees the right lsa */
+ break;
+ }
+
+ r = lsa_newer(&lsa->hdr, db_hdr);
+ if (r > 0) {
+ /* new LSA newer than DB */
+ if (v && v->flooded &&
+ v->changed + MIN_LS_ARRIVAL >= now) {
+ free(lsa);
+ break;
+ }
+
+ rde_req_list_del(nbr, &lsa->hdr);
+
+ if (!(self = lsa_self(nbr, lsa, v)))
+ if (lsa_add(nbr, lsa))
+ /* delayed lsa */
+ break;
+
+ /* flood and perhaps ack LSA */
+ imsg_compose(ibuf_ospfe, IMSG_LS_FLOOD,
+ imsg.hdr.peerid, 0, lsa,
+ ntohs(lsa->hdr.len));
+
+ /* reflood self originated LSA */
+ if (self && v)
+ imsg_compose(ibuf_ospfe, IMSG_LS_FLOOD,
+ v->peerid, 0, v->lsa,
+ ntohs(v->lsa->hdr.len));
+ /* lsa not added so free it */
+ if (self)
+ free(lsa);
+ } else if (r < 0) {
+ /* lsa no longer needed */
+ free(lsa);
+
+ /*
+ * point 6 of "The Flooding Procedure"
+ * We are violating the RFC here because
+ * it does not make sense to reset a session
+ * because an equal LSA is already in the table.
+ * Only if the LSA sent is older than the one
+ * in the table we should reset the session.
+ */
+ if (rde_req_list_exists(nbr, &lsa->hdr)) {
+ imsg_compose(ibuf_ospfe, IMSG_LS_BADREQ,
+ imsg.hdr.peerid, 0, NULL, 0);
+ break;
+ }
+
+ /* new LSA older than DB */
+ if (ntohl(db_hdr->seq_num) == MAX_SEQ_NUM &&
+ ntohs(db_hdr->age) == MAX_AGE)
+ /* seq-num wrap */
+ break;
+
+ if (v->changed + MIN_LS_ARRIVAL >= now)
+ break;
+
+ /* directly send current LSA, no ack */
+ imsg_compose(ibuf_ospfe, IMSG_LS_UPD,
+ imsg.hdr.peerid, 0, v->lsa,
+ ntohs(v->lsa->hdr.len));
+ } else {
+ /* LSA equal send direct ack */
+ imsg_compose(ibuf_ospfe, IMSG_LS_ACK,
+ imsg.hdr.peerid, 0, &lsa->hdr,
+ sizeof(lsa->hdr));
+ free(lsa);
+ }
+ break;
+ case IMSG_LS_MAXAGE:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ break;
+
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct lsa_hdr))
+ fatalx("invalid size of OE request");
+ memcpy(&lsa_hdr, imsg.data, sizeof(lsa_hdr));
+
+ if (rde_nbr_loading(nbr->area))
+ break;
+
+ v = lsa_find(nbr->area, lsa_hdr.type, lsa_hdr.ls_id,
+ lsa_hdr.adv_rtr);
+ if (v == NULL)
+ db_hdr = NULL;
+ else
+ db_hdr = &v->lsa->hdr;
+
+ /*
+ * only delete LSA if the one in the db is not newer
+ */
+ if (lsa_newer(db_hdr, &lsa_hdr) <= 0)
+ lsa_del(nbr, &lsa_hdr);
+ break;
+ case IMSG_CTL_SHOW_DATABASE:
+ case IMSG_CTL_SHOW_DB_EXT:
+ case IMSG_CTL_SHOW_DB_NET:
+ case IMSG_CTL_SHOW_DB_RTR:
+ case IMSG_CTL_SHOW_DB_SELF:
+ case IMSG_CTL_SHOW_DB_SUM:
+ case IMSG_CTL_SHOW_DB_ASBR:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE &&
+ imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(aid)) {
+ log_warnx("rde_dispatch: wrong imsg len");
+ break;
+ }
+ if (imsg.hdr.len == IMSG_HEADER_SIZE) {
+ LIST_FOREACH(area, &rdeconf->area_list, entry) {
+ imsg_compose(ibuf_ospfe, IMSG_CTL_AREA,
+ 0, imsg.hdr.pid, area,
+ sizeof(*area));
+ lsa_dump(&area->lsa_tree, imsg.hdr.type,
+ imsg.hdr.pid);
+ }
+ lsa_dump(&asext_tree, imsg.hdr.type,
+ imsg.hdr.pid);
+ } else {
+ memcpy(&aid, imsg.data, sizeof(aid));
+ if ((area = area_find(rdeconf, aid)) != NULL) {
+ imsg_compose(ibuf_ospfe, IMSG_CTL_AREA,
+ 0, imsg.hdr.pid, area,
+ sizeof(*area));
+ lsa_dump(&area->lsa_tree, imsg.hdr.type,
+ imsg.hdr.pid);
+ if (!area->stub)
+ lsa_dump(&asext_tree,
+ imsg.hdr.type,
+ imsg.hdr.pid);
+ }
+ }
+ imsg_compose(ibuf_ospfe, IMSG_CTL_END, 0, imsg.hdr.pid,
+ NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_RIB:
+ LIST_FOREACH(area, &rdeconf->area_list, entry) {
+ imsg_compose(ibuf_ospfe, IMSG_CTL_AREA,
+ 0, imsg.hdr.pid, area, sizeof(*area));
+
+ rt_dump(area->id, imsg.hdr.pid, RIB_RTR);
+ rt_dump(area->id, imsg.hdr.pid, RIB_NET);
+ }
+ aid.s_addr = 0;
+ rt_dump(aid, imsg.hdr.pid, RIB_EXT);
+
+ imsg_compose(ibuf_ospfe, IMSG_CTL_END, 0, imsg.hdr.pid,
+ NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_SUM:
+ rde_send_summary(imsg.hdr.pid);
+ LIST_FOREACH(area, &rdeconf->area_list, entry)
+ rde_send_summary_area(area, imsg.hdr.pid);
+ imsg_compose(ibuf_ospfe, 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);
+ }
+ if (!shut)
+ imsg_event_add(ibuf);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ }
+}
+
+/* ARGSUSED */
+void
+rde_dispatch_parent(int fd, short event, void *bula)
+{
+ static struct area *narea;
+ struct iface *niface;
+ struct imsg imsg;
+ struct kroute kr;
+ struct rroute rr;
+ struct imsgbuf *ibuf = bula;
+ struct lsa *lsa;
+ struct vertex *v;
+ struct rt_node *rn;
+ ssize_t n;
+ int shut = 0;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ 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(rr)) {
+ log_warnx("rde_dispatch: wrong imsg len");
+ break;
+ }
+ memcpy(&rr, imsg.data, sizeof(rr));
+
+ if ((lsa = rde_asext_get(&rr)) != NULL) {
+ v = lsa_find(NULL, lsa->hdr.type,
+ lsa->hdr.ls_id, lsa->hdr.adv_rtr);
+
+ lsa_merge(nbrself, lsa, v);
+ }
+ break;
+ case IMSG_NETWORK_DEL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(rr)) {
+ log_warnx("rde_dispatch: wrong imsg len");
+ break;
+ }
+ memcpy(&rr, imsg.data, sizeof(rr));
+
+ if ((lsa = rde_asext_put(&rr)) != NULL) {
+ v = lsa_find(NULL, lsa->hdr.type,
+ lsa->hdr.ls_id, lsa->hdr.adv_rtr);
+
+ /*
+ * if v == NULL no LSA is in the table and
+ * nothing has to be done.
+ */
+ if (v)
+ lsa_merge(nbrself, lsa, v);
+ }
+ 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 ((rn = rt_find(kr.prefix.s_addr, kr.prefixlen,
+ DT_NET)) != NULL)
+ rde_send_change_kroute(rn);
+ else
+ /* should not happen */
+ imsg_compose(ibuf_main, IMSG_KROUTE_DELETE, 0,
+ 0, &kr, sizeof(kr));
+ break;
+ case IMSG_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct ospfd_conf))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct ospfd_conf));
+
+ LIST_INIT(&nconf->area_list);
+ LIST_INIT(&nconf->cand_list);
+ break;
+ case IMSG_RECONF_AREA:
+ if ((narea = area_new()) == NULL)
+ fatal(NULL);
+ memcpy(narea, imsg.data, sizeof(struct area));
+
+ LIST_INIT(&narea->iface_list);
+ LIST_INIT(&narea->nbr_list);
+ RB_INIT(&narea->lsa_tree);
+
+ LIST_INSERT_HEAD(&nconf->area_list, narea, entry);
+ break;
+ case IMSG_RECONF_IFACE:
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ LIST_INIT(&niface->nbr_list);
+ TAILQ_INIT(&niface->ls_ack_list);
+
+ niface->area = narea;
+ LIST_INSERT_HEAD(&narea->iface_list, niface, entry);
+
+ break;
+ case IMSG_RECONF_END:
+ merge_config(rdeconf, nconf);
+ nconf = NULL;
+ break;
+ default:
+ log_debug("rde_dispatch_parent: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(ibuf);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ }
+}
+
+u_int32_t
+rde_router_id(void)
+{
+ return (rdeconf->rtr_id.s_addr);
+}
+
+void
+rde_send_change_kroute(struct rt_node *r)
+{
+ struct kroute kr;
+ struct rt_nexthop *rn;
+
+ TAILQ_FOREACH(rn, &r->nexthop, entry) {
+ if (!rn->invalid)
+ break;
+ }
+ if (!rn)
+ fatalx("rde_send_change_kroute: no valid nexthop found");
+
+ bzero(&kr, sizeof(kr));
+ kr.prefix.s_addr = r->prefix.s_addr;
+ kr.nexthop.s_addr = rn->nexthop.s_addr;
+ kr.prefixlen = r->prefixlen;
+ kr.ext_tag = r->ext_tag;
+
+ 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.prefixlen = r->prefixlen;
+
+ imsg_compose(ibuf_main, IMSG_KROUTE_DELETE, 0, 0, &kr, sizeof(kr));
+}
+
+void
+rde_send_summary(pid_t pid)
+{
+ static struct ctl_sum sumctl;
+ struct timeval now;
+ struct area *area;
+ struct vertex *v;
+
+ bzero(&sumctl, sizeof(struct ctl_sum));
+
+ sumctl.rtr_id.s_addr = rde_router_id();
+ sumctl.spf_delay = rdeconf->spf_delay;
+ sumctl.spf_hold_time = rdeconf->spf_hold_time;
+
+ LIST_FOREACH(area, &rdeconf->area_list, entry)
+ sumctl.num_area++;
+
+ RB_FOREACH(v, lsa_tree, &asext_tree)
+ sumctl.num_ext_lsa++;
+
+ gettimeofday(&now, NULL);
+ if (rdeconf->uptime < now.tv_sec)
+ sumctl.uptime = now.tv_sec - rdeconf->uptime;
+ else
+ sumctl.uptime = 0;
+
+ rde_imsg_compose_ospfe(IMSG_CTL_SHOW_SUM, 0, pid, &sumctl,
+ sizeof(sumctl));
+}
+
+void
+rde_send_summary_area(struct area *area, pid_t pid)
+{
+ static struct ctl_sum_area sumareactl;
+ struct iface *iface;
+ struct rde_nbr *nbr;
+ struct lsa_tree *tree = &area->lsa_tree;
+ struct vertex *v;
+
+ bzero(&sumareactl, sizeof(struct ctl_sum_area));
+
+ sumareactl.area.s_addr = area->id.s_addr;
+ sumareactl.num_spf_calc = area->num_spf_calc;
+
+ LIST_FOREACH(iface, &area->iface_list, entry)
+ sumareactl.num_iface++;
+
+ LIST_FOREACH(nbr, &area->nbr_list, entry)
+ if (nbr->state == NBR_STA_FULL && !nbr->self)
+ sumareactl.num_adj_nbr++;
+
+ RB_FOREACH(v, lsa_tree, tree)
+ sumareactl.num_lsa++;
+
+ rde_imsg_compose_ospfe(IMSG_CTL_SHOW_SUM_AREA, 0, pid, &sumareactl,
+ sizeof(sumareactl));
+}
+
+LIST_HEAD(rde_nbr_head, rde_nbr);
+
+struct nbr_table {
+ struct rde_nbr_head *hashtbl;
+ u_int32_t hashmask;
+} rdenbrtable;
+
+#define RDE_NBR_HASH(x) \
+ &rdenbrtable.hashtbl[(x) & rdenbrtable.hashmask]
+
+void
+rde_nbr_init(u_int32_t hashsize)
+{
+ struct rde_nbr_head *head;
+ u_int32_t hs, i;
+
+ for (hs = 1; hs < hashsize; hs <<= 1)
+ ;
+ rdenbrtable.hashtbl = calloc(hs, sizeof(struct rde_nbr_head));
+ if (rdenbrtable.hashtbl == NULL)
+ fatal("rde_nbr_init");
+
+ for (i = 0; i < hs; i++)
+ LIST_INIT(&rdenbrtable.hashtbl[i]);
+
+ rdenbrtable.hashmask = hs - 1;
+
+ if ((nbrself = calloc(1, sizeof(*nbrself))) == NULL)
+ fatal("rde_nbr_init");
+
+ nbrself->id.s_addr = rde_router_id();
+ nbrself->peerid = NBR_IDSELF;
+ nbrself->state = NBR_STA_DOWN;
+ nbrself->self = 1;
+ head = RDE_NBR_HASH(NBR_IDSELF);
+ LIST_INSERT_HEAD(head, nbrself, hash);
+}
+
+void
+rde_nbr_free(void)
+{
+ free(nbrself);
+ free(rdenbrtable.hashtbl);
+}
+
+struct rde_nbr *
+rde_nbr_find(u_int32_t peerid)
+{
+ struct rde_nbr_head *head;
+ struct rde_nbr *nbr;
+
+ head = RDE_NBR_HASH(peerid);
+
+ LIST_FOREACH(nbr, head, hash) {
+ if (nbr->peerid == peerid)
+ return (nbr);
+ }
+
+ return (NULL);
+}
+
+struct rde_nbr *
+rde_nbr_new(u_int32_t peerid, struct rde_nbr *new)
+{
+ struct rde_nbr_head *head;
+ struct rde_nbr *nbr;
+ struct area *area;
+
+ if (rde_nbr_find(peerid))
+ return (NULL);
+ if ((area = area_find(rdeconf, new->area_id)) == NULL)
+ fatalx("rde_nbr_new: unknown area");
+
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal("rde_nbr_new");
+
+ memcpy(nbr, new, sizeof(*nbr));
+ nbr->peerid = peerid;
+ nbr->area = area;
+
+ TAILQ_INIT(&nbr->req_list);
+
+ head = RDE_NBR_HASH(peerid);
+ LIST_INSERT_HEAD(head, nbr, hash);
+ LIST_INSERT_HEAD(&area->nbr_list, nbr, entry);
+
+ return (nbr);
+}
+
+void
+rde_nbr_del(struct rde_nbr *nbr)
+{
+ if (nbr == NULL)
+ return;
+
+ rde_req_list_free(nbr);
+
+ LIST_REMOVE(nbr, entry);
+ LIST_REMOVE(nbr, hash);
+
+ free(nbr);
+}
+
+int
+rde_nbr_loading(struct area *area)
+{
+ struct rde_nbr *nbr;
+ int checkall = 0;
+
+ if (area == NULL) {
+ area = LIST_FIRST(&rdeconf->area_list);
+ checkall = 1;
+ }
+
+ while (area != NULL) {
+ LIST_FOREACH(nbr, &area->nbr_list, entry) {
+ if (nbr->self)
+ continue;
+ if (nbr->state & NBR_STA_XCHNG ||
+ nbr->state & NBR_STA_LOAD)
+ return (1);
+ }
+ if (!checkall)
+ break;
+ area = LIST_NEXT(area, entry);
+ }
+
+ return (0);
+}
+
+struct rde_nbr *
+rde_nbr_self(struct area *area)
+{
+ struct rde_nbr *nbr;
+
+ LIST_FOREACH(nbr, &area->nbr_list, entry)
+ if (nbr->self)
+ return (nbr);
+
+ /* this may not happen */
+ fatalx("rde_nbr_self: area without self");
+ return (NULL);
+}
+
+/*
+ * LSA req list
+ */
+void
+rde_req_list_add(struct rde_nbr *nbr, struct lsa_hdr *lsa)
+{
+ struct rde_req_entry *le;
+
+ if ((le = calloc(1, sizeof(*le))) == NULL)
+ fatal("rde_req_list_add");
+
+ TAILQ_INSERT_TAIL(&nbr->req_list, le, entry);
+ le->type = lsa->type;
+ le->ls_id = lsa->ls_id;
+ le->adv_rtr = lsa->adv_rtr;
+}
+
+int
+rde_req_list_exists(struct rde_nbr *nbr, struct lsa_hdr *lsa_hdr)
+{
+ struct rde_req_entry *le;
+
+ TAILQ_FOREACH(le, &nbr->req_list, entry) {
+ if ((lsa_hdr->type == le->type) &&
+ (lsa_hdr->ls_id == le->ls_id) &&
+ (lsa_hdr->adv_rtr == le->adv_rtr))
+ return (1);
+ }
+ return (0);
+}
+
+void
+rde_req_list_del(struct rde_nbr *nbr, struct lsa_hdr *lsa_hdr)
+{
+ struct rde_req_entry *le;
+
+ TAILQ_FOREACH(le, &nbr->req_list, entry) {
+ if ((lsa_hdr->type == le->type) &&
+ (lsa_hdr->ls_id == le->ls_id) &&
+ (lsa_hdr->adv_rtr == le->adv_rtr)) {
+ TAILQ_REMOVE(&nbr->req_list, le, entry);
+ free(le);
+ return;
+ }
+ }
+}
+
+void
+rde_req_list_free(struct rde_nbr *nbr)
+{
+ struct rde_req_entry *le;
+
+ while ((le = TAILQ_FIRST(&nbr->req_list)) != NULL) {
+ TAILQ_REMOVE(&nbr->req_list, le, entry);
+ free(le);
+ }
+}
+
+/*
+ * as-external LSA handling
+ */
+struct lsa *
+rde_asext_get(struct rroute *rr)
+{
+#if 0
+ struct area *area;
+ struct iface *iface;
+XXX
+ LIST_FOREACH(area, &rdeconf->area_list, entry)
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if ((iface->addr.s_addr & iface->mask.s_addr) ==
+ rr->kr.prefix.s_addr && iface->mask.s_addr ==
+ prefixlen2mask(rr->kr.prefixlen)) {
+ /* already announced as (stub) net LSA */
+ log_debug("rde_asext_get: %s/%d is net LSA",
+ inet_ntoa(rr->kr.prefix), rr->kr.prefixlen);
+ return (NULL);
+ }
+ }
+#endif
+ /* update of seqnum is done by lsa_merge */
+ return (orig_asext_lsa(rr, DEFAULT_AGE));
+}
+
+struct lsa *
+rde_asext_put(struct rroute *rr)
+{
+ /*
+ * just try to remove the LSA. If the prefix is announced as
+ * stub net LSA lsa_find() will fail later and nothing will happen.
+ */
+
+ /* remove by reflooding with MAX_AGE */
+ return (orig_asext_lsa(rr, MAX_AGE));
+}
+
+/*
+ * summary LSA stuff
+ */
+void
+rde_summary_update(struct rt_node *rte, struct area *area)
+{
+ struct vertex *v = NULL;
+ struct lsa *lsa;
+ u_int8_t type = 0;
+
+ /* first check if we actually need to announce this route */
+ if (!(rte->d_type == DT_NET || rte->flags & OSPF_RTR_E))
+ return;
+ /* never create summaries for as-ext LSA */
+ if (rte->p_type == PT_TYPE1_EXT || rte->p_type == PT_TYPE2_EXT)
+ return;
+ /* no need for summary LSA in the originating area */
+ if (rte->area.s_addr == area->id.s_addr)
+ return;
+ /* no need to originate inter-area routes to the backbone */
+ if (rte->p_type == PT_INTER_AREA && area->id.s_addr == INADDR_ANY)
+ return;
+ /* TODO nexthop check, nexthop part of area -> no summary */
+ if (rte->cost >= LS_INFINITY)
+ return;
+ /* TODO AS border router specific checks */
+ /* TODO inter-area network route stuff */
+ /* TODO intra-area stuff -- condense LSA ??? */
+
+ if (rte->d_type == DT_NET) {
+ type = LSA_TYPE_SUM_NETWORK;
+ } else if (rte->d_type == DT_RTR) {
+ type = LSA_TYPE_SUM_ROUTER;
+ } else
+ fatalx("rde_summary_update: unknown route type");
+
+ /* update lsa but only if it was changed */
+ v = lsa_find(area, type, rte->prefix.s_addr, rde_router_id());
+ lsa = orig_sum_lsa(rte, type, rte->invalid);
+ lsa_merge(rde_nbr_self(area), lsa, v);
+
+ if (v == NULL)
+ v = lsa_find(area, type, rte->prefix.s_addr, rde_router_id());
+
+ /* suppressed/deleted routes are not found in the second lsa_find */
+ if (v)
+ v->cost = rte->cost;
+}
+
+
+/*
+ * functions for self-originated LSA
+ */
+struct lsa *
+orig_asext_lsa(struct rroute *rr, u_int16_t age)
+{
+ struct lsa *lsa;
+ u_int16_t len;
+
+ len = sizeof(struct lsa_hdr) + sizeof(struct lsa_asext);
+ if ((lsa = calloc(1, len)) == NULL)
+ fatal("orig_asext_lsa");
+
+ log_debug("orig_asext_lsa: %s/%d age %d",
+ inet_ntoa(rr->kr.prefix), rr->kr.prefixlen, age);
+
+ /* LSA header */
+ lsa->hdr.age = htons(age);
+ lsa->hdr.type = LSA_TYPE_EXTERNAL;
+ lsa->hdr.adv_rtr = rdeconf->rtr_id.s_addr;
+ lsa->hdr.seq_num = htonl(INIT_SEQ_NUM);
+ lsa->hdr.len = htons(len);
+
+ /* prefix and mask */
+ /*
+ * TODO ls_id must be unique, for overlapping routes this may
+ * not be true. In this case a hack needs to be done to
+ * make the ls_id unique.
+ */
+ lsa->hdr.ls_id = rr->kr.prefix.s_addr;
+ lsa->data.asext.mask = prefixlen2mask(rr->kr.prefixlen);
+
+ /*
+ * nexthop -- on connected routes we are the nexthop,
+ * on all other cases we announce the true nexthop.
+ * XXX this is wrong as the true nexthop may be outside
+ * of the ospf cloud and so unreachable. For now we force
+ * all traffic to be directed to us.
+ */
+ lsa->data.asext.fw_addr = 0;
+
+ lsa->data.asext.metric = htonl(rr->metric);
+ lsa->data.asext.ext_tag = htonl(rr->kr.ext_tag);
+
+ lsa->hdr.ls_chksum = 0;
+ lsa->hdr.ls_chksum =
+ htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
+
+ return (lsa);
+}
+
+struct lsa *
+orig_sum_lsa(struct rt_node *rte, u_int8_t type, int invalid)
+{
+ struct lsa *lsa;
+ u_int16_t len;
+
+ len = sizeof(struct lsa_hdr) + sizeof(struct lsa_sum);
+ if ((lsa = calloc(1, len)) == NULL)
+ fatal("orig_sum_lsa");
+
+ /* LSA header */
+ lsa->hdr.age = htons(invalid ? MAX_AGE : DEFAULT_AGE);
+ lsa->hdr.type = type;
+ lsa->hdr.adv_rtr = rdeconf->rtr_id.s_addr;
+ lsa->hdr.seq_num = htonl(INIT_SEQ_NUM);
+ lsa->hdr.len = htons(len);
+
+ /* prefix and mask */
+ /*
+ * TODO ls_id must be unique, for overlapping routes this may
+ * not be true. In this case a hack needs to be done to
+ * make the ls_id unique.
+ */
+ lsa->hdr.ls_id = rte->prefix.s_addr;
+ if (type == LSA_TYPE_SUM_NETWORK)
+ lsa->data.sum.mask = prefixlen2mask(rte->prefixlen);
+ else
+ lsa->data.sum.mask = 0; /* must be zero per RFC */
+
+ lsa->data.sum.metric = htonl(rte->cost & LSA_METRIC_MASK);
+
+ lsa->hdr.ls_chksum = 0;
+ lsa->hdr.ls_chksum =
+ htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
+
+ return (lsa);
+}
diff --git a/usr.sbin/ospf6d/rde.h b/usr.sbin/ospf6d/rde.h
new file mode 100644
index 00000000000..3d8d0d00c1a
--- /dev/null
+++ b/usr.sbin/ospf6d/rde.h
@@ -0,0 +1,169 @@
+/* $OpenBSD: rde.h,v 1.1 2007/10/08 10:44:51 norby Exp $ */
+
+/*
+ * 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 v_nexthop {
+ TAILQ_ENTRY(v_nexthop) entry;
+ struct vertex *prev;
+ struct in_addr nexthop;
+};
+
+TAILQ_HEAD(v_nexthead, v_nexthop);
+
+struct vertex {
+ RB_ENTRY(vertex) entry;
+ TAILQ_ENTRY(vertex) cand;
+ struct v_nexthead nexthop;
+ struct event ev;
+ struct area *area;
+ struct lsa *lsa;
+ time_t changed;
+ time_t stamp;
+ u_int32_t cost;
+ u_int32_t peerid; /* neighbor unique imsg ID */
+ u_int32_t ls_id;
+ u_int32_t adv_rtr;
+ u_int8_t type;
+ u_int8_t flooded;
+ u_int8_t deleted;
+ u_int8_t self;
+};
+
+struct rde_req_entry {
+ TAILQ_ENTRY(rde_req_entry) entry;
+ u_int32_t ls_id;
+ u_int32_t adv_rtr;
+ u_int8_t type;
+};
+
+/* just the info RDE needs */
+struct rde_nbr {
+ LIST_ENTRY(rde_nbr) entry, hash;
+ struct in_addr id;
+ struct in_addr area_id;
+ TAILQ_HEAD(, rde_req_entry) req_list;
+ struct area *area;
+ u_int32_t peerid; /* unique ID in DB */
+ int state;
+ int self;
+};
+
+struct rt_nexthop {
+ TAILQ_ENTRY(rt_nexthop) entry;
+ struct in_addr nexthop;
+ struct in_addr adv_rtr;
+ time_t uptime;
+ u_int8_t connected;
+ u_int8_t invalid;
+};
+
+struct rt_node {
+ RB_ENTRY(rt_node) entry;
+ TAILQ_HEAD(,rt_nexthop) nexthop;
+ struct in_addr prefix;
+ struct in_addr area;
+ u_int32_t cost;
+ u_int32_t cost2;
+ u_int32_t ext_tag;
+ enum path_type p_type;
+ enum dst_type d_type;
+ u_int8_t flags;
+ u_int8_t prefixlen;
+ u_int8_t invalid;
+};
+
+struct abr_rtr {
+ struct in6_addr addr;
+ struct in_addr abr_id;
+ struct in6_addr dst_ip;
+ struct in_addr area;
+ u_int16_t metric;
+};
+
+extern struct lsa_tree asext_tree;
+
+/* rde.c */
+pid_t rde(struct ospfd_conf *, int [2], int [2], int [2]);
+int rde_imsg_compose_ospfe(int, u_int32_t, pid_t, void *,
+ u_int16_t);
+u_int32_t rde_router_id(void);
+void rde_send_change_kroute(struct rt_node *);
+void rde_send_delete_kroute(struct rt_node *);
+void rde_nbr_del(struct rde_nbr *);
+int rde_nbr_loading(struct area *);
+struct rde_nbr *rde_nbr_self(struct area *);
+void rde_summary_update(struct rt_node *, struct area *);
+
+/* rde_lsdb.c */
+void lsa_init(struct lsa_tree *);
+int lsa_compare(struct vertex *, struct vertex *);
+void vertex_free(struct vertex *);
+int lsa_newer(struct lsa_hdr *, struct lsa_hdr *);
+int lsa_check(struct rde_nbr *, struct lsa *, u_int16_t);
+int lsa_self(struct rde_nbr *, struct lsa *, struct vertex *);
+int lsa_add(struct rde_nbr *, struct lsa *);
+void lsa_del(struct rde_nbr *, struct lsa_hdr *);
+void lsa_age(struct vertex *);
+struct vertex *lsa_find(struct area *, u_int8_t, u_int32_t, u_int32_t);
+struct vertex *lsa_find_net(struct area *area, u_int32_t);
+u_int16_t lsa_num_links(struct vertex *);
+void lsa_snap(struct area *, u_int32_t);
+void lsa_dump(struct lsa_tree *, int, pid_t);
+void lsa_merge(struct rde_nbr *, struct lsa *, struct vertex *);
+void lsa_remove_invalid_sums(struct area *);
+
+/* rde_spf.c */
+void spf_calc(struct area *);
+void rt_calc(struct vertex *, struct area *, struct ospfd_conf *);
+void asext_calc(struct vertex *);
+void spf_tree_clr(struct area *);
+
+void cand_list_init(void);
+void cand_list_add(struct vertex *);
+struct vertex *cand_list_pop(void);
+int cand_list_present(struct vertex *);
+void cand_list_clr(void);
+
+void spf_timer(int, short, void *);
+void start_spf_timer(void);
+void stop_spf_timer(struct ospfd_conf *);
+void start_spf_holdtimer(struct ospfd_conf *);
+
+void rt_init(void);
+int rt_compare(struct rt_node *, struct rt_node *);
+struct rt_node *rt_find(in_addr_t, u_int8_t, enum dst_type);
+int rt_insert(struct rt_node *);
+int rt_remove(struct rt_node *);
+void rt_clear(void);
+void rt_dump(struct in_addr, pid_t, u_int8_t);
+
+struct lsa_rtr_link *get_rtr_link(struct vertex *, int);
+struct lsa_net_link *get_net_link(struct vertex *, int);
+
+RB_PROTOTYPE(lsa_tree, vertex, entry, lsa_compare)
+
+#endif /* _RDE_H_ */
diff --git a/usr.sbin/ospf6d/rde_lsdb.c b/usr.sbin/ospf6d/rde_lsdb.c
new file mode 100644
index 00000000000..70c23652f16
--- /dev/null
+++ b/usr.sbin/ospf6d/rde_lsdb.c
@@ -0,0 +1,737 @@
+/* $OpenBSD: rde_lsdb.c,v 1.1 2007/10/08 10:44:51 norby Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@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/tree.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ospf6.h"
+#include "ospf6d.h"
+#include "rde.h"
+#include "log.h"
+
+struct vertex *vertex_get(struct lsa *, struct rde_nbr *);
+
+int lsa_router_check(struct lsa *, u_int16_t);
+void lsa_timeout(int, short, void *);
+void lsa_refresh(struct vertex *);
+int lsa_equal(struct lsa *, struct lsa *);
+
+RB_GENERATE(lsa_tree, vertex, entry, lsa_compare)
+
+void
+lsa_init(struct lsa_tree *t)
+{
+ RB_INIT(t);
+}
+
+int
+lsa_compare(struct vertex *a, struct vertex *b)
+{
+ if (a->type < b->type)
+ return (-1);
+ if (a->type > b->type)
+ return (1);
+ if (a->ls_id < b->ls_id)
+ return (-1);
+ if (a->ls_id > b->ls_id)
+ return (1);
+ if (a->adv_rtr < b->adv_rtr)
+ return (-1);
+ if (a->adv_rtr > b->adv_rtr)
+ return (1);
+ return (0);
+}
+
+
+struct vertex *
+vertex_get(struct lsa *lsa, struct rde_nbr *nbr)
+{
+ struct vertex *v;
+ struct timespec tp;
+
+ if ((v = calloc(1, sizeof(struct vertex))) == NULL)
+ fatal(NULL);
+ TAILQ_INIT(&v->nexthop);
+ v->area = nbr->area;
+ v->peerid = nbr->peerid;
+ v->lsa = lsa;
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ v->changed = v->stamp = tp.tv_sec;
+ v->cost = LS_INFINITY;
+ v->ls_id = ntohl(lsa->hdr.ls_id);
+ v->adv_rtr = ntohl(lsa->hdr.adv_rtr);
+ v->type = lsa->hdr.type;
+
+ if (!nbr->self)
+ v->flooded = 1; /* XXX fix me */
+ v->self = nbr->self;
+
+ evtimer_set(&v->ev, lsa_timeout, v);
+
+ return (v);
+}
+
+void
+vertex_free(struct vertex *v)
+{
+ if (v->type == LSA_TYPE_EXTERNAL)
+ RB_REMOVE(lsa_tree, &asext_tree, v);
+ else
+ RB_REMOVE(lsa_tree, &v->area->lsa_tree, v);
+
+ (void)evtimer_del(&v->ev);
+ free(v->lsa);
+ free(v);
+}
+
+/* returns -1 if a is older, 1 if newer and 0 if equal to b */
+int
+lsa_newer(struct lsa_hdr *a, struct lsa_hdr *b)
+{
+ int32_t a32, b32;
+ u_int16_t a16, b16;
+ int i;
+
+ if (a == NULL)
+ return (-1);
+ if (b == NULL)
+ return (1);
+
+ /*
+ * The sequence number is defined as signed 32-bit integer,
+ * no idea how IETF came up with such a stupid idea.
+ */
+ a32 = (int32_t)ntohl(a->seq_num);
+ b32 = (int32_t)ntohl(b->seq_num);
+
+ if (a32 > b32)
+ return (1);
+ if (a32 < b32)
+ return (-1);
+
+ a16 = ntohs(a->ls_chksum);
+ b16 = ntohs(b->ls_chksum);
+
+ if (a16 > b16)
+ return (1);
+ if (a16 < b16)
+ return (-1);
+
+ a16 = ntohs(a->age);
+ b16 = ntohs(b->age);
+
+ if (a16 >= MAX_AGE && b16 >= MAX_AGE)
+ return (0);
+ if (b16 >= MAX_AGE)
+ return (-1);
+ if (a16 >= MAX_AGE)
+ return (1);
+
+ i = b16 - a16;
+ if (abs(i) > MAX_AGE_DIFF)
+ return (i > 0 ? 1 : -1);
+
+ return (0);
+}
+
+int
+lsa_check(struct rde_nbr *nbr, struct lsa *lsa, u_int16_t len)
+{
+ struct area *area = nbr->area;
+ u_int32_t metric;
+
+ if (len < sizeof(lsa->hdr)) {
+ log_warnx("lsa_check: bad packet size");
+ return (0);
+ }
+ if (ntohs(lsa->hdr.len) != len) {
+ log_warnx("lsa_check: bad packet size");
+ return (0);
+ }
+
+ if (iso_cksum(lsa, len, 0)) {
+ log_warnx("lsa_check: bad packet checksum");
+ return (0);
+ }
+
+ /* invalid ages */
+ if ((ntohs(lsa->hdr.age) < 1 && !nbr->self) ||
+ ntohs(lsa->hdr.age) > MAX_AGE) {
+ log_warnx("lsa_check: bad age");
+ return (0);
+ }
+
+ /* invalid sequence number */
+ if (ntohl(lsa->hdr.seq_num) == RESV_SEQ_NUM) {
+ log_warnx("ls_check: bad seq num");
+ return (0);
+ }
+
+ switch (lsa->hdr.type) {
+ case LSA_TYPE_ROUTER:
+ if (!lsa_router_check(lsa, len))
+ return (0);
+ break;
+ case LSA_TYPE_NETWORK:
+ if ((len % sizeof(u_int32_t)) ||
+ len < sizeof(lsa->hdr) + sizeof(u_int32_t)) {
+ log_warnx("lsa_check: bad LSA network packet");
+ return (0);
+ }
+ break;
+ case LSA_TYPE_SUM_NETWORK:
+ case LSA_TYPE_SUM_ROUTER:
+ if ((len % sizeof(u_int32_t)) ||
+ len < sizeof(lsa->hdr) + sizeof(lsa->data.sum)) {
+ log_warnx("lsa_check: bad LSA summary packet");
+ return (0);
+ }
+ metric = ntohl(lsa->data.sum.metric);
+ if (metric & ~LSA_METRIC_MASK) {
+ log_warnx("lsa_check: bad LSA summary metric");
+ return (0);
+ }
+ break;
+ case LSA_TYPE_EXTERNAL:
+ if ((len % (3 * sizeof(u_int32_t))) ||
+ len < sizeof(lsa->hdr) + sizeof(lsa->data.asext)) {
+ log_warnx("lsa_check: bad LSA as-external packet");
+ return (0);
+ }
+ metric = ntohl(lsa->data.asext.metric);
+ if (metric & ~(LSA_METRIC_MASK | LSA_ASEXT_E_FLAG)) {
+ log_warnx("lsa_check: bad LSA as-external metric");
+ return (0);
+ }
+ /* AS-external-LSA are silently discarded in stub areas */
+ if (area->stub)
+ return (0);
+ break;
+ default:
+ log_warnx("lsa_check: unknown type %u", lsa->hdr.type);
+ return (0);
+ }
+
+ /* MaxAge handling */
+ if (lsa->hdr.age == htons(MAX_AGE) && !nbr->self && lsa_find(area,
+ lsa->hdr.type, lsa->hdr.ls_id, lsa->hdr.adv_rtr) == NULL &&
+ !rde_nbr_loading(area)) {
+ /*
+ * if no neighbor in state Exchange or Loading
+ * ack LSA but don't add it. Needs to be a direct ack.
+ */
+ rde_imsg_compose_ospfe(IMSG_LS_ACK, nbr->peerid, 0, &lsa->hdr,
+ sizeof(struct lsa_hdr));
+ return (0);
+ }
+
+ return (1);
+}
+
+int
+lsa_router_check(struct lsa *lsa, u_int16_t len)
+{
+ struct lsa_rtr_link *rtr_link;
+ char *buf = (char *)lsa;
+ u_int16_t i, off, nlinks;
+
+ off = sizeof(lsa->hdr) + sizeof(struct lsa_rtr);
+ if (off > len) {
+ log_warnx("lsa_check: invalid LSA router packet");
+ return (0);
+ }
+
+ nlinks = ntohs(lsa->data.rtr.nlinks);
+ for (i = 0; i < nlinks; i++) {
+ rtr_link = (struct lsa_rtr_link *)(buf + off);
+ off += sizeof(struct lsa_rtr_link);
+ if (off > len) {
+ log_warnx("lsa_check: invalid LSA router packet");
+ return (0);
+ }
+ off += rtr_link->num_tos * sizeof(u_int32_t);
+ if (off > len) {
+ log_warnx("lsa_check: invalid LSA router packet");
+ return (0);
+ }
+ }
+
+ if (i != nlinks) {
+ log_warnx("lsa_check: invalid LSA router packet");
+ return (0);
+ }
+ return (1);
+}
+
+int
+lsa_self(struct rde_nbr *nbr, struct lsa *new, struct vertex *v)
+{
+ struct lsa *dummy;
+#if 0
+ struct iface *iface;
+
+ if (nbr->self)
+ return (0);
+
+ if (rde_router_id() == new->hdr.adv_rtr)
+ goto self;
+
+ if (new->hdr.type == LSA_TYPE_NETWORK)
+ LIST_FOREACH(iface, &nbr->area->iface_list, entry)
+ if (iface->addr.s_addr == new->hdr.ls_id)
+ goto self;
+
+ return (0);
+self:
+#endif
+ if (v == NULL) {
+ /*
+ * LSA is no longer announced, remove by premature aging.
+ * The problem is that new may not be altered so a copy
+ * needs to be added to the LSA DB first.
+ */
+ if ((dummy = malloc(ntohs(new->hdr.len))) == NULL)
+ fatal("lsa_self");
+ memcpy(dummy, new, ntohs(new->hdr.len));
+ dummy->hdr.age = htons(MAX_AGE);
+ /*
+ * The clue is that by using the remote nbr as originator
+ * the dummy LSA will be reflooded via the default timeout
+ * handler.
+ */
+ (void)lsa_add(rde_nbr_self(nbr->area), dummy);
+ return (1);
+ }
+
+ /*
+ * LSA is still originated, just reflood it. But we need to create
+ * a new instance by setting the LSA sequence number equal to the
+ * one of new and calling lsa_refresh(). Flooding will be done by the
+ * caller.
+ */
+ v->lsa->hdr.seq_num = new->hdr.seq_num;
+ lsa_refresh(v);
+ return (1);
+}
+
+int
+lsa_add(struct rde_nbr *nbr, struct lsa *lsa)
+{
+ struct lsa_tree *tree;
+ struct vertex *new, *old;
+ struct timeval tv, now, res;
+
+ if (lsa->hdr.type == LSA_TYPE_EXTERNAL)
+ tree = &asext_tree;
+ else
+ tree = &nbr->area->lsa_tree;
+
+ new = vertex_get(lsa, nbr);
+ old = RB_INSERT(lsa_tree, tree, new);
+
+ if (old != NULL) {
+ if (old->deleted && evtimer_pending(&old->ev, &tv)) {
+ /* new update added before hold time expired */
+ gettimeofday(&now, NULL);
+ timersub(&tv, &now, &res);
+
+ /* remove old LSA and insert new LSA with delay */
+ vertex_free(old);
+ RB_INSERT(lsa_tree, tree, new);
+ new->deleted = 1;
+
+ if (evtimer_add(&new->ev, &res) != 0)
+ fatal("lsa_add");
+ return (1);
+ }
+ if (!lsa_equal(new->lsa, old->lsa)) {
+ if (lsa->hdr.type != LSA_TYPE_EXTERNAL)
+ nbr->area->dirty = 1;
+ start_spf_timer();
+ }
+ vertex_free(old);
+ RB_INSERT(lsa_tree, tree, new);
+ } else {
+ if (lsa->hdr.type != LSA_TYPE_EXTERNAL)
+ nbr->area->dirty = 1;
+ start_spf_timer();
+ }
+
+ /* timeout handling either MAX_AGE or LS_REFRESH_TIME */
+ timerclear(&tv);
+
+ if (nbr->self && ntohs(new->lsa->hdr.age) == DEFAULT_AGE)
+ tv.tv_sec = LS_REFRESH_TIME;
+ else
+ tv.tv_sec = MAX_AGE - ntohs(new->lsa->hdr.age);
+
+ if (evtimer_add(&new->ev, &tv) != 0)
+ fatal("lsa_add");
+ return (0);
+}
+
+void
+lsa_del(struct rde_nbr *nbr, struct lsa_hdr *lsa)
+{
+ struct vertex *v;
+ struct timeval tv;
+
+ v = lsa_find(nbr->area, lsa->type, lsa->ls_id, lsa->adv_rtr);
+ if (v == NULL)
+ return;
+
+ v->deleted = 1;
+ /* hold time to make sure that a new lsa is not added premature */
+ timerclear(&tv);
+ tv.tv_sec = MIN_LS_INTERVAL;
+ if (evtimer_add(&v->ev, &tv) == -1)
+ fatal("lsa_del");
+}
+
+void
+lsa_age(struct vertex *v)
+{
+ struct timespec tp;
+ time_t now;
+ int d;
+ u_int16_t age;
+
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ now = tp.tv_sec;
+
+ d = now - v->stamp;
+ /* set stamp so that at least new calls work */
+ v->stamp = now;
+
+ if (d < 0) {
+ log_warnx("lsa_age: time went backwards");
+ return;
+ }
+
+ age = ntohs(v->lsa->hdr.age);
+ if (age + d > MAX_AGE)
+ age = MAX_AGE;
+ else
+ age += d;
+
+ v->lsa->hdr.age = htons(age);
+}
+
+struct vertex *
+lsa_find(struct area *area, u_int8_t type, u_int32_t ls_id, u_int32_t adv_rtr)
+{
+ struct vertex key;
+ struct vertex *v;
+ struct lsa_tree *tree;
+
+ key.ls_id = ntohl(ls_id);
+ key.adv_rtr = ntohl(adv_rtr);
+ key.type = type;
+
+ if (type == LSA_TYPE_EXTERNAL)
+ tree = &asext_tree;
+ else
+ tree = &area->lsa_tree;
+
+ v = RB_FIND(lsa_tree, tree, &key);
+
+ /* LSA that are deleted are not findable */
+ if (v && v->deleted)
+ return (NULL);
+
+ if (v)
+ lsa_age(v);
+
+ return (v);
+}
+
+struct vertex *
+lsa_find_net(struct area *area, u_int32_t ls_id)
+{
+ struct lsa_tree *tree = &area->lsa_tree;
+ struct vertex *v;
+
+ /* XXX speed me up */
+ RB_FOREACH(v, lsa_tree, tree) {
+ if (v->lsa->hdr.type == LSA_TYPE_NETWORK &&
+ v->lsa->hdr.ls_id == ls_id) {
+ /* LSA that are deleted are not findable */
+ if (v->deleted)
+ return (NULL);
+ lsa_age(v);
+ return (v);
+ }
+ }
+
+ return (NULL);
+}
+
+u_int16_t
+lsa_num_links(struct vertex *v)
+{
+ switch (v->type) {
+ case LSA_TYPE_ROUTER:
+ return (ntohs(v->lsa->data.rtr.nlinks));
+ case LSA_TYPE_NETWORK:
+ return ((ntohs(v->lsa->hdr.len) - sizeof(struct lsa_hdr)
+ - sizeof(u_int32_t)) / sizeof(struct lsa_net_link));
+ default:
+ fatalx("lsa_num_links: invalid LSA type");
+ }
+
+ return (0);
+}
+
+void
+lsa_snap(struct area *area, u_int32_t peerid)
+{
+ struct lsa_tree *tree = &area->lsa_tree;
+ struct vertex *v;
+
+ do {
+ RB_FOREACH(v, lsa_tree, tree) {
+ if (v->deleted)
+ continue;
+ lsa_age(v);
+ if (ntohs(v->lsa->hdr.age) >= MAX_AGE)
+ rde_imsg_compose_ospfe(IMSG_LS_UPD, peerid,
+ 0, &v->lsa->hdr, ntohs(v->lsa->hdr.len));
+ else
+ rde_imsg_compose_ospfe(IMSG_DB_SNAPSHOT, peerid,
+ 0, &v->lsa->hdr, sizeof(struct lsa_hdr));
+ }
+ if (tree != &area->lsa_tree)
+ break;
+ tree = &asext_tree;
+ } while (1);
+}
+
+void
+lsa_dump(struct lsa_tree *tree, int imsg_type, pid_t pid)
+{
+ struct vertex *v;
+
+ RB_FOREACH(v, lsa_tree, tree) {
+ if (v->deleted)
+ continue;
+ lsa_age(v);
+ switch (imsg_type) {
+ case IMSG_CTL_SHOW_DATABASE:
+ rde_imsg_compose_ospfe(IMSG_CTL_SHOW_DATABASE, 0, pid,
+ &v->lsa->hdr, ntohs(v->lsa->hdr.len));
+ continue;
+ case IMSG_CTL_SHOW_DB_SELF:
+ if (v->lsa->hdr.adv_rtr == rde_router_id())
+ break;
+ continue;
+ case IMSG_CTL_SHOW_DB_EXT:
+ if (v->type == LSA_TYPE_EXTERNAL)
+ break;
+ continue;
+ case IMSG_CTL_SHOW_DB_NET:
+ if (v->type == LSA_TYPE_NETWORK)
+ break;
+ continue;
+ case IMSG_CTL_SHOW_DB_RTR:
+ if (v->type == LSA_TYPE_ROUTER)
+ break;
+ continue;
+ case IMSG_CTL_SHOW_DB_SUM:
+ if (v->type == LSA_TYPE_SUM_NETWORK)
+ break;
+ continue;
+ case IMSG_CTL_SHOW_DB_ASBR:
+ if (v->type == LSA_TYPE_SUM_ROUTER)
+ break;
+ continue;
+ default:
+ log_warnx("lsa_dump: unknown imsg type");
+ return;
+ }
+ rde_imsg_compose_ospfe(imsg_type, 0, pid, &v->lsa->hdr,
+ ntohs(v->lsa->hdr.len));
+ }
+}
+
+/* ARGSUSED */
+void
+lsa_timeout(int fd, short event, void *bula)
+{
+ struct vertex *v = bula;
+ struct timeval tv;
+
+ lsa_age(v);
+
+ if (v->deleted) {
+ if (ntohs(v->lsa->hdr.age) >= MAX_AGE) {
+ vertex_free(v);
+ } else {
+ v->deleted = 0;
+
+ /* schedule recalculation of the RIB */
+ if (v->lsa->hdr.type != LSA_TYPE_EXTERNAL)
+ v->area->dirty = 1;
+ start_spf_timer();
+
+ rde_imsg_compose_ospfe(IMSG_LS_FLOOD, v->peerid, 0,
+ v->lsa, ntohs(v->lsa->hdr.len));
+
+ /* timeout handling either MAX_AGE or LS_REFRESH_TIME */
+ timerclear(&tv);
+ if (v->self)
+ tv.tv_sec = LS_REFRESH_TIME;
+ else
+ tv.tv_sec = MAX_AGE - ntohs(v->lsa->hdr.age);
+
+ if (evtimer_add(&v->ev, &tv) != 0)
+ fatal("lsa_add");
+ }
+ return;
+ }
+
+ if (v->self && ntohs(v->lsa->hdr.age) < MAX_AGE)
+ lsa_refresh(v);
+
+ rde_imsg_compose_ospfe(IMSG_LS_FLOOD, v->peerid, 0,
+ v->lsa, ntohs(v->lsa->hdr.len));
+}
+
+void
+lsa_refresh(struct vertex *v)
+{
+ struct timeval tv;
+ struct timespec tp;
+ u_int32_t seqnum;
+ u_int16_t len;
+
+ /* refresh LSA by increasing sequence number by one */
+ v->lsa->hdr.age = htons(DEFAULT_AGE);
+ seqnum = ntohl(v->lsa->hdr.seq_num);
+ if (seqnum++ == MAX_SEQ_NUM)
+ /* XXX fix me */
+ fatalx("sequence number wrapping");
+ v->lsa->hdr.seq_num = htonl(seqnum);
+
+ /* recalculate checksum */
+ len = ntohs(v->lsa->hdr.len);
+ v->lsa->hdr.ls_chksum = 0;
+ v->lsa->hdr.ls_chksum = htons(iso_cksum(v->lsa, len, LS_CKSUM_OFFSET));
+
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ v->changed = v->stamp = tp.tv_sec;
+
+ timerclear(&tv);
+ tv.tv_sec = LS_REFRESH_TIME;
+ if (evtimer_add(&v->ev, &tv) == -1)
+ fatal("lsa_refresh");
+}
+
+void
+lsa_merge(struct rde_nbr *nbr, struct lsa *lsa, struct vertex *v)
+{
+ struct timeval tv;
+ struct timespec tp;
+ time_t now;
+ u_int16_t len;
+
+ if (v == NULL) {
+ if (lsa_add(nbr, lsa))
+ /* delayed update */
+ return;
+ rde_imsg_compose_ospfe(IMSG_LS_FLOOD, nbr->peerid, 0,
+ lsa, ntohs(lsa->hdr.len));
+ return;
+ }
+
+ /* set the seq_num to the current one. lsa_refresh() will do the ++ */
+ lsa->hdr.seq_num = v->lsa->hdr.seq_num;
+ /* recalculate checksum */
+ len = ntohs(lsa->hdr.len);
+ lsa->hdr.ls_chksum = 0;
+ lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
+
+ /* compare LSA most header fields are equal so don't check them */
+ if (lsa_equal(lsa, v->lsa)) {
+ free(lsa);
+ return;
+ }
+
+ /* overwrite the lsa all other fields are unaffected */
+ free(v->lsa);
+ v->lsa = lsa;
+ start_spf_timer();
+ if (lsa->hdr.type != LSA_TYPE_EXTERNAL)
+ nbr->area->dirty = 1;
+
+ /* set correct timeout for reflooding the LSA */
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ now = tp.tv_sec;
+ timerclear(&tv);
+ if (v->changed + MIN_LS_INTERVAL >= now)
+ tv.tv_sec = MIN_LS_INTERVAL;
+ if (evtimer_add(&v->ev, &tv) == -1)
+ fatal("lsa_merge");
+}
+
+void
+lsa_remove_invalid_sums(struct area *area)
+{
+ struct lsa_tree *tree = &area->lsa_tree;
+ struct vertex *v, *nv;
+
+ /* XXX speed me up */
+ for (v = RB_MIN(lsa_tree, tree); v != NULL; v = nv) {
+ nv = RB_NEXT(lsa_tree, tree, v);
+ if ((v->lsa->hdr.type == LSA_TYPE_SUM_NETWORK ||
+ v->lsa->hdr.type == LSA_TYPE_SUM_ROUTER) &&
+ v->self && v->cost == LS_INFINITY &&
+ v->deleted == 0) {
+ /*
+ * age the lsa and call lsa_timeout() which will
+ * actually remove it from the database.
+ */
+ v->lsa->hdr.age = htons(MAX_AGE);
+ lsa_timeout(0, 0, v);
+ }
+ }
+}
+
+int
+lsa_equal(struct lsa *a, struct lsa *b)
+{
+ /*
+ * compare LSA that already have same type, adv_rtr and ls_id
+ * so not all header need to be compared
+ */
+ if (a == NULL || b == NULL)
+ return (0);
+ if (a->hdr.len != b->hdr.len)
+ return (0);
+ /* LSA with age MAX_AGE are never equal */
+ if (a->hdr.age == htons(MAX_AGE) || b->hdr.age == htons(MAX_AGE))
+ return (0);
+ if (memcmp(&a->data, &b->data, ntohs(a->hdr.len) -
+ sizeof(struct lsa_hdr)))
+ return (0);
+
+ return (1);
+}
+
diff --git a/usr.sbin/ospf6d/rde_spf.c b/usr.sbin/ospf6d/rde_spf.c
new file mode 100644
index 00000000000..7ef02ef6206
--- /dev/null
+++ b/usr.sbin/ospf6d/rde_spf.c
@@ -0,0 +1,1077 @@
+/* $OpenBSD: rde_spf.c,v 1.1 2007/10/08 10:44:51 norby Exp $ */
+
+/*
+ * Copyright (c) 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 <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <stdlib.h>
+
+#include "ospf6d.h"
+#include "ospf6.h"
+#include "log.h"
+#include "rde.h"
+
+extern struct ospfd_conf *rdeconf;
+TAILQ_HEAD(, vertex) cand_list;
+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)
+struct vertex *spf_root = NULL;
+
+void calc_nexthop_clear(struct vertex *);
+void calc_nexthop_add(struct vertex *, struct vertex *, u_int32_t);
+void calc_nexthop(struct vertex *, struct vertex *);
+void rt_nexthop_clear(struct rt_node *);
+void rt_nexthop_add(struct rt_node *, struct v_nexthead *,
+ struct in_addr);
+void rt_update(struct in_addr, u_int8_t, struct v_nexthead *,
+ u_int32_t, u_int32_t, struct in_addr, struct in_addr,
+ enum path_type, enum dst_type, u_int8_t, u_int32_t);
+struct rt_node *rt_lookup(enum dst_type, in_addr_t);
+void rt_invalidate(struct area *);
+int linked(struct vertex *, struct vertex *);
+
+void
+spf_calc(struct area *area)
+{
+ struct vertex *v, *w;
+ struct lsa_rtr_link *rtr_link = NULL;
+ struct lsa_net_link *net_link;
+ u_int32_t d;
+ int i;
+ struct in_addr addr;
+
+ /* clear SPF tree */
+ spf_tree_clr(area);
+ cand_list_clr();
+
+ /* initialize SPF tree */
+ if ((v = spf_root = lsa_find(area, LSA_TYPE_ROUTER, rde_router_id(),
+ rde_router_id())) == NULL)
+ /* empty area because no interface is active */
+ return;
+
+ area->transit = 0;
+ spf_root->cost = 0;
+ w = NULL;
+
+ /* calculate SPF tree */
+ do {
+ /* loop links */
+ for (i = 0; i < lsa_num_links(v); i++) {
+ switch (v->type) {
+ case LSA_TYPE_ROUTER:
+ rtr_link = get_rtr_link(v, i);
+ switch (rtr_link->type) {
+ case LINK_TYPE_STUB_NET:
+ /* skip */
+ continue;
+ case LINK_TYPE_POINTTOPOINT:
+ case LINK_TYPE_VIRTUAL:
+ /* find router LSA */
+ w = lsa_find(area, LSA_TYPE_ROUTER,
+ rtr_link->id, rtr_link->id);
+ break;
+ case LINK_TYPE_TRANSIT_NET:
+ /* find network LSA */
+ w = lsa_find_net(area, rtr_link->id);
+ break;
+ default:
+ fatalx("spf_calc: invalid link type");
+ }
+ break;
+ case LSA_TYPE_NETWORK:
+ net_link = get_net_link(v, i);
+ /* find router LSA */
+ w = lsa_find(area, LSA_TYPE_ROUTER,
+ net_link->att_rtr, net_link->att_rtr);
+ break;
+ default:
+ fatalx("spf_calc: invalid LSA type");
+ }
+
+ if (w == NULL)
+ continue;
+
+ if (w->lsa->hdr.age == MAX_AGE)
+ continue;
+
+ if (!linked(w, v)) {
+ addr.s_addr = htonl(w->ls_id);
+ log_debug("spf_calc: w id %s type %d has ",
+ inet_ntoa(addr), w->type);
+ addr.s_addr = htonl(v->ls_id);
+ log_debug(" no link to v id %s type %d",
+ inet_ntoa(addr), v->type);
+ continue;
+ }
+
+ if (v->type == LSA_TYPE_ROUTER)
+ d = v->cost + ntohs(rtr_link->metric);
+ else
+ d = v->cost;
+
+ if (cand_list_present(w)) {
+ if (d > w->cost)
+ continue;
+ if (d < w->cost) {
+ w->cost = d;
+ calc_nexthop_clear(w);
+ calc_nexthop(w, v);
+ /*
+ * need to readd to candidate list
+ * because the list is sorted
+ */
+ TAILQ_REMOVE(&cand_list, w, cand);
+ cand_list_add(w);
+ } else
+ /* equal cost path */
+ calc_nexthop(w, v);
+ } else if (w->cost == LS_INFINITY && d < LS_INFINITY) {
+ w->cost = d;
+
+ calc_nexthop_clear(w);
+ calc_nexthop(w, v);
+ cand_list_add(w);
+ }
+ }
+
+ /* get next vertex */
+ v = cand_list_pop();
+ w = NULL;
+ } while (v != NULL);
+
+ /* spf_dump(area); */
+ log_debug("spf_calc: area %s calculated",
+ inet_ntoa(area->id));
+
+ area->num_spf_calc++;
+ start_spf_timer();
+}
+
+void
+rt_calc(struct vertex *v, struct area *area, struct ospfd_conf *conf)
+{
+ struct vertex *w;
+ struct v_nexthop *vn;
+ struct lsa_rtr_link *rtr_link = NULL;
+ int i;
+ struct in_addr addr, adv_rtr;
+
+ lsa_age(v);
+ if (ntohs(v->lsa->hdr.age) == MAX_AGE)
+ return;
+
+ switch (v->type) {
+ case LSA_TYPE_ROUTER:
+ /* stub networks */
+ if (v->cost >= LS_INFINITY || TAILQ_EMPTY(&v->nexthop))
+ return;
+
+ for (i = 0; i < lsa_num_links(v); i++) {
+ rtr_link = get_rtr_link(v, i);
+ if (rtr_link->type != LINK_TYPE_STUB_NET)
+ continue;
+
+ addr.s_addr = rtr_link->id;
+ adv_rtr.s_addr = htonl(v->adv_rtr);
+
+ rt_update(addr, mask2prefixlen(rtr_link->data),
+ &v->nexthop, v->cost + ntohs(rtr_link->metric), 0,
+ area->id, adv_rtr, PT_INTRA_AREA, DT_NET,
+ v->lsa->data.rtr.flags, 0);
+ }
+
+ /* router, only add border and as-external routers */
+ if ((v->lsa->data.rtr.flags & (OSPF_RTR_B | OSPF_RTR_E)) == 0)
+ return;
+
+ addr.s_addr = htonl(v->ls_id);
+ adv_rtr.s_addr = htonl(v->adv_rtr);
+
+ rt_update(addr, 32, &v->nexthop, v->cost, 0, area->id,
+ adv_rtr, PT_INTRA_AREA, DT_RTR, v->lsa->data.rtr.flags, 0);
+ break;
+ case LSA_TYPE_NETWORK:
+ if (v->cost >= LS_INFINITY || TAILQ_EMPTY(&v->nexthop))
+ return;
+
+ addr.s_addr = htonl(v->ls_id) & v->lsa->data.net.mask;
+ adv_rtr.s_addr = htonl(v->adv_rtr);
+ rt_update(addr, mask2prefixlen(v->lsa->data.net.mask),
+ &v->nexthop, v->cost, 0, area->id, adv_rtr, PT_INTRA_AREA,
+ DT_NET, 0, 0);
+ break;
+ case LSA_TYPE_SUM_NETWORK:
+ case LSA_TYPE_SUM_ROUTER:
+ /* if ABR only look at area 0.0.0.0 LSA */
+ if (area_border_router(conf) && area->id.s_addr != INADDR_ANY)
+ return;
+
+ /* ignore self-originated stuff */
+ if (v->self)
+ return;
+
+ /* TODO type 3 area address range check */
+
+ if ((w = lsa_find(area, LSA_TYPE_ROUTER,
+ htonl(v->adv_rtr),
+ htonl(v->adv_rtr))) == NULL)
+ return;
+
+ /* copy nexthops */
+ calc_nexthop_clear(v); /* XXX needed ??? */
+ TAILQ_FOREACH(vn, &w->nexthop, entry)
+ calc_nexthop_add(v, w, vn->nexthop.s_addr);
+
+ v->cost = w->cost +
+ (ntohl(v->lsa->data.sum.metric) & LSA_METRIC_MASK);
+
+ if (v->cost >= LS_INFINITY || TAILQ_EMPTY(&v->nexthop))
+ return;
+
+ adv_rtr.s_addr = htonl(v->adv_rtr);
+ if (v->type == LSA_TYPE_SUM_NETWORK) {
+ addr.s_addr = htonl(v->ls_id) & v->lsa->data.sum.mask;
+ rt_update(addr, mask2prefixlen(v->lsa->data.sum.mask),
+ &v->nexthop, v->cost, 0, area->id, adv_rtr,
+ PT_INTER_AREA, DT_NET, 0, 0);
+ } else {
+ addr.s_addr = htonl(v->ls_id);
+ rt_update(addr, 32, &v->nexthop, v->cost, 0, area->id,
+ adv_rtr, PT_INTER_AREA, DT_RTR,
+ v->lsa->data.rtr.flags, 0);
+ }
+
+ break;
+ default:
+ /* as-external LSA are stored in a different tree */
+ fatalx("rt_calc: invalid LSA type");
+ }
+}
+
+void
+asext_calc(struct vertex *v)
+{
+ struct rt_node *r;
+ struct rt_nexthop *rn;
+ u_int32_t cost2;
+ struct in_addr addr, adv_rtr, a;
+ enum path_type type;
+
+ lsa_age(v);
+ if (ntohs(v->lsa->hdr.age) == MAX_AGE ||
+ (ntohl(v->lsa->data.asext.metric) & LSA_METRIC_MASK) >=
+ LS_INFINITY)
+ return;
+
+ switch (v->type) {
+ case LSA_TYPE_EXTERNAL:
+ /* ignore self-originated stuff */
+ if (v->self)
+ return;
+
+ if ((r = rt_lookup(DT_RTR, htonl(v->adv_rtr))) == NULL)
+ return;
+
+ /* XXX RFC1583Compatibility */
+ if (v->lsa->data.asext.fw_addr != 0 &&
+ (r = rt_lookup(DT_NET, v->lsa->data.asext.fw_addr)) == NULL)
+ return;
+
+ if (v->lsa->data.asext.fw_addr != 0 &&
+ r->p_type != PT_INTRA_AREA &&
+ r->p_type != PT_INTER_AREA)
+ return;
+
+ if (ntohl(v->lsa->data.asext.metric) & LSA_ASEXT_E_FLAG) {
+ v->cost = r->cost;
+ cost2 = ntohl(v->lsa->data.asext.metric) &
+ LSA_METRIC_MASK;
+ type = PT_TYPE2_EXT;
+ } else {
+ v->cost = r->cost + (ntohl(v->lsa->data.asext.metric) &
+ LSA_METRIC_MASK);
+ cost2 = 0;
+ type = PT_TYPE1_EXT;
+ }
+
+ a.s_addr = 0;
+ adv_rtr.s_addr = htonl(v->adv_rtr);
+ addr.s_addr = htonl(v->ls_id) & v->lsa->data.asext.mask;
+
+ calc_nexthop_clear(v);
+ TAILQ_FOREACH(rn, &r->nexthop, entry) {
+ if (rn->invalid)
+ continue;
+
+ if (rn->connected && r->d_type == DT_NET) {
+ if (v->lsa->data.asext.fw_addr != 0)
+ calc_nexthop_add(v, NULL,
+ v->lsa->data.asext.fw_addr);
+ else
+ calc_nexthop_add(v, NULL,
+ htonl(v->adv_rtr));
+ } else
+ calc_nexthop_add(v, NULL, rn->nexthop.s_addr);
+ }
+
+ rt_update(addr, mask2prefixlen(v->lsa->data.asext.mask),
+ &v->nexthop, v->cost, cost2, a, adv_rtr, type,
+ DT_NET, 0, ntohl(v->lsa->data.asext.ext_tag));
+ break;
+ default:
+ fatalx("asext_calc: invalid LSA type");
+ }
+}
+
+void
+spf_tree_clr(struct area *area)
+{
+ struct lsa_tree *tree = &area->lsa_tree;
+ struct vertex *v;
+
+ RB_FOREACH(v, lsa_tree, tree) {
+ v->cost = LS_INFINITY;
+ calc_nexthop_clear(v);
+ }
+}
+
+void
+calc_nexthop_clear(struct vertex *v)
+{
+ struct v_nexthop *vn;
+
+ while ((vn = TAILQ_FIRST(&v->nexthop))) {
+ TAILQ_REMOVE(&v->nexthop, vn, entry);
+ free(vn);
+ }
+}
+
+void
+calc_nexthop_add(struct vertex *dst, struct vertex *parent, u_int32_t nexthop)
+{
+ struct v_nexthop *vn;
+
+ if (nexthop == 0)
+ /* invalid nexthop, skip it */
+ return;
+
+ if ((vn = calloc(1, sizeof(*vn))) == NULL)
+ fatal("calc_nexthop_add");
+
+ vn->prev = parent;
+ vn->nexthop.s_addr = nexthop;
+
+ TAILQ_INSERT_TAIL(&dst->nexthop, vn, entry);
+}
+
+void
+calc_nexthop(struct vertex *dst, struct vertex *parent)
+{
+ struct lsa_rtr_link *rtr_link = NULL;
+ struct v_nexthop *vn;
+ int i;
+
+ /* case 1 */
+ if (parent == spf_root) {
+ switch (dst->type) {
+ case LSA_TYPE_ROUTER:
+ for (i = 0; i < lsa_num_links(dst); i++) {
+ rtr_link = get_rtr_link(dst, i);
+ if (rtr_link->type == LINK_TYPE_POINTTOPOINT &&
+ ntohl(rtr_link->id) == parent->ls_id) {
+ calc_nexthop_add(dst, parent,
+ rtr_link->data);
+ break;
+ }
+ }
+ return;
+ case LSA_TYPE_NETWORK:
+ for (i = 0; i < lsa_num_links(parent); i++) {
+ rtr_link = get_rtr_link(parent, i);
+ switch (rtr_link->type) {
+ case LINK_TYPE_POINTTOPOINT:
+ /* ignore */
+ break;
+ case LINK_TYPE_TRANSIT_NET:
+ if ((htonl(dst->ls_id) &
+ dst->lsa->data.net.mask) ==
+ (rtr_link->data &
+ dst->lsa->data.net.mask)) {
+ calc_nexthop_add(dst, parent,
+ rtr_link->data);
+ }
+ break;
+ case LINK_TYPE_STUB_NET:
+ break;
+
+ default:
+ fatalx("calc_nexthop: invalid link "
+ "type");
+ }
+ }
+ return;
+ default:
+ fatalx("calc_nexthop: invalid dst type");
+ }
+ }
+
+ /* case 2 */
+ if (parent->type == LSA_TYPE_NETWORK && dst->type == LSA_TYPE_ROUTER) {
+ TAILQ_FOREACH(vn, &parent->nexthop, entry) {
+ if (vn->prev == spf_root) {
+ for (i = 0; i < lsa_num_links(dst); i++) {
+ rtr_link = get_rtr_link(dst, i);
+ if ((rtr_link->type ==
+ LINK_TYPE_TRANSIT_NET) &&
+ (rtr_link->data &
+ parent->lsa->data.net.mask) ==
+ (htonl(parent->ls_id) &
+ parent->lsa->data.net.mask))
+ calc_nexthop_add(dst, parent,
+ rtr_link->data);
+ }
+ } else {
+ calc_nexthop_add(dst, parent,
+ vn->nexthop.s_addr);
+ }
+ }
+ return;
+ }
+
+ /* case 3 */
+ TAILQ_FOREACH(vn, &parent->nexthop, entry)
+ calc_nexthop_add(dst, parent, vn->nexthop.s_addr);
+}
+
+/* candidate list */
+void
+cand_list_init(void)
+{
+ TAILQ_INIT(&cand_list);
+}
+
+void
+cand_list_add(struct vertex *v)
+{
+ struct vertex *c = NULL;
+
+ TAILQ_FOREACH(c, &cand_list, cand) {
+ if (c->cost > v->cost) {
+ TAILQ_INSERT_BEFORE(c, v, cand);
+ return;
+ } else if (c->cost == v->cost && c->type == LSA_TYPE_ROUTER &&
+ v->type == LSA_TYPE_NETWORK) {
+ TAILQ_INSERT_BEFORE(c, v, cand);
+ return;
+ }
+ }
+ TAILQ_INSERT_TAIL(&cand_list, v, cand);
+}
+
+struct vertex *
+cand_list_pop(void)
+{
+ struct vertex *c;
+
+ if ((c = TAILQ_FIRST(&cand_list)) != NULL) {
+ TAILQ_REMOVE(&cand_list, c, cand);
+ }
+
+ return (c);
+}
+
+int
+cand_list_present(struct vertex *v)
+{
+ struct vertex *c;
+
+ TAILQ_FOREACH(c, &cand_list, cand) {
+ if (c == v)
+ return (1);
+ }
+
+ return (0);
+}
+
+void
+cand_list_clr(void)
+{
+ struct vertex *c;
+
+ while ((c = TAILQ_FIRST(&cand_list)) != NULL) {
+ TAILQ_REMOVE(&cand_list, c, cand);
+ }
+}
+
+/* timers */
+/* ARGSUSED */
+void
+spf_timer(int fd, short event, void *arg)
+{
+ struct vertex *v;
+ struct ospfd_conf *conf = arg;
+ struct area *area;
+ struct rt_node *r;
+
+ switch (conf->spf_state) {
+ case SPF_IDLE:
+ fatalx("spf_timer: invalid state IDLE");
+ case SPF_HOLDQUEUE:
+ conf->spf_state = SPF_DELAY;
+ /* FALLTHROUGH */
+ case SPF_DELAY:
+ LIST_FOREACH(area, &conf->area_list, entry) {
+ if (area->dirty) {
+ /* invalidate RIB entries of this area */
+ rt_invalidate(area);
+
+ /* calculate SPF tree */
+ spf_calc(area);
+
+ /* calculate route table */
+ RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
+ rt_calc(v, area, conf);
+ }
+
+ area->dirty = 0;
+ }
+ }
+
+ /* calculate as-external routes, first invalidate them */
+ rt_invalidate(NULL);
+ RB_FOREACH(v, lsa_tree, &asext_tree) {
+ asext_calc(v);
+ }
+
+ RB_FOREACH(r, rt_tree, &rt) {
+ LIST_FOREACH(area, &conf->area_list, entry)
+ rde_summary_update(r, area);
+
+ if (r->d_type != DT_NET)
+ continue;
+
+ if (r->invalid)
+ rde_send_delete_kroute(r);
+ else
+ rde_send_change_kroute(r);
+ }
+
+ LIST_FOREACH(area, &conf->area_list, entry)
+ lsa_remove_invalid_sums(area);
+
+ start_spf_holdtimer(conf);
+ break;
+ case SPF_HOLD:
+ conf->spf_state = SPF_IDLE;
+ break;
+ default:
+ fatalx("spf_timer: unknown state");
+ }
+}
+
+void
+start_spf_timer(void)
+{
+ struct timeval tv;
+
+ switch (rdeconf->spf_state) {
+ case SPF_IDLE:
+ timerclear(&tv);
+ tv.tv_sec = rdeconf->spf_delay;
+ rdeconf->spf_state = SPF_DELAY;
+ if (evtimer_add(&rdeconf->ev, &tv) == -1)
+ fatal("start_spf_timer");
+ break;
+ case SPF_DELAY:
+ /* ignore */
+ break;
+ case SPF_HOLD:
+ rdeconf->spf_state = SPF_HOLDQUEUE;
+ break;
+ case SPF_HOLDQUEUE:
+ /* ignore */
+ break;
+ default:
+ fatalx("start_spf_timer: invalid spf_state");
+ }
+}
+
+void
+stop_spf_timer(struct ospfd_conf *conf)
+{
+ if (evtimer_del(&conf->ev) == -1)
+ fatal("stop_spf_timer");
+}
+
+void
+start_spf_holdtimer(struct ospfd_conf *conf)
+{
+ struct timeval tv;
+
+ switch (conf->spf_state) {
+ case SPF_DELAY:
+ timerclear(&tv);
+ tv.tv_sec = conf->spf_hold_time;
+ conf->spf_state = SPF_HOLD;
+ if (evtimer_add(&conf->ev, &tv) == -1)
+ fatal("start_spf_holdtimer");
+ break;
+ case SPF_IDLE:
+ case SPF_HOLD:
+ case SPF_HOLDQUEUE:
+ fatalx("start_spf_holdtimer: invalid state");
+ default:
+ fatalx("spf_start_holdtimer: unknown state");
+ }
+}
+
+/* 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 (a->prefixlen < b->prefixlen)
+ return (-1);
+ if (a->prefixlen > b->prefixlen)
+ return (1);
+ if (a->d_type > b->d_type)
+ return (-1);
+ if (a->d_type < b->d_type)
+ return (1);
+ return (0);
+}
+
+struct rt_node *
+rt_find(in_addr_t prefix, u_int8_t prefixlen, enum dst_type d_type)
+{
+ struct rt_node s;
+
+ s.prefix.s_addr = prefix;
+ s.prefixlen = prefixlen;
+ s.d_type = d_type;
+
+ return (RB_FIND(rt_tree, &rt, &s));
+}
+
+int
+rt_insert(struct rt_node *r)
+{
+ if (RB_INSERT(rt_tree, &rt, r) != NULL) {
+ log_warnx("rt_insert failed for %s/%u",
+ inet_ntoa(r->prefix), r->prefixlen);
+ free(r);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+rt_remove(struct rt_node *r)
+{
+ if (RB_REMOVE(rt_tree, &rt, r) == NULL) {
+ log_warnx("rt_remove failed for %s/%u",
+ inet_ntoa(r->prefix), r->prefixlen);
+ return (-1);
+ }
+
+ rt_nexthop_clear(r);
+ free(r);
+ return (0);
+}
+
+void
+rt_invalidate(struct area *area)
+{
+ struct rt_node *r, *nr;
+ struct rt_nexthop *rn, *nrn;
+
+ for (r = RB_MIN(rt_tree, &rt); r != NULL; r = nr) {
+ nr = RB_NEXT(rt_tree, &rt, r);
+ if (area == NULL) {
+ /* look only at as_ext routes */
+ if (r->p_type != PT_TYPE1_EXT &&
+ r->p_type != PT_TYPE2_EXT)
+ continue;
+ } else {
+ /* ignore all as_ext routes */
+ if (r->p_type == PT_TYPE1_EXT ||
+ r->p_type == PT_TYPE2_EXT)
+ continue;
+
+ /* look only at routes matching the area */
+ if (r->area.s_addr != area->id.s_addr)
+ continue;
+ }
+ r->invalid = 1;
+ for (rn = TAILQ_FIRST(&r->nexthop); rn != NULL; rn = nrn) {
+ nrn = TAILQ_NEXT(rn, entry);
+ if (rn->invalid) {
+ TAILQ_REMOVE(&r->nexthop, rn, entry);
+ free(rn);
+ } else
+ rn->invalid = 1;
+ }
+ if (TAILQ_EMPTY(&r->nexthop))
+ rt_remove(r);
+ }
+}
+
+void
+rt_nexthop_clear(struct rt_node *r)
+{
+ struct rt_nexthop *rn;
+
+ while ((rn = TAILQ_FIRST(&r->nexthop)) != NULL) {
+ TAILQ_REMOVE(&r->nexthop, rn, entry);
+ free(rn);
+ }
+}
+
+void
+rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh,
+ struct in_addr adv_rtr)
+{
+ struct v_nexthop *vn;
+ struct rt_nexthop *rn;
+ struct timespec now;
+
+ TAILQ_FOREACH(vn, vnh, entry) {
+ TAILQ_FOREACH(rn, &r->nexthop, entry) {
+ if (rn->nexthop.s_addr != vn->nexthop.s_addr)
+ continue;
+
+ rn->adv_rtr.s_addr = adv_rtr.s_addr;
+ rn->connected = vn->prev == spf_root;
+ rn->invalid = 0;
+
+ r->invalid = 0;
+ break;
+ }
+ if (rn)
+ continue;
+
+ if ((rn = calloc(1, sizeof(struct rt_nexthop))) == NULL)
+ fatal("rt_nexthop_add");
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ rn->nexthop.s_addr = vn->nexthop.s_addr;
+ rn->adv_rtr.s_addr = adv_rtr.s_addr;
+ rn->uptime = now.tv_sec;
+ rn->connected = vn->prev == spf_root;
+ rn->invalid = 0;
+
+ r->invalid = 0;
+ TAILQ_INSERT_TAIL(&r->nexthop, rn, entry);
+ }
+}
+
+void
+rt_clear(void)
+{
+ struct rt_node *r;
+
+ while ((r = RB_MIN(rt_tree, &rt)) != NULL)
+ rt_remove(r);
+}
+
+void
+rt_dump(struct in_addr area, pid_t pid, u_int8_t r_type)
+{
+ static struct ctl_rt rtctl;
+ struct timespec now;
+ struct rt_node *r;
+ struct rt_nexthop *rn;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ RB_FOREACH(r, rt_tree, &rt) {
+ if (r->invalid)
+ continue;
+
+ if (r->area.s_addr != area.s_addr)
+ continue;
+
+ switch (r_type) {
+ case RIB_RTR:
+ if (r->d_type != DT_RTR)
+ continue;
+ break;
+ case RIB_NET:
+ if (r->d_type != DT_NET)
+ continue;
+ if (r->p_type == PT_TYPE1_EXT ||
+ r->p_type == PT_TYPE2_EXT)
+ continue;
+ break;
+ case RIB_EXT:
+ if (r->p_type != PT_TYPE1_EXT &&
+ r->p_type != PT_TYPE2_EXT)
+ continue;
+ break;
+ default:
+ fatalx("rt_dump: invalid RIB type");
+ }
+
+ TAILQ_FOREACH(rn, &r->nexthop, entry) {
+ if (rn->invalid)
+ continue;
+
+ rtctl.prefix.s_addr = r->prefix.s_addr;
+ rtctl.nexthop.s_addr = rn->nexthop.s_addr;
+ rtctl.area.s_addr = r->area.s_addr;
+ rtctl.adv_rtr.s_addr = rn->adv_rtr.s_addr;
+ rtctl.cost = r->cost;
+ rtctl.cost2 = r->cost2;
+ rtctl.p_type = r->p_type;
+ rtctl.d_type = r->d_type;
+ rtctl.flags = r->flags;
+ rtctl.prefixlen = r->prefixlen;
+ rtctl.uptime = now.tv_sec - rn->uptime;
+
+ rde_imsg_compose_ospfe(IMSG_CTL_SHOW_RIB, 0, pid,
+ &rtctl, sizeof(rtctl));
+ }
+ }
+}
+
+void
+rt_update(struct in_addr prefix, u_int8_t prefixlen, struct v_nexthead *vnh,
+ u_int32_t cost, u_int32_t cost2, struct in_addr area,
+ struct in_addr adv_rtr, enum path_type p_type, enum dst_type d_type,
+ u_int8_t flags, u_int32_t tag)
+{
+ struct rt_node *rte;
+ struct rt_nexthop *rn;
+ int better = 0, equal = 0;
+
+ if (vnh == NULL || TAILQ_EMPTY(vnh)) /* XXX remove */
+ fatalx("rt_update: invalid nexthop");
+
+ if ((rte = rt_find(prefix.s_addr, prefixlen, d_type)) == NULL) {
+ if ((rte = calloc(1, sizeof(struct rt_node))) == NULL)
+ fatal("rt_update");
+
+ TAILQ_INIT(&rte->nexthop);
+ rte->prefix.s_addr = prefix.s_addr;
+ rte->prefixlen = prefixlen;
+ rte->cost = cost;
+ rte->cost2 = cost2;
+ rte->area = area;
+ rte->p_type = p_type;
+ rte->d_type = d_type;
+ rte->flags = flags;
+ rte->ext_tag = tag;
+
+ rt_nexthop_add(rte, vnh, adv_rtr);
+
+ rt_insert(rte);
+ } else {
+ /* order:
+ * 1. intra-area
+ * 2. inter-area
+ * 3. type 1 as ext
+ * 4. type 2 as ext
+ */
+ if (rte->invalid) /* everything is better than invalid */
+ better = 1;
+ else if (p_type < rte->p_type)
+ better = 1;
+ else if (p_type == rte->p_type)
+ switch (p_type) {
+ case PT_INTRA_AREA:
+ case PT_INTER_AREA:
+ if (cost < rte->cost)
+ better = 1;
+ else if (cost == rte->cost &&
+ rte->area.s_addr == area.s_addr)
+ equal = 1;
+ break;
+ case PT_TYPE1_EXT:
+ if (cost < rte->cost)
+ better = 1;
+ else if (cost == rte->cost)
+ equal = 1;
+ break;
+ case PT_TYPE2_EXT:
+ if (cost2 < rte->cost2)
+ better = 1;
+ else if (cost2 == rte->cost2 &&
+ cost < rte->cost)
+ better = 1;
+ else if (cost2 == rte->cost2 &&
+ cost == rte->cost)
+ equal = 1;
+ break;
+ }
+
+ if (!better && !equal)
+ return;
+
+ if (better) {
+ TAILQ_FOREACH(rn, &rte->nexthop, entry)
+ rn->invalid = 1;
+
+ rte->area = area;
+ rte->cost = cost;
+ rte->cost2 = cost2;
+ rte->p_type = p_type;
+ rte->flags = flags;
+ rte->ext_tag = tag;
+ }
+
+ if (equal || better)
+ rt_nexthop_add(rte, vnh, adv_rtr);
+ }
+}
+
+struct rt_node *
+rt_lookup(enum dst_type type, in_addr_t addr)
+{
+ struct rt_node *rn;
+ u_int8_t i = 32;
+
+ if (type == DT_RTR) {
+ rn = rt_find(addr, 32, type);
+ if (rn && rn->invalid == 0)
+ return (rn);
+ return (NULL);
+ }
+
+ /* type == DT_NET */
+ do {
+ if ((rn = rt_find(addr & prefixlen2mask(i), i, type)) &&
+ rn->invalid == 0)
+ return (rn);
+ } while (i-- != 0);
+
+ return (NULL);
+}
+
+/* router LSA links */
+struct lsa_rtr_link *
+get_rtr_link(struct vertex *v, int idx)
+{
+ struct lsa_rtr_link *rtr_link = NULL;
+ char *buf = (char *)v->lsa;
+ u_int16_t i, off, nlinks;
+
+ if (v->type != LSA_TYPE_ROUTER)
+ fatalx("get_rtr_link: invalid LSA type");
+
+ off = sizeof(v->lsa->hdr) + sizeof(struct lsa_rtr);
+
+ /* nlinks validated earlier by lsa_check() */
+ nlinks = lsa_num_links(v);
+ for (i = 0; i < nlinks; i++) {
+ rtr_link = (struct lsa_rtr_link *)(buf + off);
+ if (i == idx)
+ return (rtr_link);
+
+ off += sizeof(struct lsa_rtr_link) +
+ rtr_link->num_tos * sizeof(u_int32_t);
+ }
+
+ return (NULL);
+}
+
+/* network LSA links */
+struct lsa_net_link *
+get_net_link(struct vertex *v, int idx)
+{
+ struct lsa_net_link *net_link = NULL;
+ char *buf = (char *)v->lsa;
+ u_int16_t i, off, nlinks;
+
+ if (v->type != LSA_TYPE_NETWORK)
+ fatalx("get_net_link: invalid LSA type");
+
+ off = sizeof(v->lsa->hdr) + sizeof(u_int32_t);
+
+ /* nlinks validated earlier by lsa_check() */
+ nlinks = lsa_num_links(v);
+ for (i = 0; i < nlinks; i++) {
+ net_link = (struct lsa_net_link *)(buf + off);
+ if (i == idx)
+ return (net_link);
+
+ off += sizeof(struct lsa_net_link);
+ }
+
+ return (NULL);
+}
+
+/* misc */
+int
+linked(struct vertex *w, struct vertex *v)
+{
+ struct lsa_rtr_link *rtr_link = NULL;
+ struct lsa_net_link *net_link = NULL;
+ int i;
+
+ switch (w->type) {
+ case LSA_TYPE_ROUTER:
+ for (i = 0; i < lsa_num_links(w); i++) {
+ rtr_link = get_rtr_link(w, i);
+ switch (v->type) {
+ case LSA_TYPE_ROUTER:
+ if (rtr_link->type == LINK_TYPE_POINTTOPOINT &&
+ rtr_link->id == htonl(v->ls_id))
+ return (1);
+ break;
+ case LSA_TYPE_NETWORK:
+ if (rtr_link->id == htonl(v->ls_id))
+ return (1);
+ break;
+ default:
+ fatalx("spf_calc: invalid type");
+ }
+ }
+ return (0);
+ case LSA_TYPE_NETWORK:
+ for (i = 0; i < lsa_num_links(w); i++) {
+ net_link = get_net_link(w, i);
+ switch (v->type) {
+ case LSA_TYPE_ROUTER:
+ if (net_link->att_rtr == htonl(v->ls_id))
+ return (1);
+ break;
+ default:
+ fatalx("spf_calc: invalid type");
+ }
+ }
+ return (0);
+ default:
+ fatalx("spf_calc: invalid LSA type");
+ }
+
+ return (0);
+}