summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorEsben Norby <norby@cvs.openbsd.org>2007-10-08 10:44:52 +0000
committerEsben Norby <norby@cvs.openbsd.org>2007-10-08 10:44:52 +0000
commit0d1232986fa83ed3facd84218e46f9669b1bb50d (patch)
treeea563e553560720fe6fef08021371caaabe3b898 /usr.sbin
parentf3720901532f9c4a0d5b0e9275018e92f323a627 (diff)
Welcome ospf6d
The new ospf6d daemon will support OSPFv3, basically OSPF for IPv6 networks. It is heavily based on ospfd(8), it is more or less a copy and paste of it. Currently some unneeded stuff has been removed and the trasition from IPv4 to IPv6 has begun. ospf6d is not very usefull at the moment, it is being imported to allow more people to work on it concurrently. Not yet connected to the builds. ok claudio@ dlg@
Diffstat (limited to 'usr.sbin')
-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);
+}