summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/ospfd/Makefile20
-rw-r--r--usr.sbin/ospfd/area.c91
-rw-r--r--usr.sbin/ospfd/auth.c97
-rw-r--r--usr.sbin/ospfd/buffer.c257
-rw-r--r--usr.sbin/ospfd/config.c153
-rw-r--r--usr.sbin/ospfd/control.c286
-rw-r--r--usr.sbin/ospfd/control.h50
-rw-r--r--usr.sbin/ospfd/database.c454
-rw-r--r--usr.sbin/ospfd/hello.c259
-rw-r--r--usr.sbin/ospfd/imsg.c223
-rw-r--r--usr.sbin/ospfd/in_cksum.c90
-rw-r--r--usr.sbin/ospfd/interface.c824
-rw-r--r--usr.sbin/ospfd/iso_cksum.c58
-rw-r--r--usr.sbin/ospfd/kroute.c1270
-rw-r--r--usr.sbin/ospfd/log.c164
-rw-r--r--usr.sbin/ospfd/log.h39
-rw-r--r--usr.sbin/ospfd/lsack.c305
-rw-r--r--usr.sbin/ospfd/lsreq.c259
-rw-r--r--usr.sbin/ospfd/lsupdate.c447
-rw-r--r--usr.sbin/ospfd/neighbor.c698
-rw-r--r--usr.sbin/ospfd/ospf.h203
-rw-r--r--usr.sbin/ospfd/ospfd.869
-rw-r--r--usr.sbin/ospfd/ospfd.c456
-rw-r--r--usr.sbin/ospfd/ospfd.h418
-rw-r--r--usr.sbin/ospfd/ospfe.c725
-rw-r--r--usr.sbin/ospfd/ospfe.h286
-rw-r--r--usr.sbin/ospfd/packet.c288
-rw-r--r--usr.sbin/ospfd/parse.y878
-rw-r--r--usr.sbin/ospfd/rde.c555
-rw-r--r--usr.sbin/ospfd/rde.h81
-rw-r--r--usr.sbin/ospfd/rde_lsdb.c490
31 files changed, 10493 insertions, 0 deletions
diff --git a/usr.sbin/ospfd/Makefile b/usr.sbin/ospfd/Makefile
new file mode 100644
index 00000000000..6d77c3006fc
--- /dev/null
+++ b/usr.sbin/ospfd/Makefile
@@ -0,0 +1,20 @@
+# $OpenBSD: Makefile,v 1.1 2005/01/28 14:05:40 claudio Exp $
+
+
+PROG= ospfd
+SRCS= area.c auth.c buffer.c config.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 ospfd.c ospfe.c packet.c \
+ parse.y rde.c rde_lsdb.c
+
+MAN= ospfd.8
+
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+YFLAGS=
+LDADD+= -levent
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ospfd/area.c b/usr.sbin/ospfd/area.c
new file mode 100644
index 00000000000..3fbec52532d
--- /dev/null
+++ b/usr.sbin/ospfd/area.c
@@ -0,0 +1,91 @@
+/* $OpenBSD: area.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <stdlib.h>
+
+#include "ospf.h"
+#include "ospfd.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;
+
+ log_debug("area_del: area ID %s", inet_ntoa(area->id));
+
+ /* 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);
+ RB_REMOVE(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 = NULL;
+
+ LIST_FOREACH(area, &conf->area_list, entry) {
+ if (area->id.s_addr == area_id.s_addr) {
+ return (area);
+ }
+ }
+
+ log_debug("area_find: area ID %s not found", inet_ntoa(area_id));
+ return (NULL);
+}
diff --git a/usr.sbin/ospfd/auth.c b/usr.sbin/ospfd/auth.c
new file mode 100644
index 00000000000..67cd52a1472
--- /dev/null
+++ b/usr.sbin/ospfd/auth.c
@@ -0,0 +1,97 @@
+/* $OpenBSD: auth.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 <string.h>
+
+#include "ospfd.h"
+#include "ospf.h"
+#include "log.h"
+#include "ospfe.h"
+
+int
+auth_validate(struct ospf_hdr *pkt, const struct iface *iface)
+{
+ if (ntohs(pkt->auth_type) != (u_int16_t)iface->auth_type) {
+ log_debug("auth_validate: wrong auth type, interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ switch (iface->auth_type) {
+ case AUTH_NONE:
+ break;
+ case AUTH_SIMPLE:
+ if (bcmp(pkt->auth_key.simple, iface->auth_key,
+ sizeof(pkt->auth_key.simple))) {
+ log_debug("auth_validate: wrong password, interface %s",
+ iface->name);
+ return (-1);
+ }
+ /* clear the key before chksum */
+ bzero(pkt->auth_key.simple,
+ sizeof(pkt->auth_key.simple));
+ break;
+ case AUTH_CRYPT:
+ log_debug("auth_validate: not supported, interface %s",
+ iface->name);
+ return (-1);
+ default:
+ log_debug("auth_validate: unknown auth type, interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ if (in_cksum(pkt, ntohs(pkt->len))) {
+ log_debug("recv_packet: invalid checksum, interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+auth_gen(void *buf, u_int16_t len, const struct iface *iface)
+{
+ struct ospf_hdr *ospf_hdr = buf;
+
+ /* update length, and checksum */
+ ospf_hdr->len = htons(len);
+ ospf_hdr->chksum = in_cksum(buf, len);
+
+ switch (iface->auth_type) {
+ case AUTH_NONE:
+ break;
+ case AUTH_SIMPLE:
+ strncpy(ospf_hdr->auth_key.simple, iface->auth_key,
+ sizeof(ospf_hdr->auth_key.simple));
+ break;
+ case AUTH_CRYPT:
+ log_debug("auth_gen: not supported, interface %s",
+ iface->name);
+ return (-1);
+ default:
+ log_debug("auth_gen: unknown auth type, interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/ospfd/buffer.c b/usr.sbin/ospfd/buffer.c
new file mode 100644
index 00000000000..f8fa58797d6
--- /dev/null
+++ b/usr.sbin/ospfd/buffer.c
@@ -0,0 +1,257 @@
+/* $OpenBSD: buffer.c,v 1.1 2005/01/28 14:05:40 claudio 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/socket.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ospfd.h"
+
+void buf_enqueue(struct msgbuf *, struct buf *);
+void buf_dequeue(struct msgbuf *, struct buf *);
+
+struct buf *
+buf_open(ssize_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 = len;
+ buf->fd = -1;
+
+ return (buf);
+}
+
+int
+buf_add(struct buf *buf, void *data, ssize_t len)
+{
+ if (buf->wpos + len > buf->size)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+void *
+buf_reserve(struct buf *buf, ssize_t len)
+{
+ void *b;
+
+ if (buf->wpos + len > buf->size)
+ return (NULL);
+
+ b = buf->buf + buf->wpos;
+ buf->wpos += len;
+ return (b);
+}
+
+int
+buf_close(struct msgbuf *msgbuf, struct buf *buf)
+{
+ buf_enqueue(msgbuf, buf);
+ return (1);
+}
+
+int
+buf_write(int sock, struct buf *buf)
+{
+ ssize_t n;
+
+ if ((n = write(sock, buf->buf + buf->rpos,
+ buf->size - buf->rpos)) == -1) {
+ if (errno == EAGAIN || errno == ENOBUFS) /* try later */
+ return (0);
+ else
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (-2);
+ }
+
+ if (n < buf->size - buf->rpos) { /* not all data written yet */
+ buf->rpos += n;
+ return (0);
+ } else
+ 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)
+{
+ /*
+ * possible race here
+ * when we cannot write out data completely from a buffer,
+ * we MUST return and NOT try to write out stuff from later buffers -
+ * the socket might have become writeable again
+ */
+ struct iovec iov[IOV_MAX];
+ struct buf *buf, *next;
+ int i = 0;
+ ssize_t n;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ 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++;
+ if (buf->fd != -1)
+ break;
+ }
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = i;
+
+ if (buf != NULL && buf->fd != -1) {
+ msg.msg_control = (caddr_t)cmsgbuf;
+ msg.msg_controllen = CMSG_LEN(sizeof(int));
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = buf->fd;
+ }
+
+ if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+ if (errno == EAGAIN || errno == ENOBUFS) /* 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 (n >= buf->size - buf->rpos) {
+ n -= buf->size - buf->rpos;
+ buf_dequeue(msgbuf, buf);
+ } else {
+ buf->rpos += n;
+ n = 0;
+ }
+ }
+
+ return (0);
+}
+
+int
+msgbuf_writebound(struct msgbuf *msgbuf)
+{
+ /*
+ * possible race here
+ * when we cannot write out data completely from a buffer,
+ * we MUST return and NOT try to write out stuff from later buffers -
+ * the socket might have become writeable again
+ */
+ struct buf *buf;
+ int n;
+
+ if (!msgbuf_unbounded(msgbuf))
+ return (1);
+
+ buf = TAILQ_FIRST(&msgbuf->bufs);
+ if ((n = buf_write(msgbuf->fd, buf)) < 0)
+ return (n);
+
+ if (n == 1) { /* everything written out */
+ buf_dequeue(msgbuf, buf);
+ return (1);
+ } else
+ return (0);
+}
+
+int
+msgbuf_unbounded(struct msgbuf *msgbuf)
+{
+ struct buf *buf;
+
+ /* return 1 if last buffer was not completely written. */
+ buf = TAILQ_FIRST(&msgbuf->bufs);
+ if (buf != NULL && buf->rpos != 0)
+ return (1);
+ else
+ 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);
+
+ if (buf->fd != -1)
+ close(buf->fd);
+
+ msgbuf->queued--;
+ buf_free(buf);
+}
diff --git a/usr.sbin/ospfd/config.c b/usr.sbin/ospfd/config.c
new file mode 100644
index 00000000000..5db51be9084
--- /dev/null
+++ b/usr.sbin/ospfd/config.c
@@ -0,0 +1,153 @@
+/* $OpenBSD: config.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 "ospf.h"
+#include "ospfd.h"
+#include "ospfe.h"
+#include "log.h"
+
+void show_db_sum(struct lsa_hdr *);
+void show_neighbor(struct nbr *);
+void show_interface(struct iface *);
+void show_area(struct area *);
+
+extern char *__progname;
+
+void
+show_db_sum(struct lsa_hdr *db_sum)
+{
+
+ log_debug(" age %d", db_sum->age);
+ log_debug(" opts %d", db_sum->opts);
+ log_debug(" type %d", db_sum->type);
+ log_debug(" ls_id %s", (db_sum->ls_id));
+ log_debug(" adv_rtr %s", (db_sum->adv_rtr));
+ log_debug(" seq_num 0x%x", db_sum->seq_num);
+ log_debug(" chksum 0x%x", db_sum->ls_chksum);
+ log_debug(" len %d", db_sum->len);
+}
+
+void
+show_neighbor(struct nbr *nbr)
+{
+ struct lsa_entry *lsa_entry = NULL;
+
+ log_debug(" state: %s", nbr_state_name(nbr->state));
+ log_debug(" inactivity timer: ");
+ log_debug(" master: %d", nbr->master);
+ log_debug(" dd seq num: %d", nbr->dd_seq_num);
+ log_debug(" last rx options: %d", nbr->last_rx_options);
+ log_debug(" id: %s", inet_ntoa(nbr->id));
+ log_debug(" priority: %d", nbr->priority);
+ log_debug(" address: %s", inet_ntoa(nbr->addr));
+ log_debug(" options: %d", nbr->options);
+ log_debug(" dr: %s", inet_ntoa(nbr->dr));
+ log_debug(" bdr: %s", inet_ntoa(nbr->bdr));
+
+ log_debug(" ls retrans: ");
+
+ log_debug(" db sum list: ");
+ TAILQ_FOREACH(lsa_entry, &nbr->db_sum_list, entry) {
+ show_db_sum(lsa_entry->le_lsa);
+ }
+
+ log_debug(" ls request: ");
+ TAILQ_FOREACH(lsa_entry, &nbr->ls_req_list, entry) {
+ show_db_sum(lsa_entry->le_lsa);
+ }
+}
+
+void
+show_interface(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+
+ log_debug(" interface: %s", iface->name);
+ log_debug(" type: %s", if_type_name(iface->type));
+ log_debug(" state: %s", if_state_name(iface->state));
+ log_debug(" address: %s", inet_ntoa(iface->addr));
+ log_debug(" mask: %s", inet_ntoa(iface->mask));
+ log_debug(" area: %s", inet_ntoa(iface->area->id));
+ log_debug(" hello interval: %d", iface->hello_interval);
+ log_debug(" dead interval: %d", iface->dead_interval);
+ log_debug(" transfer delay: %d", iface->transfer_delay);
+ log_debug(" priority: %d", iface->priority);
+ log_debug(" hello timer: ");
+ log_debug(" wait timer: ");
+ log_debug(" neighbor:");
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ show_neighbor(nbr);
+ }
+
+ log_debug(" dr: ");
+ log_debug(" bdr: ");
+ log_debug(" metric: %d", iface->metric);
+ log_debug(" rxmt interval: %d", iface->rxmt_interval);
+ log_debug(" auth type: %s", if_auth_name(iface->auth_type));
+ if (iface->auth_type == AUTH_TYPE_SIMPLE) {
+ log_debug(" auth key: '%s'", iface->auth_key);
+ } else {
+ log_debug(" auth key:" );
+ }
+
+ log_debug(" mtu: %d", iface->mtu);
+ log_debug(" fd: %d", iface->fd);
+ log_debug(" passive: %d", iface->passive);
+ log_debug(" ifindex: %d", iface->ifindex);
+}
+
+void
+show_area(struct area *area)
+{
+ struct iface *iface = NULL;
+
+ log_debug("area: %s", inet_ntoa(area->id));
+
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ show_interface(iface);
+ }
+
+ log_debug(" transit: %d", area->transit);
+ log_debug(" stub: %d", area->stub);
+ log_debug(" stub default cost: %d", area->stub_default_cost);
+}
+
+void
+show_config(struct ospfd_conf *xconf)
+{
+ struct area *area = NULL;
+
+ log_debug("--------------------------------------------------------");
+ log_debug("dumping %s configuration", __progname);
+ log_debug("--------------------------------------------------------");
+
+ log_debug("router-id: %s", inet_ntoa(xconf->rtr_id));
+ log_debug("ospf socket: %d", xconf->ospf_socket);
+
+ LIST_FOREACH(area, &xconf->area_list, entry) {
+ show_area(area);
+ }
+ log_debug("--------------------------------------------------------");
+}
diff --git a/usr.sbin/ospfd/control.c b/usr.sbin/ospfd/control.c
new file mode 100644
index 00000000000..c426d270a98
--- /dev/null
+++ b/usr.sbin/ospfd/control.c
@@ -0,0 +1,286 @@
+/* $OpenBSD: control.c,v 1.1 2005/01/28 14:05:40 claudio 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 "ospfd.h"
+#include "ospf.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);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, OSPFD_SOCKET, sizeof(sun.sun_path));
+
+ if (unlink(OSPFD_SOCKET) == -1)
+ if (errno != ENOENT) {
+ log_warn("control_init: unlink %s", OSPFD_SOCKET);
+ close(fd);
+ return (-1);
+ }
+
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("control_init: bind: %s", OSPFD_SOCKET);
+ close(fd);
+ return (-1);
+ }
+
+ if (chmod(OSPFD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("control_init: chmod");
+ close(fd);
+ return (-1);
+ }
+
+ umask(old_umask);
+
+ 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(OSPFD_SOCKET);
+}
+
+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);
+}
+
+void
+control_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct ctl_conn *c;
+ struct imsg imsg;
+ int n;
+ unsigned int ifidx;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warn("control_dispatch_imsg: fd %d: not found", fd);
+ return;
+ }
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(&c->ibuf)) <= 0) {
+ control_close(fd);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&c->ibuf.w) < 0) {
+ control_close(fd);
+ return;
+ }
+ imsg_event_add(&c->ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
+ control_close(fd);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_FIB_COUPLE:
+ case IMSG_CTL_FIB_DECOUPLE:
+ case IMSG_CTL_RELOAD:
+ ospfe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
+ 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, -1, NULL, 0);
+ }
+ break;
+ case IMSG_CTL_SHOW_DATABASE:
+ 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) {
+ log_debug("control_imsg_relay: no connection pid");
+ return (0);
+ }
+
+ return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
+ imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
+}
+
+void
+session_socket_blockmode(int fd, enum blockmodes bm)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fnctl F_GETFL");
+
+ if (bm == BM_NONBLOCK)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fnctl F_SETFL");
+}
diff --git a/usr.sbin/ospfd/control.h b/usr.sbin/ospfd/control.h
new file mode 100644
index 00000000000..47947f01593
--- /dev/null
+++ b/usr.sbin/ospfd/control.h
@@ -0,0 +1,50 @@
+/* $OpenBSD: control.h,v 1.1 2005/01/28 14:05:40 claudio 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 _CONTORL_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/ospfd/database.c b/usr.sbin/ospfd/database.c
new file mode 100644
index 00000000000..8d893b75cf1
--- /dev/null
+++ b/usr.sbin/ospfd/database.c
@@ -0,0 +1,454 @@
+/* $OpenBSD: database.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 <string.h>
+#include <unistd.h>
+
+#include "ospfd.h"
+#include "ospf.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_in dst;
+ struct db_dscrp_hdr *dd_hdr;
+ struct lsa_hdr *lsa_hdr;
+ struct lsa_entry *le, *nle;
+ char *buf;
+ char *ptr;
+ int ret = 0;
+
+ log_debug("send_db_description: neighbor ID %s, seq_num %x",
+ inet_ntoa(nbr->id), nbr->dd_seq_num);
+
+ if (nbr->iface->passive)
+ return (0);
+
+ if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("send_db_description");
+
+ /* OSPF header */
+ gen_ospf_hdr(ptr, nbr->iface, PACKET_TYPE_DD);
+ ptr += sizeof(struct ospf_hdr);
+
+ /* database description header */
+ dd_hdr = (struct db_dscrp_hdr *)ptr;
+ dd_hdr->opts = oeconf->options;
+ dd_hdr->dd_seq_num = htonl(nbr->dd_seq_num);
+
+ ptr += sizeof(*dd_hdr);
+
+ 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:
+ log_debug("send_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ nbr->options |= OSPF_DBD_MS | OSPF_DBD_M | OSPF_DBD_I;
+ break;
+ case NBR_STA_XCHNG:
+ log_debug("send_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ 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 */
+ lsa_hdr = (struct lsa_hdr *)ptr;
+
+ for (le = TAILQ_FIRST(&nbr->db_sum_list); (le != NULL) &&
+ ((ptr - buf) < nbr->iface->mtu - PACKET_HDR); le = nle) {
+ nbr->dd_end = nle = TAILQ_NEXT(le, entry);
+ memcpy(ptr, le->le_lsa, sizeof(struct lsa_hdr));
+ ptr += sizeof(*lsa_hdr);
+ }
+ break;
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ log_debug("send_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ 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:
+ log_debug("send_db_description: unknown neighbor state, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ ret = -1;
+ goto done;
+ break;
+ }
+
+ /* set destination */
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+
+ switch (nbr->iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_aton(AllSPFRouters, &dst.sin_addr);
+ dd_hdr->iface_mtu = htons(nbr->iface->mtu);
+ break;
+ case IF_TYPE_BROADCAST:
+ dst.sin_addr = nbr->addr;
+ dd_hdr->iface_mtu = htons(nbr->iface->mtu);
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ dst.sin_addr = nbr->addr;
+ dd_hdr->iface_mtu = 0;
+ break;
+ default:
+ fatalx("send_db_description: unknown interface type");
+ }
+
+ dd_hdr->bits = nbr->options;
+
+ /* update authentication and calculate checksum */
+ auth_gen(buf, ptr - buf, nbr->iface);
+
+ /* transmit packet */
+ if ((ret = send_packet(nbr->iface, buf, (ptr - buf), &dst)) == -1)
+ log_warnx("send_db_description: error sending packet on "
+ "interface %s", nbr->iface->name);
+
+done:
+ free(buf);
+
+ return (ret);
+}
+
+void
+recv_db_description(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct db_dscrp_hdr dd_hdr;
+ int dupe = 0;
+
+ log_debug("recv_db_description: neighbor ID %s, seq_num %x",
+ inet_ntoa(nbr->id), nbr->dd_seq_num);
+
+ 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, neighbor ID %s",
+ inet_ntoa(nbr->id));
+ 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;
+ }
+
+ log_debug("recv_db_description: seq_num %x", ntohl(dd_hdr.dd_seq_num));
+ 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:
+ log_debug("recv_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ /* evaluate dr and bdr before issuing a 2-Way event */
+ if_fsm(nbr->iface, IF_EVT_NBR_CHNG);
+ nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
+ if (nbr->state != NBR_STA_XSTRT)
+ return;
+ /* FALLTHROUGH */
+ case NBR_STA_XSTRT:
+ log_debug("recv_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ if (dupe)
+ return;
+ /*
+ * check bits: either I,M,MS or only M
+ * I,M,MS is checked here the M only case is a fall-through
+ */
+ 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(nbr->iface->rtr_id.s_addr)) {
+ /* slave */
+ log_debug("recv_db_description: slave, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ nbr->master = false;
+ 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_M) {
+ /* 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)
+ 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:
+ log_debug("recv_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ 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 LSA's to request */
+ if (len > 0)
+ 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))
+ 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;
+ return;
+}
+
+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 */
+void
+db_tx_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+ struct timeval tv;
+
+ log_debug("db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+
+ 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_LOAD:
+ return ;
+ case NBR_STA_XSTRT:
+ case NBR_STA_XCHNG:
+ 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;
+ log_debug("db_tx_timer: reschedule neighbor ID %s",
+ inet_ntoa(nbr->id));
+ evtimer_add(&nbr->db_tx_timer, &tv);
+ }
+}
+
+int
+start_db_tx_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ log_debug("start_db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+
+ timerclear(&tv);
+
+ return (evtimer_add(&nbr->db_tx_timer, &tv));
+}
+
+int
+stop_db_tx_timer(struct nbr *nbr)
+{
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ log_debug("stop_db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return (evtimer_del(&nbr->db_tx_timer));
+}
diff --git a/usr.sbin/ospfd/hello.c b/usr.sbin/ospfd/hello.c
new file mode 100644
index 00000000000..25506b19125
--- /dev/null
+++ b/usr.sbin/ospfd/hello.c
@@ -0,0 +1,259 @@
+/* $OpenBSD: hello.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 "ospfd.h"
+#include "ospf.h"
+#include "log.h"
+#include "ospfe.h"
+
+extern struct ospfd_conf *oeconf;
+
+/* hello packet handling */
+int
+send_hello(struct iface *iface)
+{
+ struct sockaddr_in dst;
+ struct hello_hdr *hello;
+ struct nbr *nbr;
+ char *buf;
+ char *ptr;
+ int ret = 0;
+
+ if (iface->passive)
+ return (0);
+
+ /* XXX use buffer API instead for better decoupling */
+ if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("send_hello");
+
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ inet_aton(AllSPFRouters, &dst.sin_addr);
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ /* XXX these networks need to be preconfigured */
+ /* dst.sin_addr.s_addr = nbr->addr.s_addr; */
+ inet_aton(AllSPFRouters, &dst.sin_addr);
+ break;
+ default:
+ fatalx("send_hello: unknown interface type");
+ }
+
+ /* OSPF header */
+ gen_ospf_hdr(ptr, iface, PACKET_TYPE_HELLO);
+ ptr += sizeof(struct ospf_hdr);
+
+ /* hello header */
+ hello = (struct hello_hdr *)ptr;
+ hello->mask = iface->mask.s_addr;
+ hello->hello_interval = htons(iface->hello_interval);
+ hello->opts = oeconf->options;
+ hello->rtr_priority = iface->priority;
+ hello->rtr_dead_interval = htonl(iface->dead_interval);
+
+ if (iface->dr)
+ hello->d_rtr = iface->dr->addr.s_addr;
+ if (iface->bdr)
+ hello->bd_rtr = iface->bdr->addr.s_addr;
+ ptr += sizeof(*hello);
+
+ /* active neighbor(s) */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (ptr - buf > iface->mtu - PACKET_HDR) {
+ log_warnx("send_hello: too many neighbors on "
+ "interface %s", iface->name);
+ break;
+ }
+ if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self)) {
+ memcpy(ptr, &nbr->id, sizeof(nbr->id));
+ ptr += sizeof(nbr->id);
+ }
+ }
+
+ /* update authentication and calculate checksum */
+ auth_gen(buf, ptr - buf, iface);
+
+ if ((ret = send_packet(iface, buf, (ptr - buf), &dst)) == -1)
+ log_warnx("send_hello: error sending packet on "
+ "interface %s", iface->name);
+
+ free(buf);
+ return (ret);
+}
+
+void
+recv_hello(struct iface *iface, struct in_addr src, u_int32_t rtr_id, char *buf,
+ u_int16_t len)
+{
+ struct hello_hdr hello;
+ struct nbr *nbr = NULL;
+ u_int32_t nbr_id;
+ int twoway = 0, 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 (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;
+ }
+
+ 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.opts & OSPF_OPTION_E && iface->area->stub) ||
+ ((hello.opts & OSPF_OPTION_E) == 0 && !iface->area->stub)) {
+ 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;
+ if (nbr->addr.s_addr == src.s_addr)
+ break;
+ }
+ break;
+ default:
+ fatalx("recv_hello: unknown interface type");
+ }
+
+ if (!nbr)
+ nbr = nbr_new(rtr_id, iface, 0);
+ /* actually the neighbor address shouldn't be stored on virtual links */
+ nbr->addr.s_addr = src.s_addr;
+ nbr->options = hello.opts;
+
+ nbr_fsm(nbr, NBR_EVT_HELLO_RCVD);
+
+ while (len >= sizeof(nbr_id)) {
+ memcpy(&nbr_id, buf, sizeof(nbr_id));
+ if (nbr_id == iface->rtr_id.s_addr) {
+ /* seen myself */
+ if (nbr->state < NBR_STA_XSTRT)
+ twoway = 1;
+ 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 &&
+ ((nbr->dr.s_addr == nbr->addr.s_addr &&
+ nbr->bdr.s_addr == 0) || nbr->bdr.s_addr == nbr->addr.s_addr))
+ if_fsm(iface, IF_EVT_BACKUP_SEEN);
+
+ 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;
+
+ nbr->dr.s_addr = hello.d_rtr;
+ nbr->bdr.s_addr = hello.bd_rtr;
+
+ if (twoway) {
+ /*
+ * event 2 way recieved needs to be delayed after the
+ * interface neighbor change check else the DR and BDR
+ * may not be set correctly.
+ */
+ nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
+ }
+
+ if (nbr_change)
+ if_fsm(iface, IF_EVT_NBR_CHNG);
+
+ /* TODO NBMA needs some special handling */
+ return;
+}
diff --git a/usr.sbin/ospfd/imsg.c b/usr.sbin/ospfd/imsg.c
new file mode 100644
index 00000000000..b8634ebd62e
--- /dev/null
+++ b/usr.sbin/ospfd/imsg.c
@@ -0,0 +1,223 @@
+/* $OpenBSD: imsg.c,v 1.1 2005/01/28 14:05:40 claudio 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/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ospfd.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);
+}
+
+int
+imsg_read(struct imsgbuf *ibuf)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cmsgbuf[CMSG_SPACE(sizeof(int) * 16)];
+ struct iovec iov;
+ ssize_t n;
+ int fd;
+ struct imsg_fd *ifd;
+
+ bzero(&msg, sizeof(msg));
+ iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
+ iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
+ if (errno != EINTR && errno != EAGAIN) {
+ log_warn("imsg_read: pipe read error");
+ return (-1);
+ }
+ return (0);
+ }
+
+ ibuf->r.wpos += n;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ fd = (*(int *)CMSG_DATA(cmsg));
+ if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
+ fatal("imsg_read");
+ ifd->fd = fd;
+ TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry);
+ } else
+ log_warn("imsg_read: got unexpected ctl data level %d "
+ "type %d", cmsg->cmsg_level, cmsg->cmsg_type);
+ }
+
+ return (n);
+}
+
+int
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+ ssize_t datalen = 0;
+ size_t av, left;
+
+ 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;
+ memcpy(&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, int fd, 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);
+
+ wbuf->fd = fd;
+
+ if ((n = imsg_close(ibuf, wbuf)) < 0)
+ return (-1);
+
+ return (n);
+}
+
+struct buf *
+imsg_create(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid,
+ pid_t pid, u_int16_t datalen)
+{
+ struct buf *wbuf;
+ struct imsg_hdr hdr;
+
+ if (datalen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
+ log_warnx("imsg_create_core: len %u > MAX_IMSGSIZE; "
+ "type %u peerid %lu", datalen + IMSG_HEADER_SIZE,
+ type, peerid);
+ return (NULL);
+ }
+
+ hdr.len = datalen + IMSG_HEADER_SIZE;
+ hdr.type = type;
+ hdr.peerid = peerid;
+ hdr.pid = pid;
+ if ((wbuf = buf_open(hdr.len)) == NULL) {
+ log_warn("imsg_create: buf_open");
+ return (NULL);
+ }
+ if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+ return (NULL);
+
+ return (wbuf);
+}
+
+int
+imsg_add(struct buf *msg, void *data, u_int16_t datalen)
+{
+ if (datalen)
+ if (buf_add(msg, data, datalen) == -1) {
+ log_warnx("imsg_add: buf_add error");
+ buf_free(msg);
+ return (-1);
+ }
+ return (datalen);
+}
+
+int
+imsg_close(struct imsgbuf *ibuf, struct buf *msg)
+{
+ int n;
+
+ if ((n = buf_close(&ibuf->w, msg)) < 0) {
+ log_warnx("imsg_close: buf_close error");
+ buf_free(msg);
+ return (-1);
+ }
+ imsg_event_add(ibuf);
+
+ return (n);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+ free(imsg->data);
+}
+
+int
+imsg_get_fd(struct imsgbuf *ibuf)
+{
+ int fd;
+ struct imsg_fd *ifd;
+
+ if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
+ return (-1);
+
+ fd = ifd->fd;
+ TAILQ_REMOVE(&ibuf->fds, ifd, entry);
+ free(ifd);
+
+ return (fd);
+}
diff --git a/usr.sbin/ospfd/in_cksum.c b/usr.sbin/ospfd/in_cksum.c
new file mode 100644
index 00000000000..3bb43c80718
--- /dev/null
+++ b/usr.sbin/ospfd/in_cksum.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: in_cksum.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+/* $NetBSD: in_cksum.c,v 1.3 1995/04/22 13:53:48 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL)
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+
+#include "ospfd.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.
+ */
+int
+in_cksum(void *p, int len)
+{
+ int sum = 0, oddbyte = 0, v = 0;
+ u_char *cp = p;
+
+ /* we assume < 2^16 bytes being summed */
+ while (len > 0) {
+ if (oddbyte) {
+ sum += v + *cp++;
+ len--;
+ }
+ if (((long)cp & 1) == 0) {
+ while ((len -= 2) >= 0) {
+ sum += *(u_short *)cp;
+ cp += 2;
+ }
+ } else {
+ while ((len -= 2) >= 0) {
+ sum += *cp++ << 8;
+ sum += *cp++;
+ }
+ }
+ if ((oddbyte = len & 1) != 0)
+ v = *cp << 8;
+ }
+ if (oddbyte)
+ sum += v;
+ sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */
+ sum += sum >> 16; /* add potential last carry */
+ return (0xffff & ~sum);
+}
diff --git a/usr.sbin/ospfd/interface.c b/usr.sbin/ospfd/interface.c
new file mode 100644
index 00000000000..4120c537e17
--- /dev/null
+++ b/usr.sbin/ospfd/interface.c
@@ -0,0 +1,824 @@
+/* $OpenBSD: interface.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 <unistd.h>
+#include <string.h>
+#include <event.h>
+
+#include "ospfd.h"
+#include "ospf.h"
+#include "log.h"
+#include "ospfe.h"
+
+void if_hello_timer(int, short, void *);
+int if_start_hello_timer(struct iface *);
+int if_stop_hello_timer(struct iface *);
+int if_stop_wait_timer(struct iface *);
+void if_wait_timer(int, short, void *);
+int if_start_wait_timer(struct iface *);
+int if_stop_wait_timer(struct iface *);
+struct nbr *if_elect(struct nbr *, struct nbr *);
+int if_set_mcast_ttl(int, u_int8_t);
+int if_set_tos(int, int);
+int if_set_mcast_loop(int);
+
+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_WAITING, IF_EVT_NBR_CHNG, 0, 0},
+ {IF_STA_MULTI, IF_EVT_NBR_CHNG, IF_ACT_ELECT, 0},
+ {IF_STA_ANY, IF_EVT_NBR_CHNG, 0, 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, 0, IF_STA_DOWN},
+ {-1, 0, -1, 0},
+};
+
+const char * const if_action_names[] = {
+ "START",
+ "ELECT",
+ "RESET"
+};
+
+const char * const if_type_names[] = {
+ "POINTOPOINT",
+ "BROADCAST",
+ "NBMA",
+ "POINTOMULTIPOINT",
+ "VIRTUALLINK"
+};
+
+const char * const if_auth_names[] = {
+ "NONE",
+ "SIMPLE",
+ "CRYPT"
+};
+
+int
+if_fsm(struct iface *iface, enum iface_event event)
+{
+ int old_state;
+ int new_state = 0;
+ int i, ret = 0;
+
+ old_state = iface->state;
+
+ for (i = 0; iface_fsm[i].state != -1; i++)
+ if ((iface_fsm[i].state & old_state) &&
+ (iface_fsm[i].event == event)) {
+ new_state = iface_fsm[i].new_state;
+ break;
+ }
+
+ if (iface_fsm[i].state == -1) {
+ /* XXX event outside of the defined fsm, ignore it. */
+ log_debug("fsm_if: interface %s, "
+ "event %s not expected in state %s", iface->name,
+ nbr_event_name(event), if_state_name(old_state));
+ return (0);
+ }
+
+ switch (iface_fsm[i].action) {
+ case IF_ACT_STRT:
+ ret = if_act_start(iface);
+ break;
+ case IF_ACT_ELECT:
+ ret = if_act_elect(iface);
+ break;
+ case IF_ACT_RST:
+ ret = if_act_reset(iface);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ if (ret) {
+ log_debug("fsm_if: error changing state for interface %s, "
+ "event %s, state %s", iface->name, if_event_name(event),
+ if_state_name(old_state));
+ return (-1);
+ }
+
+ if (new_state != 0)
+ iface->state = new_state;
+
+ log_debug("fsm_if: event %s resulted in action %s and changing "
+ "state for interface %s from %s to %s",
+ if_event_name(event), if_action_name(iface_fsm[i].action),
+ iface->name, if_state_name(old_state), if_state_name(iface->state));
+
+ return (ret);
+}
+
+struct iface *
+if_new(char *name, unsigned int idx)
+{
+ struct sockaddr_in *sain;
+ struct iface *iface = NULL;
+ struct ifreq *ifr;
+ int s;
+
+ if ((iface = calloc(1, sizeof(*iface))) == NULL)
+ errx(1, "if_new: calloc");
+
+ iface->state = IF_STA_DOWN;
+ iface->passive = true;
+
+ LIST_INIT(&iface->nbr_list);
+ TAILQ_INIT(&iface->ls_ack_list);
+
+ evtimer_set(&iface->lsack_tx_timer, ls_ack_tx_timer, iface);
+
+ strlcpy(iface->name, name, sizeof(iface->name));
+
+ if ((ifr = calloc(1, sizeof(*ifr))) == NULL)
+ errx(1, "if_new: calloc");
+
+ /* set up ifreq */
+ strlcpy(ifr->ifr_name, name, sizeof(ifr->ifr_name));
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ errx(1, "if_new: socket");
+
+ /* get type */
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)ifr) < 0)
+ errx(1, "if_new: cannot get type");
+ if ((ifr->ifr_flags & IFF_POINTOPOINT))
+ iface->type = IF_TYPE_POINTOPOINT;
+
+ if ((ifr->ifr_flags & IFF_BROADCAST) &&
+ (ifr->ifr_flags & IFF_MULTICAST))
+ iface->type = IF_TYPE_BROADCAST;
+
+ iface->flags = ifr->ifr_flags;
+
+ /* get address */
+ if (ioctl(s, SIOCGIFADDR, (caddr_t)ifr) < 0)
+ errx(1, "if_new: cannot get address");
+ sain = (struct sockaddr_in *) &ifr->ifr_addr;
+ iface->addr = sain->sin_addr;
+
+ /* get mask */
+ if (ioctl(s, SIOCGIFNETMASK, (caddr_t)ifr) < 0)
+ errx(1, "if_new: cannot get mask");
+ sain = (struct sockaddr_in *) &ifr->ifr_addr;
+ iface->mask = sain->sin_addr;
+
+ /* get mtu */
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)ifr) < 0)
+ errx(1, "if_new: cannot get mtu");
+
+ iface->mtu = ifr->ifr_mtu;
+ iface->ifindex = idx;
+
+ /* set event handlers for interface */
+ evtimer_set(&iface->hello_timer, if_hello_timer, iface);
+ evtimer_set(&iface->wait_timer, if_wait_timer, iface);
+
+ free(ifr);
+ close(s);
+
+ return (iface);
+}
+
+int
+if_del(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+
+ log_debug("if_del: interface %s", iface->name);
+
+ /* clear lists etc */
+ iface->self = NULL; /* trick neighbor.c code to remove self too */
+ while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) {
+ LIST_REMOVE(nbr, entry);
+ nbr_del(nbr);
+ }
+
+ ls_ack_list_clr(iface);
+
+ return (-1);
+}
+
+int
+if_init(struct ospfd_conf *xconf)
+{
+ struct area *area = NULL;
+ struct iface *iface = NULL;
+
+ if ((xconf->ospf_socket = socket(AF_INET, SOCK_RAW,
+ IPPROTO_OSPF)) == -1) {
+ log_warn("if_init: error creating socket");
+ return (-1);
+ }
+
+ /* set some defaults */
+ if (if_set_mcast_ttl(xconf->ospf_socket,
+ IP_DEFAULT_MULTICAST_TTL) == -1)
+ return (-1);
+
+ if (if_set_mcast_loop(xconf->ospf_socket) == -1)
+ return (-1);
+
+ if (if_set_tos(xconf->ospf_socket, IPTOS_PREC_INTERNETCONTROL) == -1)
+ return (-1);
+
+
+ LIST_FOREACH(area, &xconf->area_list, entry) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ break;
+ case IF_TYPE_BROADCAST:
+ /* all bcast interfaces use the same socket */
+ iface->fd = xconf->ospf_socket;
+ break;
+ case IF_TYPE_NBMA:
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ break;
+ case IF_TYPE_VIRTUALLINK:
+ break;
+ default:
+ fatalx("if_init: unknown interface type");
+ }
+ }
+ }
+
+ return (0);
+}
+
+int
+if_shutdown(struct ospfd_conf *xconf)
+{
+ int ret = 0;
+
+ ret = close(xconf->ospf_socket);
+
+ return (ret);
+}
+
+/* timers */
+void
+if_hello_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+ struct timeval tv;
+
+ send_hello(iface);
+
+ /* reschedule hello_timer */
+ if (!iface->passive) {
+ timerclear(&tv);
+ tv.tv_sec = iface->hello_interval;
+ evtimer_add(&iface->hello_timer, &tv);
+ }
+}
+
+int
+if_start_hello_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ return (evtimer_add(&iface->hello_timer, &tv));
+}
+
+int
+if_stop_hello_timer(struct iface *iface)
+{
+ return (evtimer_del(&iface->hello_timer));
+}
+
+void
+if_wait_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+
+ if_fsm(iface, IF_EVT_WTIMER);
+}
+
+int
+if_start_wait_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = iface->dead_interval;
+ return (evtimer_add(&iface->wait_timer, &tv));
+}
+
+int
+if_stop_wait_timer(struct iface *iface)
+{
+ return (evtimer_del(&iface->wait_timer));
+}
+
+/* actions */
+int
+if_act_start(struct iface *iface)
+{
+ struct in_addr addr;
+
+ if (iface->passive) {
+ log_debug("if_act_start: cannot start passive interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ /* init the dummy local neighbor */
+ iface->self = nbr_new(ospfe_router_id(), iface, 1);
+
+ /* up interface */
+ /* ... */
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ 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_aton(AllSPFRouters, &addr);
+ if (if_join_group(iface, &addr)) {
+ log_warnx("if_act_start: error joining group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ }
+ if (iface->priority == 0) {
+ iface->state = IF_STA_DROTHER;
+ if (if_start_hello_timer(iface))
+ log_warnx("if_act_start: cannot schedule hello "
+ "timer, interface %s", iface->name);
+ } else {
+ iface->state = IF_STA_WAITING;
+ if (if_start_wait_timer(iface))
+ log_warnx("if_act_start: cannot schedule wait "
+ "timer, interface %s", iface->name);
+ }
+ break;
+ case IF_TYPE_NBMA:
+ log_debug("if_act_start: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ default:
+ fatalx("if_act_start: unknown interface type");
+ }
+
+ 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 nbr *nbr, *bdr = NULL, *dr = NULL;
+ int round = 0;
+ int changed = 0;
+ char b1[16], b2[16], b3[16], b4[16];
+
+ log_debug("if_act_elect: interface %s", iface->name);
+
+start:
+ /* elect backup designated router */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->priority == 0 || nbr == dr || /* not electable */
+ nbr->dr.s_addr == nbr->addr.s_addr) /* don't elect DR */
+ continue;
+ if (bdr != NULL) {
+ if (nbr->bdr.s_addr == nbr->addr.s_addr) {
+ if (bdr->bdr.s_addr == bdr->addr.s_addr)
+ bdr = if_elect(bdr, nbr);
+ else
+ bdr = nbr;
+ } else if (bdr->bdr.s_addr != bdr->addr.s_addr)
+ bdr = if_elect(bdr, nbr);
+ } else
+ bdr = nbr;
+ }
+ log_debug("if_act_elect: bdr %s", bdr ?
+ inet_ntop(AF_INET, &bdr->addr, b4, sizeof(b4)) : "none");
+ /* elect designated router */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->priority == 0 || (nbr != dr &&
+ nbr->dr.s_addr != nbr->addr.s_addr))
+ /* only DR may be elected check priority too */
+ continue;
+ if (dr == NULL || bdr == NULL)
+ dr = nbr;
+ else
+ dr = if_elect(bdr, nbr);
+ }
+ log_debug("if_act_elect: dr %s", dr ?
+ inet_ntop(AF_INET, &dr->addr, b4, sizeof(b4)) : "none");
+ 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))) {
+ log_debug("if_act_elect: round two");
+ 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.
+ */
+ 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)
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->state & NBR_STA_BIDIR)
+ nbr_fsm(nbr, NBR_EVT_ADJ_OK);
+ }
+
+ if (if_start_hello_timer(iface)) {
+ log_warnx("if_act_elect: cannot schedule hello_timer");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_act_reset(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+ struct in_addr addr;
+
+ if (if_stop_hello_timer(iface)) {
+ log_warnx("if_act_reset: error removing hello_timer, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+
+ if (if_stop_wait_timer(iface)) {
+ log_warnx("if_act_reset: error removing wait_timer, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+
+ if (stop_ls_ack_tx_timer(iface)) {
+ log_warnx("if_act_reset: error removing ls_ack_tx_timer, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ log_debug("if_act_reset: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ case IF_TYPE_BROADCAST:
+ inet_aton(AllSPFRouters, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: error leaving group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ }
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ 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));
+ }
+ }
+
+ while ((nbr = LIST_FIRST(&iface->nbr_list))) {
+ LIST_REMOVE(nbr, entry);
+ nbr_del(nbr);
+ }
+
+ iface->dr = NULL;
+ iface->bdr = NULL;
+
+ 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));
+ memcpy(&ictl.rtr_id, &iface->rtr_id, sizeof(ictl.rtr_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.transfer_delay = iface->transfer_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.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;
+
+ 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);
+}
+
+/* names */
+const char *
+if_state_name(int state)
+{
+ switch (state) {
+ case IF_STA_DOWN:
+ return ("DOWN");
+ case IF_STA_LOOPBACK:
+ return ("LOOPBACK");
+ case IF_STA_WAITING:
+ return ("WAITING");
+ case IF_STA_POINTTOPOINT:
+ return ("POINT-TO-POINT");
+ case IF_STA_DROTHER:
+ return ("DROTHER");
+ case IF_STA_BACKUP:
+ return ("BACKUP");
+ case IF_STA_DR:
+ return ("DR");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+if_event_name(int event)
+{
+ return (if_event_names[event]);
+}
+
+const char *
+if_action_name(int action)
+{
+ return (if_action_names[action]);
+}
+
+const char *
+if_type_name(int type)
+{
+ return (if_type_names[type]);
+}
+
+const char *
+if_auth_name(int type)
+{
+ return (if_auth_names[type]);
+}
+
+/* misc */
+int
+if_set_mcast_ttl(int fd, u_int8_t ttl)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ log_warn("if_set_mcast_ttl: error setting "
+ "IP_MULTICAST_TTL to %d", ttl);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_tos(int fd, int tos)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS,
+ (int *)&tos, sizeof(tos)) < 0) {
+ log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_join_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+
+ switch (iface->type) {
+ case IF_TYPE_BROADCAST:
+ mreq.imr_multiaddr.s_addr = addr->s_addr;
+ mreq.imr_interface.s_addr = iface->addr.s_addr;
+
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_debug("if_join_group: error IP_ADD_MEMBERSHIP, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+ break;
+ case IF_TYPE_POINTOPOINT:
+ 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 in_addr *addr)
+{
+ struct ip_mreq mreq;
+
+ switch (iface->type) {
+ case IF_TYPE_BROADCAST:
+ mreq.imr_multiaddr.s_addr = addr->s_addr;
+ mreq.imr_interface.s_addr = iface->addr.s_addr;
+
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_debug("if_leave_group: error IP_DROP_MEMBERSHIP, "
+ "interface %s", iface->name);
+ return (-1);
+ }
+ break;
+ case IF_TYPE_POINTOPOINT:
+ 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_BROADCAST:
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&iface->addr.s_addr,
+ sizeof(iface->addr.s_addr)) < 0) {
+ log_debug("if_set_mcast: error setting "
+ "IP_MULTICAST_IF, interface %s", iface->name);
+ return (-1);
+ }
+ break;
+ case IF_TYPE_POINTOPOINT:
+ 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_int8_t loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0) {
+ log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/ospfd/iso_cksum.c b/usr.sbin/ospfd/iso_cksum.c
new file mode 100644
index 00000000000..f3a050b37fe
--- /dev/null
+++ b/usr.sbin/ospfd/iso_cksum.c
@@ -0,0 +1,58 @@
+/* $OpenBSD: iso_cksum.c,v 1.1 2005/01/28 14:05:40 claudio 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 "ospfd.h"
+#include "log.h"
+
+/* 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; /* counters */
+ int r0, r1; /* results */
+ u_int16_t sop;
+
+ sop = len - pos - 1; /* pos is an offset (pos 2 is at len 3) */
+ p += 2;
+ len -= 2;
+ while (len--) {
+ c0 += *p++;
+ c1 += c0;
+ if ((len & 0xfff) == 0) {
+ /* overflow protection */
+ c0 %= 255;
+ c1 %= 255;
+ }
+ }
+ r0 = c0 = c0 % 255;
+ r1 = c1 % 255;
+
+ if (pos) {
+ r0 = ((sop * r0 - r1)) % 255;
+ if (r0 <= 0)
+ r0 += 255;
+ r1 = 510 - r0 - c0;
+ if (r1 > 255)
+ r1 -= 255;
+ }
+
+ return (r0 << 8 | r1);
+}
diff --git a/usr.sbin/ospfd/kroute.c b/usr.sbin/ospfd/kroute.c
new file mode 100644
index 00000000000..2aa833051b4
--- /dev/null
+++ b/usr.sbin/ospfd/kroute.c
@@ -0,0 +1,1270 @@
+/* $OpenBSD: kroute.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/tree.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ospfd.h"
+#include "ospfd.h"
+#include "log.h"
+
+/* XXX */
+void main_imsg_compose_ospfe(int, pid_t, void *, u_int16_t);
+void main_imsg_compose_rde(int, pid_t, void *, u_int16_t);
+extern struct ospfd_conf *conf;
+
+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 knexthop_node {
+ RB_ENTRY(knexthop_node) entry;
+ struct in_addr nexthop;
+ struct kroute_node *kroute;
+};
+
+struct kif_kr {
+ LIST_ENTRY(kif_kr) entry;
+ struct kroute_node *kr;
+};
+
+LIST_HEAD(kif_kr_head, kif_kr);
+
+struct kif_node {
+ RB_ENTRY(kif_node) entry;
+ struct kif k;
+ struct kif_kr_head kroute_l;
+};
+
+int kroute_compare(struct kroute_node *, struct kroute_node *);
+int knexthop_compare(struct knexthop_node *, struct knexthop_node *);
+int kif_compare(struct kif_node *, struct kif_node *);
+
+struct kroute_node *kroute_find(in_addr_t, u_int8_t);
+int kroute_insert(struct kroute_node *);
+int kroute_remove(struct kroute_node *);
+void kroute_clear(void);
+
+struct knexthop_node *knexthop_find(struct in_addr);
+int knexthop_insert(struct knexthop_node *);
+int knexthop_remove(struct knexthop_node *);
+void knexthop_clear(void);
+
+struct kif_node *kif_find(int);
+int kif_insert(struct kif_node *);
+int kif_remove(struct kif_node *);
+void kif_clear(void);
+
+int kif_kr_insert(struct kroute_node *);
+int kif_kr_remove(struct kroute_node *);
+
+void knexthop_validate(struct knexthop_node *);
+struct kroute_node *kroute_match(in_addr_t);
+void kroute_attach_nexthop(struct knexthop_node *,
+ struct kroute_node *);
+void kroute_detach_nexthop(struct knexthop_node *);
+
+int protect_lo(void);
+u_int8_t prefixlen_classful(in_addr_t);
+u_int8_t mask2prefixlen(in_addr_t);
+in_addr_t prefixlen2mask(u_int8_t);
+void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
+void if_change(u_short, int, struct if_data *);
+void if_announce(void *);
+
+int send_rtmsg(int, int, struct kroute *);
+int dispatch_rtmsg(void);
+int fetchtable(void);
+int fetchifs(int);
+
+RB_HEAD(kroute_tree, kroute_node) kroute_tree, krt;
+RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare)
+RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare)
+
+RB_HEAD(knexthop_tree, knexthop_node) knexthop_tree, knt;
+RB_PROTOTYPE(knexthop_tree, knexthop_node, entry, knexthop_compare)
+RB_GENERATE(knexthop_tree, knexthop_node, entry, knexthop_compare)
+
+RB_HEAD(kif_tree, kif_node) kif_tree, kit;
+RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
+RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
+
+int
+kr_init(int fs)
+{
+ int opt;
+
+ 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 */
+
+ kr_state.pid = getpid();
+ kr_state.rtseq = 1;
+
+ RB_INIT(&krt);
+ RB_INIT(&knt);
+ RB_INIT(&kit);
+
+ if (fetchifs(0) == -1)
+ return (-1);
+
+ if (fetchtable() == -1)
+ return (-1);
+
+ if (protect_lo() == -1)
+ return (-1);
+
+ event_set(&kr_state.ev, kr_state.fd, EV_READ, kr_dispatch_msg, NULL);
+ event_add(&kr_state.ev, NULL);
+
+ return (0);
+}
+
+int
+kr_change(struct kroute *kroute)
+{
+ struct kroute_node *kr;
+ int action = RTM_ADD;
+
+ if ((kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen)) !=
+ NULL) {
+ if (kr->r.flags & F_OSPFD_INSERTED)
+ action = RTM_CHANGE;
+ else /* a non-bgp route already exists. not a problem */
+ return (0);
+ }
+
+ /* nexthop within 127/8 -> ignore silently */
+ if ((kroute->nexthop.s_addr & htonl(0xff000000)) ==
+ inet_addr("127.0.0.0"))
+ 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;
+
+ if (kroute_insert(kr) == -1)
+ free(kr);
+ } else {
+ kr->r.nexthop.s_addr = kroute->nexthop.s_addr;
+ if (kroute->flags & F_BLACKHOLE)
+ kr->r.flags |= F_BLACKHOLE;
+ else
+ kr->r.flags &= ~F_BLACKHOLE;
+ if (kroute->flags & F_REJECT)
+ kr->r.flags |= F_REJECT;
+ else
+ kr->r.flags &= ~F_REJECT;
+ }
+
+ 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);
+
+ /* nexthop within 127/8 -> ignore silently */
+ if ((kroute->nexthop.s_addr & htonl(0xff000000)) ==
+ inet_addr("127.0.0.0"))
+ 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();
+ knexthop_clear();
+ 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_OSPFD_INSERTED))
+ 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_OSPFD_INSERTED))
+ send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
+
+ kr_state.fib_sync = 0;
+
+ log_info("kernel routing table decoupled");
+}
+
+void
+kr_dispatch_msg(int fd, short event, void *bula)
+{
+ dispatch_rtmsg();
+}
+
+int
+kr_nexthop_add(struct in_addr addr)
+{
+ struct knexthop_node *h;
+
+ if ((h = knexthop_find(addr)) != NULL) {
+ /* should not happen... this is really an error path */
+ struct kroute_nexthop nh;
+
+ bzero(&nh, sizeof(nh));
+ memcpy(&nh.nexthop, &addr, sizeof(nh.nexthop));
+ if (h->kroute != NULL) {
+ nh.valid = 1;
+ nh.connected = h->kroute->r.flags & F_CONNECTED;
+ if (h->kroute->r.nexthop.s_addr != 0)
+ nh.gateway.s_addr =
+ h->kroute->r.nexthop.s_addr;
+ memcpy(&nh.kr, &h->kroute->r, sizeof(nh.kr));
+ }
+ send_nexthop_update(&nh);
+ } else {
+ if ((h = calloc(1, sizeof(struct knexthop_node))) == NULL) {
+ log_warn("kr_nexthop_add");
+ return (-1);
+ }
+ memcpy(&h->nexthop, &addr, sizeof(h->nexthop));
+
+ if (knexthop_insert(h) == -1)
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+kr_nexthop_delete(struct in_addr addr)
+{
+ struct knexthop_node *kn;
+
+ if ((kn = knexthop_find(addr)) == NULL)
+ return;
+
+ knexthop_remove(kn);
+}
+
+void
+kr_show_route(struct imsg *imsg)
+{
+ struct area *area = NULL;
+ struct iface *iface = NULL;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_INTERFACE:
+ LIST_FOREACH(area, &conf->area_list, entry) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ main_imsg_compose_ospfe(IMSG_CTL_SHOW_INTERFACE,
+ imsg->hdr.pid, iface, sizeof(*iface));
+ }
+ }
+ 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)
+{
+ struct kif_node *kif;
+
+ RB_FOREACH(kif, kif_tree, &kit)
+ if (!strcmp(ifname, kif->k.ifname)) {
+ main_imsg_compose_ospfe(IMSG_IFINFO, 0,
+ &kif->k, sizeof(kif->k));
+ return;
+ }
+}
+
+/* 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
+knexthop_compare(struct knexthop_node *a, struct knexthop_node *b)
+{
+ u_int32_t r;
+
+ if ((r = b->nexthop.s_addr - a->nexthop.s_addr) != 0)
+ return (r);
+
+ 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));
+}
+
+int
+kroute_insert(struct kroute_node *kr)
+{
+ struct knexthop_node *h;
+ in_addr_t mask, ina;
+
+ if (RB_INSERT(kroute_tree, &krt, kr) != NULL) {
+ log_warnx("kroute_tree insert failed for %s/%u",
+ inet_ntoa(kr->r.prefix), kr->r.prefixlen);
+ free(kr);
+ return (-1);
+ }
+
+ if (kr->r.flags & F_KERNEL) {
+ if (!(kr->r.flags & F_CONNECTED))
+ kr->r.flags |= F_STATIC;
+
+ mask = prefixlen2mask(kr->r.prefixlen);
+ ina = ntohl(kr->r.prefix.s_addr);
+ RB_FOREACH(h, knexthop_tree, &knt)
+ if ((ntohl(h->nexthop.s_addr) & mask) == ina)
+ knexthop_validate(h);
+
+ if (kr->r.flags & F_CONNECTED)
+ if (kif_kr_insert(kr) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+int
+kroute_remove(struct kroute_node *kr)
+{
+ struct knexthop_node *s;
+
+ 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);
+ }
+
+ /* check wether a nexthop depends on this kroute */
+ if ((kr->r.flags & F_KERNEL) && (kr->r.flags & F_NEXTHOP))
+ RB_FOREACH(s, knexthop_tree, &knt)
+ if (s->kroute == kr)
+ knexthop_validate(s);
+
+ if (kr->r.flags & F_CONNECTED)
+ if (kif_kr_remove(kr) == -1) {
+ free(kr);
+ return (-1);
+ }
+
+ free(kr);
+ return (0);
+}
+
+void
+kroute_clear(void)
+{
+ struct kroute_node *kr;
+
+ while ((kr = RB_MIN(kroute_tree, &krt)) != NULL)
+ kroute_remove(kr);
+}
+
+struct knexthop_node *
+knexthop_find(struct in_addr addr)
+{
+ struct knexthop_node s;
+
+ memcpy(&s.nexthop, &addr, sizeof(s.nexthop));
+
+ return (RB_FIND(knexthop_tree, &knt, &s));
+}
+
+int
+knexthop_insert(struct knexthop_node *kn)
+{
+ if (RB_INSERT(knexthop_tree, &knt, kn) != NULL) {
+ log_warnx("knexthop_tree insert failed for %s",
+ inet_ntoa(kn->nexthop));
+ free(kn);
+ return (-1);
+ }
+
+ knexthop_validate(kn);
+
+ return (0);
+}
+
+int
+knexthop_remove(struct knexthop_node *kn)
+{
+ kroute_detach_nexthop(kn);
+
+ if (RB_REMOVE(knexthop_tree, &knt, kn) == NULL) {
+ log_warnx("knexthop_remove failed for %s",
+ inet_ntoa(kn->nexthop));
+ return (-1);
+ }
+
+ free(kn);
+ return (0);
+}
+
+void
+knexthop_clear(void)
+{
+ struct knexthop_node *kn;
+
+ while ((kn = RB_MIN(knexthop_tree, &knt)) != NULL)
+ knexthop_remove(kn);
+}
+
+struct kif_node *
+kif_find(int ifindex)
+{
+ struct kif_node s;
+
+ bzero(&s, sizeof(s));
+ s.k.ifindex = ifindex;
+
+ return (RB_FIND(kif_tree, &kit, &s));
+}
+
+int
+kif_insert(struct kif_node *kif)
+{
+ LIST_INIT(&kif->kroute_l);
+
+ if (RB_INSERT(kif_tree, &kit, kif) != NULL) {
+ log_warnx("RB_INSERT(kif_tree, &kit, kif)");
+ free(kif);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+kif_remove(struct kif_node *kif)
+{
+ struct kif_kr *kkr;
+
+ if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
+ log_warnx("RB_REMOVE(kif_tree, &kit, kif)");
+ return (-1);
+ }
+
+ while ((kkr = LIST_FIRST(&kif->kroute_l)) != NULL) {
+ LIST_REMOVE(kkr, entry);
+ kkr->kr->r.flags &= ~F_NEXTHOP;
+ kroute_remove(kkr->kr);
+ free(kkr);
+ }
+
+ free(kif);
+ return (0);
+}
+
+void
+kif_clear(void)
+{
+ struct kif_node *kif;
+
+ while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
+ kif_remove(kif);
+}
+
+int
+kif_kr_insert(struct kroute_node *kr)
+{
+ struct kif_node *kif;
+ struct kif_kr *kkr;
+
+ if ((kif = kif_find(kr->r.ifindex)) == NULL) {
+ if (kr->r.ifindex)
+ log_warnx("interface with index %u not found",
+ kr->r.ifindex);
+ return (0);
+ }
+
+ if ((kkr = calloc(1, sizeof(struct kif_kr))) == NULL) {
+ log_warn("kif_kr_insert");
+ return (-1);
+ }
+
+ kkr->kr = kr;
+
+ LIST_INSERT_HEAD(&kif->kroute_l, kkr, entry);
+
+ return (0);
+}
+
+int
+kif_kr_remove(struct kroute_node *kr)
+{
+ struct kif_node *kif;
+ struct kif_kr *kkr;
+
+ if ((kif = kif_find(kr->r.ifindex)) == NULL) {
+ if (kr->r.ifindex)
+ log_warnx("interface with index %u not found",
+ kr->r.ifindex);
+ return (0);
+ }
+
+ for (kkr = LIST_FIRST(&kif->kroute_l); kkr != NULL && kkr->kr != kr;
+ kkr = LIST_NEXT(kkr, entry))
+ ; /* nothing */
+
+ if (kkr == NULL) {
+ log_warnx("can't remove connected route from interface "
+ "with index %u: not found", kr->r.ifindex);
+ return (-1);
+ }
+
+ LIST_REMOVE(kkr, entry);
+ free(kkr);
+
+ return (0);
+}
+
+/* nexthop validation */
+void
+knexthop_validate(struct knexthop_node *kn)
+{
+ struct kroute_node *kr;
+ struct kroute_nexthop n;
+ int was_valid = 0;
+
+ if (kn->kroute != NULL && (!(kn->kroute->r.flags & F_DOWN)))
+ was_valid = 1;
+
+ bzero(&n, sizeof(n));
+ memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
+ kroute_detach_nexthop(kn);
+
+ if ((kr = kroute_match(kn->nexthop.s_addr)) == NULL) {
+ if (was_valid)
+ send_nexthop_update(&n);
+ } else { /* match */
+ if (kr->r.flags & F_DOWN) { /* is down */
+ if (was_valid)
+ send_nexthop_update(&n);
+ } else { /* valid */
+ n.valid = 1;
+ n.connected = kr->r.flags & F_CONNECTED;
+ n.gateway.s_addr = kr->r.nexthop.s_addr;
+ memcpy(&n.kr, &kr->r, sizeof(n.kr));
+ send_nexthop_update(&n);
+ }
+ kroute_attach_nexthop(kn, kr);
+ }
+}
+
+struct kroute_node *
+kroute_match(in_addr_t key)
+{
+ int i;
+ struct kroute_node *kr;
+ in_addr_t ina;
+
+ ina = ntohl(key);
+
+ /* we will never match the default route */
+ for (i = 32; i > 0; i--)
+ if ((kr =
+ kroute_find(htonl(ina & 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);
+}
+
+void
+kroute_attach_nexthop(struct knexthop_node *kn, struct kroute_node *kr)
+{
+ kn->kroute = kr;
+ kr->r.flags |= F_NEXTHOP;
+}
+
+void
+kroute_detach_nexthop(struct knexthop_node *kn)
+{
+ struct knexthop_node *s;
+
+ /*
+ * check wether there's another nexthop depending on this kroute
+ * if not remove the flag
+ */
+
+ if (kn->kroute == NULL)
+ return;
+
+ for (s = RB_MIN(knexthop_tree, &knt); s != NULL &&
+ s->kroute != kn->kroute; s = RB_NEXT(knexthop_tree, &knt, s))
+ ; /* nothing */
+
+ if (s == NULL)
+ kn->kroute->r.flags &= ~F_NEXTHOP;
+
+ kn->kroute = 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 = inet_addr("127.0.0.1");
+ 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 (0xffffffff << (32 - prefixlen));
+}
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a))
+
+void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ sa = (struct sockaddr *)((char *)(sa) +
+ ROUNDUP(sa->sa_len, sizeof(long)));
+ } else
+ rti_info[i] = NULL;
+ }
+}
+
+void
+if_change(u_short ifindex, int flags, struct if_data *ifd)
+{
+ struct kif_node *kif;
+ struct kif_kr *kkr;
+ struct kroute_nexthop nh;
+ struct knexthop_node *n;
+ u_int8_t reachable;
+
+ if ((kif = kif_find(ifindex)) == NULL) {
+ log_warnx("interface with index %u not found",
+ ifindex);
+ return;
+ }
+
+ kif->k.flags = flags;
+ kif->k.link_state = ifd->ifi_link_state;
+ kif->k.media_type = ifd->ifi_type;
+ kif->k.baudrate = ifd->ifi_baudrate;
+
+ main_imsg_compose_ospfe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k));
+
+ if ((reachable = (flags & IFF_UP) &&
+ (ifd->ifi_link_state != LINK_STATE_DOWN)) == kif->k.nh_reachable)
+ return; /* nothing changed wrt nexthop validity */
+
+ kif->k.nh_reachable = reachable;
+
+ LIST_FOREACH(kkr, &kif->kroute_l, entry) {
+ /*
+ * we treat link_state == LINK_STATE_UNKNOWN as valid
+ * not all interfaces have a concept of "link state" and/or
+ * do not report up
+ */
+ if (reachable)
+ kkr->kr->r.flags &= ~F_DOWN;
+ else
+ kkr->kr->r.flags |= F_DOWN;
+
+ RB_FOREACH(n, knexthop_tree, &knt)
+ if (n->kroute == kkr->kr) {
+ bzero(&nh, sizeof(nh));
+ memcpy(&nh.nexthop, &n->nexthop,
+ sizeof(nh.nexthop));
+ if (!(kkr->kr->r.flags & F_DOWN)) {
+ nh.valid = 1;
+ nh.connected = 1;
+ nh.gateway.s_addr =
+ kkr->kr->r.nexthop.s_addr;
+ }
+ memcpy(&nh.kr, &kkr->kr->r, sizeof(nh.kr));
+ send_nexthop_update(&nh);
+ }
+ }
+}
+
+void
+if_announce(void *msg)
+{
+ struct if_announcemsghdr *ifan;
+ struct kif_node *kif;
+
+ ifan = msg;
+
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
+ log_warn("if_announce");
+ return;
+ }
+
+ kif->k.ifindex = ifan->ifan_index;
+ strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname));
+ kif_insert(kif);
+ break;
+ case IFAN_DEPARTURE:
+ kif = kif_find(ifan->ifan_index);
+ kif_remove(kif);
+ break;
+ }
+}
+
+/* rtsock */
+int
+send_rtmsg(int fd, int action, struct kroute *kroute)
+{
+ struct {
+ struct rt_msghdr hdr;
+ struct sockaddr_in prefix;
+ struct sockaddr_in nexthop;
+ struct sockaddr_in mask;
+ } r;
+
+ if (kr_state.fib_sync == 0)
+ return (0);
+
+ bzero(&r, sizeof(r));
+ r.hdr.rtm_msglen = sizeof(r);
+ r.hdr.rtm_version = RTM_VERSION;
+ r.hdr.rtm_type = action;
+ r.hdr.rtm_flags = RTF_PROTO1;
+ if (kroute->flags & F_BLACKHOLE)
+ r.hdr.rtm_flags |= RTF_BLACKHOLE;
+ if (kroute->flags & F_REJECT)
+ r.hdr.rtm_flags |= RTF_REJECT;
+ r.hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */
+ r.hdr.rtm_addrs = RTA_DST|RTA_GATEWAY|RTA_NETMASK;
+ r.prefix.sin_len = sizeof(r.prefix);
+ r.prefix.sin_family = AF_INET;
+ r.prefix.sin_addr.s_addr = kroute->prefix.s_addr;
+
+ r.nexthop.sin_len = sizeof(r.nexthop);
+ r.nexthop.sin_family = AF_INET;
+ r.nexthop.sin_addr.s_addr = kroute->nexthop.s_addr;
+ if (kroute->nexthop.s_addr != 0)
+ r.hdr.rtm_flags |= RTF_GATEWAY;
+
+ r.mask.sin_len = sizeof(r.mask);
+ r.mask.sin_family = AF_INET;
+ r.mask.sin_addr.s_addr = htonl(prefixlen2mask(kroute->prefixlen));
+
+retry:
+ if (write(fd, &r, sizeof(r)) == -1) {
+ switch (errno) {
+ case ESRCH:
+ if (r.hdr.rtm_type == RTM_CHANGE) {
+ r.hdr.rtm_type = RTM_ADD;
+ goto retry;
+ } else if (r.hdr.rtm_type == RTM_DELETE) {
+ log_info("route %s/%u vanished before delete",
+ inet_ntoa(kroute->prefix),
+ kroute->prefixlen);
+ return (0);
+ } else {
+ log_warnx("send_rtmsg: action %u, "
+ "prefix %s/%u: %s", r.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",
+ r.hdr.rtm_type, inet_ntoa(kroute->prefix),
+ kroute->prefixlen, strerror(errno));
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+int
+fetchtable(void)
+{
+ size_t len;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_in *sa_in;
+ struct kroute_node *kr;
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchtable");
+ return (-1);
+ }
+ if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ free(buf);
+ return (-1);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ if ((sa = rti_info[RTAX_DST]) == NULL)
+ continue;
+
+ if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
+ continue;
+
+#ifdef RTF_MPATH
+ if (rtm->rtm_flags & RTF_MPATH) /* multipath */
+ continue;
+#endif
+
+ if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
+ log_warn("fetchtable");
+ free(buf);
+ return (-1);
+ }
+
+ kr->r.flags = F_KERNEL;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ kr->r.prefix.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK];
+ if (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;
+ /* not reached */
+ }
+
+ 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;
+ kr->r.ifindex = rtm->rtm_index;
+ break;
+ }
+
+ kroute_insert(kr);
+
+ }
+ free(buf);
+ return (0);
+}
+
+int
+fetchifs(int ifindex)
+{
+ size_t len;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct if_msghdr ifm;
+ struct kif_node *kif;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_dl *sdl;
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = ifindex;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchif");
+ return (-1);
+ }
+ if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ free(buf);
+ return (-1);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm.ifm_msglen) {
+ memcpy(&ifm, next, sizeof(ifm));
+ sa = (struct sockaddr *)(next + sizeof(ifm));
+ get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
+
+ if (ifm.ifm_type != RTM_IFINFO)
+ continue;
+
+ if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
+ log_warn("fetchifs");
+ free(buf);
+ return (-1);
+ }
+
+ kif->k.ifindex = ifm.ifm_index;
+ kif->k.flags = ifm.ifm_flags;
+ kif->k.link_state = ifm.ifm_data.ifi_link_state;
+ kif->k.media_type = ifm.ifm_data.ifi_type;
+ kif->k.baudrate = ifm.ifm_data.ifi_baudrate;
+ kif->k.nh_reachable = (kif->k.flags & IFF_UP) &&
+ (ifm.ifm_data.ifi_link_state != LINK_STATE_DOWN);
+
+ if ((sa = rti_info[RTAX_IFP]) != NULL)
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (sdl->sdl_nlen > 0)
+ strlcpy(kif->k.ifname, sdl->sdl_data,
+ sizeof(kif->k.ifname));
+ }
+
+ kif_insert(kif);
+ }
+ free(buf);
+ return (0);
+}
+
+int
+dispatch_rtmsg(void)
+{
+ char buf[RT_BUF_SIZE];
+ ssize_t n;
+ char *next, *lim;
+ struct rt_msghdr *rtm;
+ struct if_msghdr ifm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_in *sa_in;
+ struct kroute_node *kr;
+ struct in_addr prefix, nexthop;
+ u_int8_t prefixlen;
+ int flags;
+ u_short ifindex;
+
+ 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;
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ prefix.s_addr = 0;
+ prefixlen = 0;
+ flags = F_KERNEL;
+ nexthop.s_addr = 0;
+ ifindex = 0;
+
+ if (rtm->rtm_pid == kr_state.pid) /* cause by us */
+ continue;
+
+ if (rtm->rtm_errno) /* failed attempts... */
+ continue;
+
+ if (rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE ||
+ rtm->rtm_type == RTM_DELETE) {
+ if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
+ continue;
+ switch (sa->sa_family) {
+ case AF_INET:
+ prefix.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ sa_in = (struct sockaddr_in *)
+ rti_info[RTAX_NETMASK];
+ if (sa_in != NULL) {
+ if (sa_in->sin_len != 0)
+ prefixlen = mask2prefixlen(
+ sa_in->sin_addr.s_addr);
+ } else if (rtm->rtm_flags & RTF_HOST)
+ prefixlen = 32;
+ else
+ prefixlen =
+ prefixlen_classful(prefix.s_addr);
+ break;
+ default:
+ continue;
+ /* not reached */
+ }
+ }
+
+ 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;
+ ifindex = rtm->rtm_index;
+ 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 ((kr = kroute_find(prefix.s_addr, prefixlen)) !=
+ NULL) {
+ if (kr->r.flags & F_KERNEL) {
+ kr->r.nexthop.s_addr = nexthop.s_addr;
+ if (kr->r.flags & F_NEXTHOP)
+ flags |= F_NEXTHOP;
+ if ((kr->r.flags & F_CONNECTED) &&
+ !(flags & F_CONNECTED))
+ kif_kr_remove(kr);
+ if ((flags & F_CONNECTED) &&
+ !(kr->r.flags & F_CONNECTED))
+ kif_kr_insert(kr);
+ kr->r.flags = flags;
+ }
+ } else if (rtm->rtm_type == RTM_CHANGE) {
+ log_warnx("change req for %s/%u: not "
+ "in table", inet_ntoa(prefix),
+ prefixlen);
+ continue;
+ } else {
+ if ((kr = calloc(1,
+ sizeof(struct kroute_node))) == NULL) {
+ log_warn("dispatch_rtmsg");
+ return (-1);
+ }
+ kr->r.prefix.s_addr = prefix.s_addr;
+ kr->r.prefixlen = prefixlen;
+ kr->r.nexthop.s_addr = nexthop.s_addr;
+ kr->r.flags = flags;
+ kr->r.ifindex = ifindex;
+
+ kroute_insert(kr);
+ }
+ break;
+ case RTM_DELETE:
+ if ((kr = kroute_find(prefix.s_addr, prefixlen)) ==
+ NULL)
+ continue;
+ if (!(kr->r.flags & F_KERNEL))
+ continue;
+ if (kroute_remove(kr) == -1)
+ return (-1);
+ break;
+ case RTM_IFINFO:
+ memcpy(&ifm, next, sizeof(ifm));
+ if_change(ifm.ifm_index, ifm.ifm_flags,
+ &ifm.ifm_data);
+ break;
+ case RTM_IFANNOUNCE:
+ if_announce(next);
+ break;
+ default:
+ /* ignore for now */
+ break;
+ }
+ }
+ return (0);
+}
diff --git a/usr.sbin/ospfd/log.c b/usr.sbin/ospfd/log.c
new file mode 100644
index 00000000000..30f725b9609
--- /dev/null
+++ b/usr.sbin/ospfd/log.c
@@ -0,0 +1,164 @@
+/* $OpenBSD: log.c,v 1.1 2005/01/28 14:05:40 claudio 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/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "ospfd.h"
+#include "log.h"
+
+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);
+}
+
diff --git a/usr.sbin/ospfd/log.h b/usr.sbin/ospfd/log.h
new file mode 100644
index 00000000000..65caffcad90
--- /dev/null
+++ b/usr.sbin/ospfd/log.h
@@ -0,0 +1,39 @@
+/* $OpenBSD: log.h,v 1.1 2005/01/28 14:05:40 claudio 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 *);
+void fatalx(const char *);
+
+static const char * const procnames[] = {
+ "parent",
+ "ospfe",
+ "rde"
+};
+
+#endif /* _LOG_H_ */
diff --git a/usr.sbin/ospfd/lsack.c b/usr.sbin/ospfd/lsack.c
new file mode 100644
index 00000000000..dbbb4f43ad0
--- /dev/null
+++ b/usr.sbin/ospfd/lsack.c
@@ -0,0 +1,305 @@
+/* $OpenBSD: lsack.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <strings.h>
+
+#include "ospfd.h"
+#include "ospf.h"
+#include "log.h"
+#include "ospfe.h"
+
+int start_ls_ack_tx_timer_now(struct iface *);
+
+/* link state acknowledgement packet handling */
+int
+send_ls_ack(struct iface *iface, struct in_addr addr, void *data, int len)
+{
+ struct sockaddr_in dst;
+ char *buf;
+ char *ptr;
+ int ret = 0;
+
+ log_debug("send_ls_ack: interface %s addr %s",
+ iface->name, inet_ntoa(addr));
+
+ if (iface->passive)
+ return (0);
+
+ /* XXX use buffer API instead for better decoupling */
+ if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("send_ls_ack");
+
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr.s_addr = addr.s_addr;
+
+ /* OSPF header */
+ gen_ospf_hdr(ptr, iface, PACKET_TYPE_LS_ACK);
+ ptr += sizeof(struct ospf_hdr);
+
+ /* LS ack(s) */
+ memcpy(ptr, data, len); /* XXX size check ??? */
+ ptr += len;
+
+ /* update authentication and calculate checksum */
+ auth_gen(buf, ptr - buf, iface);
+
+ if ((ret = send_packet(iface, buf, (ptr - buf), &dst)) == -1)
+ log_warnx("send_ls_ack: error sending packet on "
+ "interface %s", iface->name);
+
+ free(buf);
+ return (ret);
+}
+
+void
+recv_ls_ack(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct lsa_hdr lsa_hdr;
+
+ log_debug("recv_ls_ack: neighbor ID %s", inet_ntoa(nbr->id));
+
+ 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:
+ log_debug("recv_ls_ack: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ while (len >= sizeof(lsa_hdr)) {
+ memcpy(&lsa_hdr, buf, sizeof(lsa_hdr));
+
+ if (lsa_hdr_check(nbr, &lsa_hdr))
+ 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");
+ }
+
+ return;
+}
+
+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) == 0x80000000)) {
+ 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;
+}
+
+bool
+ls_ack_list_empty(struct iface *iface)
+{
+ return (TAILQ_EMPTY(&iface->ls_ack_list));
+}
+
+/* timers */
+void
+ls_ack_tx_timer(int fd, short event, void *arg)
+{
+ struct in_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++;
+ }
+
+ log_debug("ls_ack_tx_timer: sending %d ack(s), interface %s",
+ cnt, iface->name);
+
+ /* send LS ack(s) but first set correct destination */
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_aton(AllSPFRouters, &addr);
+ send_ls_ack(iface, addr, buf, ptr - buf);
+ break;
+ case IF_TYPE_BROADCAST:
+ if (iface->state & IF_STA_DRORBDR)
+ inet_aton(AllSPFRouters, &addr);
+ else
+ inet_aton(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, ptr, ptr - buf);
+ }
+ break;
+ default:
+ fatalx("lsa_flood: unknown interface type");
+ }
+ }
+
+ free(buf);
+}
+
+int
+start_ls_ack_tx_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ log_debug("start_ls_ack_tx_timer: interface %s", iface->name);
+ timerclear(&tv);
+ tv.tv_sec = iface->rxmt_interval / 2;
+ return (evtimer_add(&iface->lsack_tx_timer, &tv));
+}
+
+int
+start_ls_ack_tx_timer_now(struct iface *iface)
+{
+ struct timeval tv;
+
+ log_debug("start_ls_ack_tx_timer_now: interface %s", iface->name);
+ timerclear(&tv);
+
+ return (evtimer_add(&iface->lsack_tx_timer, &tv));
+}
+
+int
+stop_ls_ack_tx_timer(struct iface *iface)
+{
+ log_debug("stop_ls_ack_tx_timer: interface %s", iface->name);
+
+ return (evtimer_del(&iface->lsack_tx_timer));
+}
diff --git a/usr.sbin/ospfd/lsreq.c b/usr.sbin/ospfd/lsreq.c
new file mode 100644
index 00000000000..8018c240ad5
--- /dev/null
+++ b/usr.sbin/ospfd/lsreq.c
@@ -0,0 +1,259 @@
+/* $OpenBSD: lsreq.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 "ospfd.h"
+#include "ospf.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_in dst;
+ struct ls_req_hdr *ls_req_hdr;
+ struct lsa_entry *le, *nle;
+ char *buf = NULL;
+ char *ptr;
+ int ret = 0;
+
+ log_debug("send_ls_req: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (nbr->iface->passive)
+ return (0);
+
+ /* XXX use buffer API instead for better decoupling */
+ if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("send_ls_req");
+
+ /* set destination */
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+
+ switch (nbr->iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_aton(AllSPFRouters, &dst.sin_addr);
+ break;
+ case IF_TYPE_BROADCAST:
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ dst.sin_addr.s_addr = nbr->addr.s_addr;
+ break;
+ default:
+ fatalx("send_ls_req: unknown interface type");
+ }
+
+ /* OSPF header */
+ gen_ospf_hdr(ptr, nbr->iface, PACKET_TYPE_LS_REQUEST);
+ ptr += sizeof(struct ospf_hdr);
+
+ /* LSA header(s) */
+ for (le = TAILQ_FIRST(&nbr->ls_req_list); le != NULL &&
+ (ptr - buf) < nbr->iface->mtu - PACKET_HDR; le = nle) {
+ nbr->ls_req = nle = TAILQ_NEXT(le, entry);
+ ls_req_hdr = (struct ls_req_hdr *)ptr;
+ 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;
+ ptr += sizeof(*ls_req_hdr);
+ }
+
+ /* update authentication and calculate checksum */
+ auth_gen(buf, ptr - buf, nbr->iface);
+
+ if ((ret = send_packet(nbr->iface, buf, (ptr - buf), &dst)) == -1)
+ log_warnx("send_ls_req: error sending packet on "
+ "interface %s", nbr->iface->name);
+ free(buf);
+ return (ret);
+}
+
+void
+recv_ls_req(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ log_debug("recv_ls_req: neighbor ID %s", inet_ntoa(nbr->id));
+
+ 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));
+ nbr_fsm(nbr, NBR_EVT_ADJ_OK);
+ break;
+ case NBR_STA_XCHNG:
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ imsg_compose(ibuf_rde, IMSG_LS_REQ, nbr->peerid, 0, -1,
+ 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_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;
+}
+
+bool
+ls_req_list_empty(struct nbr *nbr)
+{
+ return (TAILQ_EMPTY(&nbr->ls_req_list));
+}
+
+/* timers */
+void
+ls_req_tx_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+ struct timeval tv;
+
+ log_debug("ls_req_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+
+ 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;
+ log_debug("ls_req_tx_timer: reschedule neighbor ID %s",
+ inet_ntoa(nbr->id));
+ evtimer_add(&nbr->lsreq_tx_timer, &tv);
+ }
+}
+
+int
+start_ls_req_tx_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ log_debug("start_ls_req_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+ timerclear(&tv);
+
+ return (evtimer_add(&nbr->lsreq_tx_timer, &tv));
+}
+
+int
+stop_ls_req_tx_timer(struct nbr *nbr)
+{
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ log_debug("stop_ls_req_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return (evtimer_del(&nbr->lsreq_tx_timer));
+}
diff --git a/usr.sbin/ospfd/lsupdate.c b/usr.sbin/ospfd/lsupdate.c
new file mode 100644
index 00000000000..5795d4c8702
--- /dev/null
+++ b/usr.sbin/ospfd/lsupdate.c
@@ -0,0 +1,447 @@
+/* $OpenBSD: lsupdate.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 <strings.h>
+
+#include "ospf.h"
+#include "ospfd.h"
+#include "log.h"
+#include "ospfe.h"
+#include "rde.h"
+
+extern struct ospfd_conf *oeconf;
+extern struct imsgbuf *ibuf_rde;
+
+/* link state update packet handling */
+int
+lsa_flood(struct iface *iface, struct nbr *originator, struct lsa_hdr *lsa_hdr,
+ void *data, u_int16_t len)
+{
+ struct in_addr addr;
+ struct nbr *nbr;
+ struct lsa_entry *le;
+ int queued = 0, dont_ack = 0;
+ int r;
+
+ if (iface->passive)
+ return (0);
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr == iface->self)
+ continue;
+ if (!(nbr->state & NBR_STA_FLOOD))
+ continue;
+
+ le = ls_retrans_list_get(nbr, lsa_hdr);
+ if (le)
+ 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;
+ }
+
+ ls_retrans_list_add(nbr, data); /* XXX error handling */
+ queued = 1;
+ }
+ if (!queued)
+ return (0);
+
+ if (iface == originator->iface) {
+ if (iface->dr == originator || iface->bdr == originator)
+ return (0);
+ if (iface->state & IF_STA_BACKUP)
+ return (0);
+ dont_ack++;
+ }
+
+ /* flood LSA but first set correct destination */
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_aton(AllSPFRouters, &addr);
+ send_ls_update(iface, addr, data, len);
+ break;
+ case IF_TYPE_BROADCAST:
+ if (iface->state & IF_STA_DRORBDR)
+ inet_aton(AllSPFRouters, &addr);
+ else
+ inet_aton(AllDRouters, &addr);
+ send_ls_update(iface, addr, data, len);
+ 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;
+ }
+ send_ls_update(iface, nbr->addr, data, len);
+ }
+ break;
+ default:
+ fatalx("lsa_flood: unknown interface type");
+ }
+
+
+ return (dont_ack == 2);
+}
+
+int
+send_ls_update(struct iface *iface, struct in_addr addr, void *data, int len)
+{
+ struct sockaddr_in dst;
+ char *buf;
+ char *ptr;
+ u_int32_t nlsa;
+ u_int16_t age;
+ int ret = 0;
+
+ log_debug("send_ls_update: interface %s addr %s",
+ iface->name, inet_ntoa(addr));
+
+ if (iface->passive)
+ return (0);
+
+ /* XXX use buffer API instead for better decoupling */
+ if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("send_ls_update");
+
+ /* set destination */
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ dst.sin_addr.s_addr = addr.s_addr;
+
+ /* OSPF header */
+ gen_ospf_hdr(ptr, iface, PACKET_TYPE_LS_UPDATE);
+ ptr += sizeof(struct ospf_hdr);
+
+ nlsa = htonl(1);
+ memcpy(ptr, &nlsa, sizeof(nlsa));
+ ptr += sizeof(nlsa);
+
+ memcpy(ptr, data, len); /* XXX */
+
+ /* age LSA befor sending it out */
+ memcpy(&age, ptr, sizeof(age));
+ age = ntohs(age);
+ if ((age += iface->transfer_delay) >= MAX_AGE) age = MAX_AGE;
+ age = ntohs(age);
+ memcpy(ptr, &age, sizeof(age));
+
+ ptr += len;
+
+ /* update authentication and calculate checksum */
+ auth_gen(buf, ptr - buf, iface);
+
+ if ((ret = send_packet(iface, buf, (ptr - buf), &dst)) == -1)
+ log_warnx("send_ls_update: error sending packet on "
+ "interface %s", iface->name);
+
+ free(buf);
+ return (ret);
+}
+
+void
+recv_ls_update(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct lsa_hdr lsa;
+ u_int32_t nlsa;
+
+ log_debug("recv_ls_update: neighbor ID %s", inet_ntoa(nbr->id));
+
+ 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, -1,
+ 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");
+ }
+
+ return;
+}
+
+/* link state retransmit list */
+int
+ls_retrans_list_add(struct nbr *nbr, struct lsa_hdr *lsa)
+{
+ struct lsa_entry *le;
+ struct lsa_ref *ref;
+
+ ref = lsa_cache_get(lsa);
+ if (ref == NULL)
+ return (1);
+
+ if ((le = calloc(1, sizeof(*le))) == NULL)
+ fatal("ls_retrans_list_add");
+
+ le->le_ref = ref;
+ TAILQ_INSERT_TAIL(&nbr->ls_retrans_list, le, entry);
+
+ return (0);
+}
+
+int
+ls_retrans_list_del(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
+{
+ struct lsa_entry *le;
+
+ le = ls_retrans_list_get(nbr, lsa_hdr);
+ if (le == 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);
+ }
+
+ log_warnx("ls_retrans_list_del: invalid LS ack received, neigbor %s",
+ inet_ntoa(nbr->id));
+
+ 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_free(struct nbr *nbr, struct lsa_entry *le)
+{
+ TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
+
+ 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);
+}
+
+bool
+ls_retrans_list_empty(struct nbr *nbr)
+{
+ return (TAILQ_EMPTY(&nbr->ls_retrans_list));
+}
+
+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 *);
+struct lsa_cache_head *lsa_cache_hash(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;
+
+ 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);
+ ref->stamp = time(NULL);
+ ref->len = len;
+ ref->refcnt = 1;
+
+ head = lsa_cache_hash(&ref->hdr);
+ 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 = lsa_cache_hash(lsa_hdr);
+ LIST_FOREACH(ref, head, entry) {
+ if (ref->hdr.type == lsa_hdr->type &&
+ ref->hdr.ls_id == lsa_hdr->ls_id &&
+ ref->hdr.adv_rtr == lsa_hdr->adv_rtr &&
+ ref->hdr.seq_num == lsa_hdr->seq_num &&
+ ref->hdr.ls_chksum == lsa_hdr->ls_chksum) {
+ /* found match */
+ return (ref);
+ }
+ }
+ return (NULL);
+}
+
+struct lsa_cache_head *
+lsa_cache_hash(struct lsa_hdr *lsa_hdr)
+{
+ u_int32_t hash = 8271;
+
+ hash ^= lsa_hdr->type;
+ hash ^= lsa_hdr->ls_id;
+ hash ^= lsa_hdr->adv_rtr;
+ hash ^= lsa_hdr->seq_num;
+ hash ^= lsa_hdr->ls_chksum;
+ hash &= lsacache.hashmask;
+
+ return (&lsacache.hashtbl[hash]);
+}
diff --git a/usr.sbin/ospfd/neighbor.c b/usr.sbin/ospfd/neighbor.c
new file mode 100644
index 00000000000..3d60e9417a5
--- /dev/null
+++ b/usr.sbin/ospfd/neighbor.c
@@ -0,0 +1,698 @@
+/* $OpenBSD: neighbor.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 "ospfd.h"
+#include "ospf.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;
+
+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, 0, 0},
+ {NBR_STA_INIT, NBR_EVT_1_WAY_RCVD, 0, 0},
+ {NBR_STA_DOWN, NBR_EVT_HELLO_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_INIT},
+ {NBR_STA_DOWN, NBR_EVT_STRT, NBR_ACT_STRT, NBR_STA_ATTEMPT},
+ {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, 0, 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, NBR_STA_2_WAY},
+ {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, 0, -1, 0},
+};
+
+const char * const nbr_event_names[] = {
+ "NOTHING",
+ "HELLO_RECEIVED",
+ "START",
+ "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",
+ "START",
+ "RESET_INACTIVITY_TIMER",
+ "START_INACTIVITY_TIMER",
+ "EVAL",
+ "SNAPSHOT",
+ "SNAPSHOT_DONE",
+ "EXCHANGE_DONE",
+ "ADJ_OK",
+ "RESET_DD",
+ "DELETE",
+ "CLEAR_LISTS"
+};
+
+int
+nbr_fsm(struct nbr *nbr, enum nbr_event event)
+{
+ 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) {
+ /* XXX event outside of the defined fsm, ignore it. */
+ log_debug("nbr_fsm: neighbor ID %s, "
+ "event %s not expected in state %s",
+ inet_ntoa(nbr->id), nbr_event_name(event),
+ nbr_state_name(old_state));
+ return (0);
+ }
+
+ switch (nbr_fsm_tbl[i].action) {
+ case NBR_ACT_STRT:
+ ret = nbr_act_start(nbr);
+ break;
+ 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;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ if (ret) {
+ log_debug("nbr_fsm: error changing state for neighbor ID %s, "
+ "event %s, state %s", inet_ntoa(nbr->id),
+ nbr_event_name(event), nbr_state_name(old_state));
+ return (-1);
+ }
+
+ if (new_state != 0)
+ nbr->state = new_state;
+
+ if (nbr->state != old_state)
+ ospfe_imsg_compose_rde(IMSG_NEIGHBOR_CHANGE,
+ nbr->peerid, 0, &new_state, sizeof(new_state));
+
+ if (old_state & ~NBR_STA_PRELIM && new_state & NBR_STA_PRELIM)
+ /* bidirectional communication lost */
+ if_fsm(nbr->iface, IF_EVT_NBR_CHNG);
+
+ if (old_state != nbr->state) {
+ nbr->stats.sta_chng++;
+ log_debug("nbr_fsm: event %s resulted in action %s and "
+ "changing state for neighbor ID %s from %s to %s",
+ nbr_event_name(event),
+ nbr_action_name(nbr_fsm_tbl[i].action),
+ inet_ntoa(nbr->id), nbr_state_name(old_state),
+ nbr_state_name(nbr->state));
+ }
+
+ return (ret);
+}
+
+void
+nbr_init(u_int32_t hashsize)
+{
+ u_int32_t hs, i;
+
+ for (hs = 1; hs < hashsize; hs <<= 1)
+ ;
+ nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head));
+ if (nbrtable.hashtbl == NULL)
+ fatal("nbr_init");
+
+ for (i = 0; i < hs; i++)
+ LIST_INIT(&nbrtable.hashtbl[i]);
+
+ nbrtable.hashmask = hs - 1;
+}
+
+struct nbr *
+nbr_new(u_int32_t nbr_id, struct iface *iface, int self)
+{
+ struct nbr_head *head;
+ struct nbr *nbr = NULL;
+ struct rde_nbr rn;
+
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal("nbr_new");
+
+ nbr->state = NBR_STA_DOWN;
+ nbr->master = true;
+ 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;
+ nbr->addr.s_addr = iface->addr.s_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->adj_timer, nbr_adj_timer, nbr);
+
+ log_debug("nbr_new: neighbor ID %s, peerid %lu",
+ inet_ntoa(nbr->id), nbr->peerid);
+
+ 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);
+}
+
+int
+nbr_del(struct nbr *nbr)
+{
+ log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id),
+ nbr->peerid);
+
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ ospfe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0);
+
+ /* 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);
+
+ return (0);
+}
+
+struct nbr *
+nbr_find_peerid(u_int32_t peerid)
+{
+ struct nbr_head *head;
+ struct nbr *nbr;
+
+ head = NBR_HASH(peerid);
+
+ LIST_FOREACH(nbr, head, hash) {
+ if (nbr->peerid == peerid)
+ return (nbr);
+ }
+
+ return (NULL);
+}
+
+struct nbr *
+nbr_find_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 */
+void
+nbr_itimer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ log_debug("nbr_itimer: %s", inet_ntoa(nbr->id));
+
+ nbr_fsm(nbr, NBR_EVT_ITIMER);
+}
+
+int
+nbr_start_itimer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ log_debug("nbr_start_itimer: %s", inet_ntoa(nbr->id));
+
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->dead_interval;
+
+ return (evtimer_add(&nbr->inactivity_timer, &tv));
+}
+
+int
+nbr_stop_itimer(struct nbr *nbr)
+{
+ return (evtimer_del(&nbr->inactivity_timer));
+}
+
+int
+nbr_reset_itimer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->dead_interval;
+
+ return (evtimer_add(&nbr->inactivity_timer, &tv));
+}
+
+void
+nbr_adj_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ if (nbr->state != NBR_STA_FULL) {
+ log_debug("nbr_adj_timer: failed to form adjacency");
+ nbr_fsm(nbr, NBR_EVT_ADJTMOUT);
+ }
+}
+
+int
+nbr_start_adj_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ log_debug("nbr_start_adj_timer: %s", inet_ntoa(nbr->id));
+
+ timerclear(&tv);
+ tv.tv_sec = DEFAULT_ADJ_TMOUT;
+
+ return (evtimer_add(&nbr->adj_timer, &tv));
+}
+
+/* actions */
+int
+nbr_act_start(struct nbr *nbr)
+{
+ log_debug("nbr_act_start: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return (-1);
+}
+
+int
+nbr_act_reset_itimer(struct nbr *nbr)
+{
+ if (nbr_reset_itimer(nbr)) {
+ log_warnx("nbr_act_reset_itimer: cannot schedule inactivity "
+ "timer, neighbor ID %s", inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+nbr_act_start_itimer(struct nbr *nbr)
+{
+ if (nbr_start_itimer(nbr)) {
+ log_warnx("nbr_act_start_itimer: cannot schedule inactivity "
+ "timer, neighbor ID %s",
+ inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ 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:
+ break;
+ case IF_TYPE_BROADCAST:
+ case IF_TYPE_NBMA:
+ /*
+ * if neighbor is dr, bdr or router self is dr or bdr
+ * start forming adjacancy
+ */
+ 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)
+{
+ log_debug("nbr_act_eval: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (!nbr_adj_ok(nbr)) {
+ nbr->state = NBR_STA_2_WAY;
+ return (0);
+ }
+
+ nbr->state = NBR_STA_XSTRT;
+ nbr->master = true;
+ nbr->dd_seq_num++; /* as per RFC */
+ /* initial db negotiation */
+ start_db_tx_timer(nbr);
+
+ return (0);
+}
+
+int
+nbr_act_snapshot(struct nbr *nbr)
+{
+ log_debug("nbr_act_snapshot: neighbor ID %s", inet_ntoa(nbr->id));
+
+ stop_db_tx_timer(nbr);
+ nbr_start_adj_timer(nbr);
+
+ ospfe_imsg_compose_rde(IMSG_DB_SNAPSHOT, nbr->peerid, 0, NULL, 0);
+
+ return (0);
+}
+
+int
+nbr_act_exchange_done(struct nbr *nbr)
+{
+ log_debug("nbr_act_exchange_done: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (nbr->master)
+ stop_db_tx_timer(nbr);
+
+ if (ls_req_list_empty(nbr) && nbr->state == NBR_STA_XCHNG) {
+ nbr->state = NBR_STA_FULL;
+ return (0);
+ }
+
+ nbr->state = NBR_STA_LOAD;
+ start_ls_req_tx_timer(nbr);
+
+ return (0);
+}
+
+int
+nbr_act_adj_ok(struct nbr *nbr)
+{
+ log_debug("nbr_act_adj_ok: neighbor ID %s", inet_ntoa(nbr->id));
+
+ 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)
+{
+ log_debug("nbr_act_restart_dd: neighbor ID %s", inet_ntoa(nbr->id));
+
+ nbr->master = true;
+ nbr->dd_seq_num += arc4random() & 0xffff;
+
+ return (nbr_act_clear_lists(nbr));
+}
+
+int
+nbr_act_delete(struct nbr *nbr)
+{
+ log_debug("nbr_act_delete: neighbor ID %s", inet_ntoa(nbr->id));
+
+ /* stop timers */
+ if (nbr_stop_itimer(nbr)) {
+ log_warnx("nbr_act_delete: error removing inactivity timer, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ /* clear dr and bdr */
+ nbr->dr.s_addr = 0;
+ nbr->bdr.s_addr = 0;
+
+ return (nbr_act_clear_lists(nbr));
+}
+
+int
+nbr_act_clear_lists(struct nbr *nbr)
+{
+ log_debug("nbr_act_clear_lists: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (stop_db_tx_timer(nbr)) {
+ log_warnx("nbr_act_delete: error removing db_tx_timer, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ if (stop_ls_req_tx_timer(nbr)) {
+ log_warnx("nbr_act_delete: error removing lsreq_tx_timer, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ return (-1);
+ }
+
+ /* 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->iface->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;
+
+ /* XXX */
+ nctl.ls_retrans_lst_cnt = 0;
+ TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry)
+ nctl.ls_retrans_lst_cnt++;
+
+ nctl.nbr_state = nbr->state;
+ 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);
+ nctl.dead_timer = res.tv_sec;
+ } else
+ nctl.dead_timer = 0;
+
+ return (&nctl);
+}
+
+/* names */
+const char *
+nbr_state_name(int state)
+{
+ switch (state) {
+ case NBR_STA_DOWN:
+ return ("DOWN");
+ case NBR_STA_ATTEMPT:
+ return ("ATTEMPT");
+ case NBR_STA_INIT:
+ return ("INIT");
+ case NBR_STA_2_WAY:
+ return ("2-WAY");
+ case NBR_STA_XSTRT:
+ return ("EXSTART");
+ case NBR_STA_SNAP:
+ return ("SNAPSHOT");
+ case NBR_STA_XCHNG:
+ return ("EXCHANGE");
+ case NBR_STA_LOAD:
+ return ("LOADING");
+ case NBR_STA_FULL:
+ return ("FULL");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+nbr_event_name(int event)
+{
+ return (nbr_event_names[event]);
+}
+
+const char *
+nbr_action_name(int action)
+{
+ return (nbr_action_names[action]);
+}
+
+struct 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/ospfd/ospf.h b/usr.sbin/ospfd/ospf.h
new file mode 100644
index 00000000000..7886ffeee23
--- /dev/null
+++ b/usr.sbin/ospfd/ospf.h
@@ -0,0 +1,203 @@
+/* $OpenBSD: ospf.h,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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>
+#include <stdbool.h>
+
+/* misc */
+#define OSPF_VERSION 2
+#define IPPROTO_OSPF 89
+#define AllSPFRouters "224.0.0.5"
+#define AllDRouters "224.0.0.6"
+
+#define PACKET_HDR 100 /* XXX used to calculate the IP payload */
+
+#define DEFAULT_METRIC 1
+#define MIN_METRIC 1
+#define MAX_METRIC 65535
+
+#define DEFAULT_PRIORITY 0 /* XXX force to 0 for now */
+#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_TRANSIT_DELAY 1
+#define MIN_TRANSIT_DELAY 1
+#define MAX_TRANSIT_DELAY 3600
+
+#define DEFAULT_ADJ_TMOUT 60 /* XXX sane value? */
+
+/* OSPF compatibility flags */
+#define OSPF_OPTION_E 0x02
+#define OSPF_OPTION_MC 0x04
+#define OSPF_OPTION_NP 0x08
+#define OSPF_OPTION_EA 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
+
+/* OSPF auth types */
+#define AUTH_TYPE_NONE 0
+#define AUTH_TYPE_SIMPLE 1
+#define AUTH_TYPE_CRYPT 2
+
+#define MIN_AUTHTYPE 0
+#define MAX_AUTHTYPE 2
+
+/* 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 INIT_SEQ_NUM 0x80000001
+#define MAX_SEQ_NUM 0x7fffffff
+
+/* OSPF header */
+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_int16_t auth_type;
+ union {
+ char simple[8];
+ u_int64_t crypt;
+ } auth_key;
+};
+
+/* Hello header (type 1) */
+struct hello_hdr {
+ u_int32_t mask;
+ u_int16_t hello_interval;
+ u_int8_t opts;
+ u_int8_t rtr_priority;
+ u_int32_t rtr_dead_interval;
+ u_int32_t d_rtr;
+ u_int32_t bd_rtr;
+};
+
+/* Database Description header (type 2) */
+struct db_dscrp_hdr {
+ u_int16_t iface_mtu;
+ u_int8_t opts;
+ 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_int32_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 */
+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_sum {
+ u_int32_t mask;
+ u_int32_t metric; /* only lower 24 bit */
+};
+
+struct lsa_hdr {
+ u_int16_t age;
+ u_int8_t opts;
+ u_int8_t type;
+ u_int32_t ls_id;
+ u_int32_t adv_rtr;
+ 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;
+ } data;
+};
+
+#endif /* !_OSPF_H_ */
diff --git a/usr.sbin/ospfd/ospfd.8 b/usr.sbin/ospfd/ospfd.8
new file mode 100644
index 00000000000..5e94a860ba4
--- /dev/null
+++ b/usr.sbin/ospfd/ospfd.8
@@ -0,0 +1,69 @@
+.\" $OpenBSD: ospfd.8,v 1.1 2005/01/28 14:05:40 claudio Exp $
+.\"
+.\" Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+.\"
+.\" 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 November 4, 2004
+.Dt OSPFD 8
+.Os
+.Sh NAME
+.Nm ospfd
+.Nd "Open Shortest Path First daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is an Open Shortest Path First
+.Pq OSPF
+daemon which manages routing tables.
+Its main purpose is to keep AS internal routing tables in sync.
+.Pp
+A running
+.Nm
+can be controlled with the
+.Xr ospfctl 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.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/ospfd.confXXXX" -compact
+.It /etc/ospfd.conf
+.Nm
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr ospfctl 8
+.Rs
+.%R RFC 2328
+.%T "OSPF Version 2"
+.%D April 1998
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 3.7 .
diff --git a/usr.sbin/ospfd/ospfd.c b/usr.sbin/ospfd/ospfd.c
new file mode 100644
index 00000000000..8367ad569b1
--- /dev/null
+++ b/usr.sbin/ospfd/ospfd.c
@@ -0,0 +1,456 @@
+/* $OpenBSD: ospfd.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com>
+ * 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 <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 <signal.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "ospfd.h"
+#include "ospf.h"
+#include "ospfe.h"
+#include "control.h"
+#include "log.h"
+#include "rde.h"
+
+void main_sig_handler(int, short, void *);
+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 *);
+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 check_file_secrecy(int, const char *);
+
+int pipe_parent2ospfe[2];
+int pipe_parent2rde[2];
+int pipe_ospfe2rde[2];
+
+volatile sig_atomic_t main_quit = 0;
+
+struct ospfd_conf *conf = NULL;
+struct imsgbuf *ibuf_ospfe;
+struct imsgbuf *ibuf_rde;
+
+pid_t ospfe_pid;
+pid_t rde_pid;
+
+void
+main_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * signal handler rules don't apply, libevent decouples for us
+ */
+
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ ospfd_shutdown();
+ /* NOTREACHED */
+ case SIGCHLD:
+ if (check_child(ospfe_pid, "ospfe engine")) {
+ ospfe_pid = 0;
+ ospfd_shutdown();
+ }
+ if (check_child(rde_pid, "route decision engine")) {
+ rde_pid = 0;
+ ospfd_shutdown();
+ }
+ break;
+ case SIGHUP:
+ /* reconfigure */
+ /* ... */
+ break;
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-d] [-f file]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ char *conffile;
+ int ch;
+ int debug = 0;
+
+ conffile = CONF_FILE;
+ ospfd_process = PROC_MAIN;
+
+ /* start logging */
+ log_init(1);
+
+ while ((ch = getopt(argc, argv, "dhf:")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ log_init(debug);
+
+ /* parse config file */
+ if ((conf = parse_config(conffile, OSPFD_OPT_VERBOSE)) == NULL )
+ exit(1);
+
+ /* check for root priviledges */
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ /* check for ospfd user */
+ if (getpwnam(OSPFD_USER) == NULL)
+ errx(1, "unknown user %s", OSPFD_USER);
+
+ endpwent();
+
+ 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);
+
+ event_init();
+
+ if (if_init(conf))
+ log_info("error initializing interfaces");
+ else
+ log_debug("interfaces initialized");
+
+ /* start children */
+ rde_pid = rde(conf, pipe_parent2rde, pipe_ospfe2rde, pipe_parent2ospfe);
+ ospfe_pid = ospfe(conf, pipe_parent2ospfe, pipe_ospfe2rde,
+ pipe_parent2rde);
+
+ /* show who we are */
+ setproctitle("parent");
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGINT, main_sig_handler, NULL);
+ signal_set(&ev_sighup, SIGTERM, main_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+
+ /* setup pipes to children */
+ close(pipe_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(!(conf->flags & OSPFD_FLAG_NO_FIB_UPDATE)) == -1)
+ ospfd_shutdown();
+
+ show_config(conf);
+
+ 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();
+ if_shutdown(conf);
+
+ 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);
+
+ log_info("terminating");
+ exit(0);
+}
+
+int
+check_child(pid_t pid, const char *pname)
+{
+ int status;
+
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ if (WIFEXITED(status)) {
+ log_warnx("lost child: %s exited", pname);
+ return (1);
+ }
+ if (WIFSIGNALED(status)) {
+ log_warnx("lost child: %s terminated; signal %d",
+ pname, WTERMSIG(status));
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* imsg handling */
+void
+main_dispatch_ospfe(int fd, short event, void *bula)
+{
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ int n;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_RELOAD:
+ log_debug("main_dispatch_ospfe: IMSG_CTL_RELOAD");
+ /* reconfig */
+ break;
+ case IMSG_CTL_SHOW_INTERFACE:
+ kr_show_route(&imsg);
+ break;
+ case IMSG_CTL_FIB_COUPLE:
+ kr_fib_couple();
+ break;
+ case IMSG_CTL_FIB_DECOUPLE:
+ kr_fib_decouple();
+ break;
+ default:
+ log_debug("main_dispatch_ospfe: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+main_dispatch_rde(int fd, short event, void *bula)
+{
+ struct imsgbuf *ibuf = bula;
+ struct imsg imsg;
+ int n;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ default:
+ log_debug("main_dispatch_rde: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+main_imsg_compose_ospfe(int type, pid_t pid, void *data, u_int16_t datalen)
+{
+
+ imsg_compose(ibuf_ospfe, type, 0, pid, -1, 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, -1, data, datalen);
+}
+
+
+void
+send_nexthop_update(struct kroute_nexthop *msg)
+{
+ char *gw = NULL;
+
+ if (msg->gateway.s_addr)
+ if (asprintf(&gw, ": via %s",
+ inet_ntoa(msg->gateway)) == -1) {
+ log_warn("send_nexthop_update");
+ main_quit = 1;
+ }
+
+ log_info("nexthop %s now %s%s%s", inet_ntoa(msg->nexthop),
+ msg->valid ? "valid" : "invalid",
+ msg->connected ? ": directly connected" : "",
+ msg->gateway.s_addr ? gw : "");
+
+ free(gw);
+
+ if (imsg_compose(ibuf_rde, IMSG_NEXTHOP_UPDATE, 0, 0, -1,
+ msg, sizeof(struct kroute_nexthop)) == -1)
+ main_quit = 1;
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ log_warn("cannot stat %s", fname);
+ return (-1);
+ }
+
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ log_warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+
+ if (st.st_mode & (S_IRWXG | S_IRWXO)) {
+ log_warnx("%s: group/world readable/writeable", fname);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* this needs to be added here so that 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);
+}
diff --git a/usr.sbin/ospfd/ospfd.h b/usr.sbin/ospfd/ospfd.h
new file mode 100644
index 00000000000..b4ccb312825
--- /dev/null
+++ b/usr.sbin/ospfd/ospfd.h
@@ -0,0 +1,418 @@
+/* $OpenBSD: ospfd.h,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com>
+ * 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 _OSPFD_H_
+#define _OSPFD_H_
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <event.h>
+#include <stdbool.h>
+
+#define CONF_FILE "/etc/ospfd.conf"
+#define OSPFD_SOCKET "/var/run/ospfd.sock"
+#define OSPFD_USER "_ospfd"
+
+#define NBR_HASHSIZE 128
+#define LSA_HASHSIZE 512
+
+#define READ_BUF_SIZE 65535
+#define RT_BUF_SIZE 16384
+
+#define OSPFD_FLAG_NO_FIB_UPDATE 0x0001
+
+#define F_OSPFD_INSERTED 0x0001
+#define F_KERNEL 0x0002
+#define F_CONNECTED 0x0004
+#define F_NEXTHOP 0x0008
+#define F_DOWN 0x0010
+#define F_STATIC 0x0020
+#define F_LONGER 0x0040
+#define F_REJECT 0x0080
+#define F_BLACKHOLE 0x0100
+
+/* buffer */
+struct buf {
+ TAILQ_ENTRY(buf) entry;
+ u_char *buf;
+ ssize_t size;
+ ssize_t wpos;
+ ssize_t rpos;
+ int fd;
+};
+
+struct msgbuf {
+ u_int32_t queued;
+ int fd;
+ TAILQ_HEAD(, buf) bufs;
+};
+
+#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE 8192
+
+struct buf_read {
+ u_char buf[READ_BUF_SIZE];
+ u_char *rptr;
+ ssize_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_NBR,
+ IMSG_CTL_FIB_COUPLE,
+ IMSG_CTL_FIB_DECOUPLE,
+ IMSG_CTL_AREA,
+ IMSG_CTL_END,
+ IMSG_NEXTHOP_ADD,
+ IMSG_NEXTHOP_REMOVE,
+ IMSG_NEXTHOP_UPDATE,
+ IMSG_IFINFO,
+ IMSG_NEIGHBOR_UP,
+ IMSG_NEIGHBOR_DOWN,
+ IMSG_NEIGHBOR_CHANGE,
+ IMSG_DD,
+ IMSG_DB_SNAPSHOT,
+ IMSG_DB_END,
+ IMSG_LS_REQ,
+ IMSG_LS_UPD,
+ IMSG_LS_ACK,
+ IMSG_LS_FLOOD,
+ IMSG_LS_BADREQ,
+ IMSG_LS_MAXAGE
+};
+
+struct imsg_hdr {
+ enum imsg_type type;
+ u_int16_t len;
+ u_int32_t peerid;
+ pid_t pid;
+};
+
+struct imsg {
+ struct imsg_hdr hdr;
+ void *data;
+};
+
+struct imsg_fd {
+ TAILQ_ENTRY(imsg_fd) entry;
+ int fd;
+};
+
+/* 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; */
+ u_int32_t stub_default_cost;
+
+ u_int32_t dead_interval;
+ u_int16_t transfer_delay;
+ u_int16_t hello_interval;
+ u_int16_t rxmt_interval;
+ u_int16_t metric;
+ u_int8_t priority;
+
+ bool transit;
+ bool stub;
+};
+
+/* interface states */
+#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_UP,
+ IF_EVT_WTIMER,
+ IF_EVT_BACKUP_SEEN,
+ IF_EVT_NBR_CHNG,
+ IF_EVT_LOOP,
+ IF_EVT_UNLOOP,
+ IF_EVT_DOWN
+};
+
+static const char * const if_event_names[] = {
+ "UP",
+ "WAITTIMER",
+ "BACKUPSEEN",
+ "NEIGHBORCHANGE",
+ "LOOP",
+ "UNLOOP",
+ "DOWN"
+};
+
+/* interface actions */
+enum iface_action {
+ 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
+};
+
+/* auth types */
+enum auth_type {
+ AUTH_NONE,
+ AUTH_SIMPLE,
+ AUTH_CRYPT
+};
+
+/* 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];
+ struct in_addr addr;
+ struct in_addr mask;
+ struct in_addr rtr_id;
+ char *auth_key;
+ struct nbr *dr; /* designated router */
+ struct nbr *bdr; /* backup designated router */
+ struct nbr *self;
+ struct area *area;
+
+ u_int32_t baudrate;
+ u_int32_t dead_interval;
+ u_int32_t ls_ack_cnt;
+ unsigned int ifindex;
+ int fd;
+ int state;
+ int mtu;
+ u_int16_t flags;
+ u_int16_t transfer_delay;
+ u_int16_t hello_interval;
+ u_int16_t rxmt_interval;
+ u_int16_t metric;
+ enum iface_type type;
+ enum auth_type auth_type;
+ u_int8_t linkstate;
+ u_int8_t priority;
+
+ bool passive;
+};
+
+/* ospf_conf */
+enum {
+ PROC_MAIN,
+ PROC_OSPF_ENGINE,
+ PROC_RDE_ENGINE
+} ospfd_process;
+
+struct ospfd_conf {
+ struct event ev;
+ struct in_addr rtr_id;
+ u_int32_t opts;
+#define OSPFD_OPT_VERBOSE 0x00000001
+#define OSPFD_OPT_VERBOSE2 0x00000002
+#define OSPFD_OPT_NOACTION 0x00000004
+ int maxdepth;
+ LIST_HEAD(, area) area_list;
+
+ struct lsa_tree lsa_tree;
+ int ospf_socket;
+ int flags;
+ int options; /* OSPF options */
+};
+
+/* kroute */
+struct kroute {
+ struct in_addr prefix;
+ u_int8_t prefixlen;
+ struct in_addr nexthop;
+ u_int16_t flags;
+ u_short ifindex;
+};
+
+struct kroute_nexthop {
+ struct in_addr nexthop;
+ u_int8_t valid;
+ u_int8_t connected;
+ struct in_addr gateway;
+ struct kroute kr;
+};
+
+struct kif {
+ u_short ifindex;
+ int flags;
+ char ifname[IF_NAMESIZE];
+ u_int8_t media_type;
+ u_int8_t link_state;
+ u_long baudrate;
+ u_int8_t nh_reachable; /* for nexthop verification */
+};
+
+/* control data structures */
+struct ctl_iface {
+ char name[IF_NAMESIZE];
+ struct in_addr addr;
+ struct in_addr mask;
+ struct in_addr area;
+ struct in_addr rtr_id;
+ struct in_addr dr_id;
+ struct in_addr dr_addr;
+ struct in_addr bdr_id;
+ struct in_addr bdr_addr;
+ time_t hello_timer;
+ 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 transfer_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 priority;
+ bool passive;
+};
+
+struct ctl_nbr {
+ char name[IF_NAMESIZE];
+ struct in_addr id;
+ struct in_addr addr;
+ struct in_addr dr;
+ struct in_addr bdr;
+ struct in_addr area;
+ time_t dead_timer;
+ 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;
+};
+
+void show_config(struct ospfd_conf *xconf);
+
+/* area.c */
+struct area *area_new(void);
+int area_del(struct area *);
+struct area *area_find(struct ospfd_conf *, struct in_addr);
+
+/* buffer.c */
+struct buf *buf_open(ssize_t);
+int buf_add(struct buf *, void *, ssize_t);
+void *buf_reserve(struct buf *, ssize_t);
+int buf_close(struct msgbuf *, struct buf *);
+int buf_write(int, struct buf *);
+void buf_free(struct buf *);
+void msgbuf_init(struct msgbuf *);
+void msgbuf_clear(struct msgbuf *);
+int msgbuf_write(struct msgbuf *);
+int msgbuf_writebound(struct msgbuf *);
+int msgbuf_unbounded(struct msgbuf *msgbuf);
+
+/* 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 *));
+int imsg_read(struct imsgbuf *);
+int imsg_get(struct imsgbuf *, struct imsg *);
+int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t, int,
+ 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 *);
+int imsg_get_fd(struct imsgbuf *);
+void imsg_event_add(struct imsgbuf *); /* needs to be provided externaly */
+
+/* in_cksum.c */
+int in_cksum(void *, int);
+
+/* iso_cksum.c */
+u_int16_t iso_cksum(void *, u_int16_t, u_int16_t);
+
+/* kroute.c */
+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 *);
+int kr_nexthop_add(struct in_addr);
+void kr_nexthop_delete(struct in_addr);
+void kr_show_route(struct imsg *);
+void kr_ifinfo(char *);
+void send_nexthop_update(struct kroute_nexthop *);
+
+/* ospfd.c */
+void send_nexthop_update(struct kroute_nexthop *);
+
+#endif /* _OSPFD_H_ */
diff --git a/usr.sbin/ospfd/ospfe.c b/usr.sbin/ospfd/ospfe.c
new file mode 100644
index 00000000000..debad28f6d6
--- /dev/null
+++ b/usr.sbin/ospfd/ospfe.c
@@ -0,0 +1,725 @@
+/* $OpenBSD: ospfe.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <event.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ospf.h"
+#include "ospfd.h"
+#include "ospfe.h"
+#include "control.h"
+#include "log.h"
+
+void ospfe_sig_handler(int, short, void *);
+void ospfe_shutdown(void);
+void orig_lsa(void);
+void orig_rtr_lsa(struct area *);
+void orig_net_lsa(struct iface *);
+
+volatile sig_atomic_t ospfe_quit = 0;
+struct ospfd_conf *oeconf = NULL;
+struct imsgbuf *ibuf_main;
+struct imsgbuf *ibuf_rde;
+struct ctl_conn *ctl_conn;
+
+void
+ospfe_sig_handler(int sig, short event, void *bula)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ ospfe_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+/* ospf engine */
+pid_t
+ospfe(struct ospfd_conf *xconf, int pipe_parent2ospfe[2], int pipe_ospfe2rde[2],
+ int pipe_parent2rde[2])
+{
+ struct area *area = NULL;
+ struct iface *iface = NULL;
+ 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");
+
+ oeconf = xconf;
+
+ if ((pw = getpwnam(OSPFD_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) ||
+ setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
+ seteuid(pw->pw_uid) || setuid(pw->pw_uid)) {
+ fatal("can't drop privileges");
+ }
+
+ endpwent();
+
+ 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);
+
+ /* 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);
+
+ /* listen on ospfd control socket */
+ TAILQ_INIT(&ctl_conns);
+ control_listen();
+
+ /* start interfaces */
+ LIST_FOREACH(area, &oeconf->area_list, entry) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if (if_fsm(iface, IF_EVT_UP)) {
+ log_debug("error starting interface %s",
+ iface->name);
+ }
+ }
+ }
+
+ orig_lsa();
+
+ event_dispatch();
+
+ ospfe_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+void
+ospfe_shutdown(void)
+{
+ struct area *area;
+ struct iface *iface;
+
+ /* stop all interfaces and remove all areas */
+ LIST_FOREACH(area, &oeconf->area_list, entry) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if (if_fsm(iface, IF_EVT_DOWN)) {
+ log_debug("error stopping interface %s",
+ iface->name);
+ }
+ }
+ area_del(area);
+ }
+
+ /* 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);
+
+ 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, -1, 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, -1, data, datalen));
+}
+
+void
+ospfe_dispatch_main(int fd, short event, void *bula)
+{
+ struct imsg imsg;
+ struct imsgbuf *ibuf = bula;
+ struct area *area = NULL;
+ struct iface *iface = NULL;
+ struct kif *kif;
+ int n;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("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;
+
+ log_debug("ospfe_dispatch_main: ifindex %d changed",
+ kif->ifindex);
+
+ LIST_FOREACH(area, &oeconf->area_list, entry) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if (kif->ifindex == iface->ifindex) {
+ if (kif->flags & IFF_UP) {
+ if_fsm(iface,
+ IF_EVT_UP);
+ } else {
+ if_fsm(iface,
+ IF_EVT_DOWN);
+ }
+ }
+ }
+ }
+
+ break;
+ case IMSG_CTL_END:
+ log_debug("ospfe_dispatch_main: 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);
+ }
+ imsg_event_add(ibuf);
+}
+
+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;
+ int n, noack = 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 */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("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)
+ fatalx("ospfe_dispatch_rde: "
+ "neighbor not found");
+
+ log_debug("ospfe_dispatch_rde: IMSG_DD, "
+ "neighbor id %s, len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ /* put these on my ls_req_list for retrival */
+ lhp = lsa_hdr_new();
+ memcpy(lhp, imsg.data, sizeof(*lhp));
+ ls_req_list_add(nbr, lhp);
+ break;
+ case IMSG_DB_SNAPSHOT:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ fatalx("ospfe_dispatch_rde: "
+ "neighbor not found");
+
+ log_debug("ospfe_dispatch_rde: IMSG_DB_SNAPSHOT, "
+ "neighbor id %s, len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ /* 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)
+ fatalx("ospfe_dispatch_rde: "
+ "neighbor not found");
+
+ log_debug("ospfe_dispatch_rde: IMSG_DB_END, "
+ "neighbor id %s, len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ /* 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)
+ fatalx("ospfe_dispatch_rde: "
+ "neighbor not found");
+
+ log_debug("ospfe_dispatch_rde: IMSG_LS_FLOOD, "
+ "neighbor id %s, len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ 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, l);
+ }
+ }
+ } 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, l);
+ }
+ /* 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 */
+ noack = 1;
+ }
+
+ if (!noack && 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)
+ fatalx("ospfe_dispatch_rde: "
+ "neighbor not found");
+
+ log_debug("ospfe_dispatch_rde: IMSG_LS_UPD, "
+ "neighbor id %s, len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ if (nbr->iface->self == nbr)
+ break;
+
+ memcpy(&age, imsg.data, sizeof(age));
+ if (ntohs(age) >= MAX_AGE) {
+ /* add to retransmit list */
+ ref = lsa_cache_add(imsg.data, l);
+ ls_retrans_list_add(nbr, imsg.data); /* XXX */
+ lsa_cache_put(ref, nbr);
+ }
+
+ /* send direct don't add to retransmit list */
+ send_ls_update(nbr->iface, nbr->addr, imsg.data, l);
+ break;
+ case IMSG_LS_ACK:
+ /*
+ * IMSG_LS_ACK is used in two cases:
+ * 1. LSA was a duplicate
+ * 2. LSA's age is MaxAge and there is no current
+ * instance in the DB plus no neighbor is state
+ * Exchange or Loading
+ */
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL)
+ fatalx("ospfe_dispatch_rde: "
+ "neighbor not found");
+
+ log_debug("ospfe_dispatch_rde: IMSG_LS_ACK, "
+ "neighbor id %s, len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ if (nbr->iface->self == nbr)
+ break;
+
+ /* TODO for case one check for implied acks */
+
+ /* 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)
+ fatalx("ospfe_dispatch_rde: "
+ "neighbor not found");
+
+ log_debug("ospfe_dispatch_rde: IMSG_LS_BADREQ, "
+ "neighbor id %s, len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ if (nbr->iface->self == nbr)
+ fatalx("ospfe_dispatch_rde: "
+ "dummy neighbor got BADREQ");
+
+ nbr_fsm(nbr, NBR_EVT_BAD_LS_REQ);
+ break;
+ case IMSG_CTL_SHOW_DATABASE:
+ case IMSG_CTL_AREA:
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("ospfe_dispatch_rde: error handling imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+orig_lsa(void)
+{
+ struct area *area;
+ struct iface *iface;
+
+ LIST_FOREACH(area, &oeconf->area_list, entry) {
+ orig_rtr_lsa(area);
+ LIST_FOREACH(iface, &area->iface_list, entry)
+ orig_net_lsa(iface);
+ }
+}
+
+void
+orig_rtr_lsa(struct area *area)
+{
+ struct lsa *lsa;
+ struct iface *iface;
+ char *buf;
+ struct lsa_rtr_link *rtr_link;
+ int num_links = 0;
+ u_int16_t len;
+
+ log_debug("orig_rtr_lsa: area %s", inet_ntoa(area->id));
+
+ if ((buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("orig_rtr_lsa");
+
+ /* LSA header */
+ lsa = (struct lsa *)buf;
+ lsa->hdr.age = htons(DEFAULT_AGE); /* XXX */
+ lsa->hdr.opts = oeconf->options; /* XXX */
+ 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.ls_chksum = 0; /* updated later */
+ lsa->hdr.len = 0; /* updated later */
+ len = sizeof(struct lsa_hdr) + sizeof(struct lsa_rtr);
+
+ /* links */
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ log_debug("orig_rtr_lsa: interface %s", iface->name);
+
+ rtr_link = (struct lsa_rtr_link *)(buf + len);
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ log_debug("orig_rtr_lsa: not supported, interface %s",
+ iface->name);
+ break;
+ case IF_TYPE_BROADCAST:
+ if (iface->state == IF_STA_DOWN)
+ break;
+
+ if ((iface->state == IF_STA_WAITING) ||
+ iface->dr == NULL) { /* XXX */
+ log_debug("orig_rtr_lsa: stub net, "
+ "interface %s", iface->name);
+
+ rtr_link->id =
+ iface->addr.s_addr & iface->mask.s_addr;
+ rtr_link->data = iface->mask.s_addr;
+ rtr_link->type = LINK_TYPE_STUB_NET;
+ } else {
+ log_debug("orig_rtr_lsa: transit net, "
+ "interface %s", iface->name);
+
+ rtr_link->id = iface->dr->addr.s_addr;
+ rtr_link->data = iface->addr.s_addr;
+ rtr_link->type = LINK_TYPE_TRANSIT_NET;
+ }
+
+ rtr_link->num_tos = 0;
+ rtr_link->metric = htons(iface->metric);
+ num_links++;
+ len += sizeof(struct lsa_rtr_link);
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ log_debug("orig_rtr_lsa: not supported, interface %s",
+ iface->name);
+ break;
+ default:
+ fatalx("orig_rtr_lsa: unknown interface type");
+ }
+ }
+
+ lsa->data.rtr.flags = 0; /* XXX */
+ lsa->data.rtr.dummy = 0;
+ lsa->data.rtr.nlinks = htons(num_links);
+
+ lsa->hdr.len = htons(len);
+
+ lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
+
+ imsg_compose(ibuf_rde, IMSG_LS_UPD,
+ LIST_FIRST(&area->iface_list)->self->peerid, 0, -1, buf,
+ ntohs(lsa->hdr.len));
+}
+
+void
+orig_net_lsa(struct iface *iface)
+{
+ struct lsa *lsa;
+ struct nbr *nbr;
+ char *buf;
+ int num_rtr = 0;
+ u_int16_t len;
+
+ log_debug("orig_net_lsa: iface %s", iface->name);
+
+ /* XXX if not dr quit */
+ /* ... */
+
+ if ((buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("orig_net_lsa");
+
+ /* LSA header */
+ lsa = (struct lsa *)buf;
+ lsa->hdr.age = htons(DEFAULT_AGE); /* XXX */
+ lsa->hdr.opts = oeconf->options; /* XXX */
+ lsa->hdr.type = LSA_TYPE_NETWORK;
+ 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.ls_chksum = 0; /* updated later */
+ lsa->hdr.len = 0; /* updated later */
+ len = sizeof(lsa->hdr);
+
+ lsa->data.net.mask = iface->mask.s_addr;
+ len += sizeof(lsa->data.net.mask);
+
+ /* attached routers */
+ /* fully adjacent neighbors + self */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->state != NBR_STA_FULL) { /* XXX */
+ lsa->data.net.att_rtr[num_rtr] = nbr->id.s_addr;
+ num_rtr++;
+ len += sizeof(nbr->id);
+ }
+ }
+
+ lsa->hdr.len = htons(len);
+
+ lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
+/*
+ imsg_compose(ibuf_rde, IMSG_LS_UPD, iface->self->peerid, 0, -1, buf,
+ ntohs(lsa->hdr.len));
+*/
+}
+
+u_int32_t
+ospfe_router_id(void)
+{
+ return (oeconf->rtr_id.s_addr);
+}
+
+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, -1,
+ 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, -1, nctl,
+ sizeof(struct ctl_nbr));
+ }
+ }
+
+ imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
diff --git a/usr.sbin/ospfd/ospfe.h b/usr.sbin/ospfd/ospfe.h
new file mode 100644
index 00000000000..0e0281cbfbd
--- /dev/null
+++ b/usr.sbin/ospfd/ospfe.h
@@ -0,0 +1,286 @@
+/* $OpenBSD: ospfe.h,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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;
+
+/* 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 0xfffc
+#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_STRT,
+ 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_STRT,
+ 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
+};
+
+struct lsa_entry {
+ TAILQ_ENTRY(lsa_entry) entry;
+ union {
+ struct lsa_hdr *lu_lsa;
+ struct lsa_ref *lu_ref;
+ } le_data;
+};
+#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 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 in_addr addr;
+ struct in_addr 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 peerid; /* unique ID in DB */
+
+ u_int32_t ls_req_cnt;
+
+ int state;
+ u_int8_t priority;
+ u_int8_t options;
+ u_int8_t last_rx_options;
+ u_int8_t last_rx_bits;
+
+ bool master;
+};
+
+/* auth.c */
+int auth_validate(struct ospf_hdr *, const struct iface *);
+int auth_gen(void *, u_int16_t, const struct iface *);
+
+/* 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 *);
+int start_db_tx_timer(struct nbr *);
+int stop_db_tx_timer(struct nbr *);
+
+/* hello.c */
+int send_hello(struct iface *);
+void recv_hello(struct iface *, struct in_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_iface_ctl(struct ctl_conn *, unsigned int);
+void ospfe_nbr_ctl(struct ctl_conn *);
+
+/* interface.c */
+int if_fsm(struct iface *, enum iface_event);
+
+struct iface *if_new(char *, unsigned int);
+int if_del(struct iface *);
+int if_init(struct ospfd_conf *);
+int if_shutdown(struct ospfd_conf *);
+
+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 *);
+
+const char *if_state_name(int);
+const char *if_event_name(int);
+const char *if_action_name(int);
+const char *if_type_name(int);
+const char *if_auth_name(int);
+
+int if_join_group(struct iface *, struct in_addr *);
+int if_leave_group(struct iface *, struct in_addr *);
+int if_set_mcast(struct iface *);
+
+/* lsack.c */
+int delay_lsa_ack(struct iface *, struct lsa_hdr *);
+int send_ls_ack(struct iface *, struct in_addr, void *data, int len);
+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 *);
+bool ls_ack_list_empty(struct iface *);
+void ls_ack_tx_timer(int, short, void *);
+int start_ls_ack_tx_timer(struct iface *);
+int 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 *);
+bool ls_req_list_empty(struct nbr *);
+void ls_req_tx_timer(int, short, void *);
+int start_ls_req_tx_timer(struct nbr *);
+int stop_ls_req_tx_timer(struct nbr *);
+
+/* lsupdate.c */
+int lsa_flood(struct iface *, struct nbr *, struct lsa_hdr *,
+ void *, u_int16_t);
+int send_ls_update(struct iface *, struct in_addr, void *, int);
+void recv_ls_update(struct nbr *, char *, u_int16_t);
+
+int ls_retrans_list_add(struct nbr *, struct lsa_hdr *);
+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 *);
+bool ls_retrans_list_empty(struct nbr *);
+
+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);
+int 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 *);
+int nbr_start_itimer(struct nbr *);
+int nbr_stop_itimer(struct nbr *);
+int nbr_reset_itimer(struct nbr *);
+
+void nbr_adj_timer(int, short, void *);
+int nbr_start_adj_timer(struct nbr *);
+
+int nbr_act_start(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 *);
+
+const char *nbr_state_name(int);
+const char *nbr_event_name(int);
+const char *nbr_action_name(int);
+
+struct lsa_hdr *lsa_hdr_new(void);
+
+/* packet.c */
+void gen_ospf_hdr(void *, struct iface *, u_int8_t);
+int send_packet(struct iface *, char *, int, struct sockaddr_in *);
+void recv_packet(int, short, void *);
+
+#endif /* _OSPFE_H_ */
diff --git a/usr.sbin/ospfd/packet.c b/usr.sbin/ospfd/packet.c
new file mode 100644
index 00000000000..b76d9c22db4
--- /dev/null
+++ b/usr.sbin/ospfd/packet.c
@@ -0,0 +1,288 @@
+/* $OpenBSD: packet.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <event.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "ospfd.h"
+#include "ospf.h"
+#include "log.h"
+#include "ospfe.h"
+
+int ip_hdr_sanity_check(const struct ip *, u_int16_t);
+int ospf_hdr_sanity_check(const struct ip *,
+ struct ospf_hdr *, u_int16_t, const struct iface *);
+struct iface *find_iface(struct ospfd_conf *, struct in_addr);
+
+void
+gen_ospf_hdr(void *buf, struct iface *iface, u_int8_t type)
+{
+ struct ospf_hdr *ospf_hdr = buf;
+
+ ospf_hdr->version = OSPF_VERSION;
+ ospf_hdr->type = type;
+ ospf_hdr->len = 0; /* updated later */
+ ospf_hdr->rtr_id = iface->rtr_id.s_addr;
+ ospf_hdr->area_id = iface->area->id.s_addr;
+ ospf_hdr->chksum = 0; /* updated later */
+ ospf_hdr->auth_type = htons(iface->auth_type);
+}
+
+/* send and receive packets */
+int
+send_packet(struct iface *iface, char *pkt, int len, struct sockaddr_in *dst)
+{
+ if (iface->passive) {
+ log_warnx("send_packet: cannot send packet on passive "
+ "interface %s", iface->name);
+ return (-1);
+ }
+
+ /* set outgoing interface for multicast traffic */
+ if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr)))
+ if (if_set_mcast(iface) == -1) {
+ log_debug("send_packet: error setting multicast "
+ "interface, %s", iface->name);
+ return (-1);
+ }
+
+ if (sendto(iface->fd, pkt, len, 0,
+ (struct sockaddr *)dst, sizeof(*dst)) == -1 ) {
+ log_warnx("send_packet: error sending packet on interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+recv_packet(int fd, short event, void *bula)
+{
+ struct ospfd_conf *xconf = bula;
+ struct ip ip_hdr;
+ struct ospf_hdr *ospf_hdr;
+ struct iface *iface;
+ struct nbr *nbr = NULL;
+ struct in_addr addr;
+ char *buf, *ptr;
+ ssize_t r;
+ u_int16_t len;
+ int l;
+
+ if (event != EV_READ)
+ return;
+
+ /*
+ * XXX I don't like to allocate a buffer for each packet received
+ * and freeing that buffer at the end of the function. It would be
+ * enough to allocate the buffer on startup.
+ */
+ if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("recv_packet");
+
+ if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) {
+ if (errno != EAGAIN) {
+ log_debug("recv_packet: error receiving packet");
+ }
+ goto done;
+ }
+
+ len = (u_int16_t)r;
+
+ /* IP header sanity checks */
+ if (len < sizeof(ip_hdr)) {
+ log_warnx("recv_packet: bad packet size");
+ goto done;
+ }
+ memcpy(&ip_hdr, buf, sizeof(ip_hdr));
+ if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1)
+ goto done;
+ buf += l;
+ len -= l;
+
+ /* find a matching interface */
+ if ((iface = find_iface(xconf, ip_hdr.ip_src)) == NULL) {
+ log_debug("recv_packet: cannot find valid interface");
+ goto done;
+ }
+
+ /*
+ * 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_aton(AllSPFRouters, &addr);
+ if (ip_hdr.ip_dst.s_addr != addr.s_addr) {
+ inet_aton(AllDRouters, &addr);
+ if (ip_hdr.ip_dst.s_addr != addr.s_addr) {
+ if (ip_hdr.ip_dst.s_addr != iface->addr.s_addr) {
+ log_debug("recv_packet: packet sent to wrong "
+ "address %s, interface %s",
+ inet_ntoa(ip_hdr.ip_dst), iface->name);
+ goto done;
+ }
+ }
+ }
+
+ /* OSPF header sanity checks */
+ if (len < sizeof(*ospf_hdr)) {
+ log_warnx("recv_packet: bad packet size");
+ goto done;
+ }
+ ospf_hdr = (struct ospf_hdr *)buf;
+
+ if ((l = ospf_hdr_sanity_check(&ip_hdr, ospf_hdr, len, iface)) == -1)
+ goto done;
+
+ buf += sizeof(*ospf_hdr);
+ len = l - sizeof(*ospf_hdr);
+
+ if (ospf_hdr->type != PACKET_TYPE_HELLO)
+ /* find neighbor */
+ if ((nbr = nbr_find_id(iface, ospf_hdr->rtr_id)) == NULL) {
+ log_debug("recv_packet: unknown neighbor ID");
+ goto done;
+ }
+
+ /* switch OSPF packet type */
+ switch (ospf_hdr->type) {
+ case PACKET_TYPE_HELLO:
+ inet_aton(AllDRouters, &addr);
+ if (ip_hdr.ip_dst.s_addr == addr.s_addr) {
+ log_debug("recv_packet: invalid destination IP "
+ "address");
+ break;
+ }
+
+ recv_hello(iface, ip_hdr.ip_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);
+ }
+done:
+ free(ptr);
+ return;
+}
+
+int
+ip_hdr_sanity_check(const struct ip *ip_hdr, u_int16_t len)
+{
+ if (ntohs(ip_hdr->ip_len) != len) {
+ log_debug("recv_packet: invalid IP packet length %s",
+ ntohs(ip_hdr->ip_len));
+ return (-1);
+ }
+
+ if (ip_hdr->ip_p != IPPROTO_OSPF)
+ /* this is enforced by the socket itself */
+ fatalx("recv_packet: invalid IP proto");
+
+ return (ip_hdr->ip_hl << 2);
+}
+
+int
+ospf_hdr_sanity_check(const struct ip *ip_hdr, struct ospf_hdr *ospf_hdr,
+ u_int16_t len, const struct iface *iface)
+{
+ struct in_addr addr;
+
+ if (ospf_hdr->version != OSPF_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 (ospf_hdr->area_id != iface->area->id.s_addr) {
+ /* TODO backbone area is allowed for virtual links */
+ addr.s_addr = ospf_hdr->area_id;
+ log_debug("recv_packet: invalid area ID %s, interface %s",
+ inet_ntoa(addr), iface->name);
+ return (-1);
+ }
+
+ if (iface->type == IF_TYPE_BROADCAST || iface->type == IF_TYPE_NBMA) {
+ if (inet_aton(AllDRouters, &addr) == 0)
+ fatalx("recv_packet: inet_aton");
+ if ((ip_hdr->ip_dst.s_addr == addr.s_addr) &&
+ (iface->state != (IF_STA_DR | IF_STA_BACKUP))) {
+ log_debug("recv_packet: invalid destination IP in "
+ "state %s, interface %s",
+ if_state_name(iface->state), iface->name);
+ return (-1);
+ }
+ }
+
+ if (auth_validate(ospf_hdr, iface)) {
+ log_warnx("recv_packet: authentication error, interface %s",
+ iface->name);
+ return (-1);
+ }
+
+ return (ntohs(ospf_hdr->len));
+}
+
+struct iface *
+find_iface(struct ospfd_conf *xconf, struct in_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) {
+ if (iface->fd > 0 && (iface->addr.s_addr &
+ iface->mask.s_addr) == (src.s_addr &
+ iface->mask.s_addr) && !iface->passive)
+ return (iface);
+ }
+ }
+
+ return (NULL);
+}
diff --git a/usr.sbin/ospfd/parse.y b/usr.sbin/ospfd/parse.y
new file mode 100644
index 00000000000..5cfad9b860c
--- /dev/null
+++ b/usr.sbin/ospfd/parse.y
@@ -0,0 +1,878 @@
+/* $OpenBSD: parse.y,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "ospf.h"
+#include "ospfd.h"
+#include "ospfe.h"
+#include "log.h"
+
+static struct ospfd_conf *conf;
+static FILE *fin = NULL;
+static int lineno = 1;
+static int errors = 0;
+static int pdebug = 1;
+char *infile;
+char *start_state;
+
+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);
+
+static struct {
+ u_int32_t dead_interval;
+ u_int16_t transfer_delay;
+ u_int16_t hello_interval;
+ u_int16_t rxmt_interval;
+ u_int16_t metric;
+ u_int8_t priority;
+} defaults;
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entries;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+
+int symset(const char *, const char *, int);
+char *symget(const char *);
+int atoul(char *, u_long *);
+struct area *conf_get_area(struct in_addr);
+struct iface *conf_get_if(char *, unsigned int);
+
+typedef struct {
+ union {
+ u_int32_t number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token AREA INTERFACE ROUTERID FIBUPDATE
+%token AUTHKEY AUTHTYPE
+%token METRIC PASSIVE
+%token HELLOINTERVAL TRANSMITDELAY
+%token RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY
+%token ERROR
+%token <v.string> STRING
+%type <v.number> number yesno
+%type <v.string> string
+
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar conf_main '\n'
+ | grammar varset '\n'
+ | grammar area '\n'
+ | grammar error '\n' { errors++; }
+ ;
+
+number : STRING {
+ u_long ulval;
+
+ if (atoul($1, &ulval) == -1) {
+ yyerror("%s is not a number", $1);
+ free($1);
+ YYERROR;
+ } else
+ $$ = ulval;
+ free($1);
+ }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+ free($1);
+ free($2);
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+yesno : STRING {
+ if (!strcmp($1, "yes"))
+ $$ = 1;
+ else if (!strcmp($1, "no"))
+ $$ = 0;
+ else {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+varset : STRING '=' string {
+ if (conf->opts & 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 : METRIC number {
+ if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
+ yyerror("metric out of range (%d-%d)",
+ MIN_METRIC, MAX_METRIC);
+ YYERROR;
+ }
+ defaults.metric = $2;
+ }
+ | ROUTERPRIORITY number {
+ if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
+ yyerror("router-priority out of range (%d-%d)",
+ MIN_PRIORITY, MAX_PRIORITY);
+ YYERROR;
+ }
+ defaults.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;
+ }
+ defaults.dead_interval = $2;
+ }
+ | TRANSMITDELAY number {
+ if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) {
+ yyerror("transfer-delay out of range (%d-%d)",
+ MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY);
+ YYERROR;
+ }
+ defaults.transfer_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;
+ }
+ defaults.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;
+ }
+ defaults.rxmt_interval = $2;
+ }
+ | ROUTERID string {
+ if (!inet_aton($2, &conf->rtr_id)) {
+ yyerror("error parsing router-id");
+ free($2);
+ YYERROR;
+ }
+ }
+ | FIBUPDATE yesno {
+ if ($2 == 0)
+ conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE;
+ else
+ conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE;
+ }
+ ;
+
+authtype : AUTHTYPE STRING {
+ enum auth_type type;
+
+ if (!strcmp($2, "none"))
+ type = AUTH_NONE;
+ else if (!strcmp($2, "simple"))
+ type = AUTH_SIMPLE;
+ else if (!strcmp($2, "crypt"))
+ type = AUTH_SIMPLE;
+ else {
+ yyerror("unknown auth-type");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ iface->auth_type = type;
+ }
+ ;
+
+authkey : AUTHKEY STRING {
+ if (iface != NULL) {
+ iface->auth_key = $2;
+ /* XXX truncate and warn! */
+ }
+ }
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl /* one newline or more */
+ ;
+
+area : AREA string {
+ struct in_addr id;
+ if (inet_aton($2, &id) == 0) {
+ yyerror("error parsing area-id");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ area = conf_get_area(id);
+ } optnl '{' optnl {
+
+ } areaopts_l '}' {
+ area = NULL;
+ }
+ ;
+
+areaopts_l : areaopts_l areaoptsl
+ | areaoptsl
+ ;
+
+areaoptsl : interface nl
+ | METRIC number nl {
+ if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
+ yyerror("metric out of range (%d-%d)",
+ MIN_METRIC, MAX_METRIC);
+ YYERROR;
+ }
+ area->metric = $2;
+ }
+ | ROUTERPRIORITY number nl {
+ if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
+ yyerror("router-priority out of range (%d-%d)",
+ MIN_PRIORITY, MAX_PRIORITY);
+ YYERROR;
+ }
+ area->priority = $2;
+ }
+ | ROUTERDEADTIME number nl {
+ 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;
+ }
+ area->dead_interval = $2;
+ }
+ | TRANSMITDELAY number nl {
+ if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) {
+ yyerror("transfer-delay out of range (%d-%d)",
+ MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY);
+ YYERROR;
+ }
+ area->transfer_delay = $2;
+ }
+ | HELLOINTERVAL number nl {
+ if ($2 < MIN_HELLO_INTERVAL ||
+ $2 > MAX_HELLO_INTERVAL) {
+ yyerror("hello-interval out of range (%d-%d)",
+ MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
+ YYERROR;
+ }
+ area->hello_interval = $2;
+ }
+ | RETRANSMITINTERVAL number nl {
+ if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
+ yyerror("retransmit-interval out of range "
+ "(%d-%d)", MIN_RXMT_INTERVAL,
+ MAX_RXMT_INTERVAL);
+ YYERROR;
+ }
+ area->rxmt_interval = $2;
+ }
+ ;
+
+interface : INTERFACE STRING {
+ unsigned int idx;
+
+ if ((idx = if_nametoindex($2)) == 0 ) {
+ yyerror("unknown interface %s", $2);
+ free($2);
+ YYERROR;
+ }
+ iface = conf_get_if($2, idx);
+ free($2);
+ if (iface == NULL)
+ YYERROR;
+ iface->area = area;
+ LIST_INSERT_HEAD(&area->iface_list,
+ iface, entry);
+ iface->rtr_id = conf->rtr_id;
+ iface->passive = false;
+ } optnl '{' optnl {
+
+ } interfaceopts_l '}' {
+ iface = NULL;
+ }
+ ;
+
+interfaceopts_l : interfaceopts_l interfaceoptsl
+ | interfaceoptsl
+ ;
+
+interfaceoptsl : authkey nl
+ | authtype nl
+ | PASSIVE nl { iface->passive = true; }
+ | METRIC number nl {
+ if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
+ yyerror("metric out of range (%d-%d)",
+ MIN_METRIC, MAX_METRIC);
+ YYERROR;
+ }
+ iface->metric = $2;
+ }
+ | ROUTERPRIORITY number nl {
+ if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
+ yyerror("router-priority out of range (%d-%d)",
+ MIN_PRIORITY, MAX_PRIORITY);
+ YYERROR;
+ }
+ iface->priority = $2;
+ }
+ | ROUTERDEADTIME number nl {
+ 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;
+ }
+ iface->dead_interval = $2;
+ }
+ | TRANSMITDELAY number nl {
+ if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) {
+ yyerror("transfer-delay out of range (%d-%d)",
+ MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY);
+ YYERROR;
+ }
+ iface->transfer_delay = $2;
+ }
+ | HELLOINTERVAL number nl {
+ if ($2 < MIN_HELLO_INTERVAL ||
+ $2 > MAX_HELLO_INTERVAL) {
+ yyerror("hello-interval out of range (%d-%d)",
+ MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
+ YYERROR;
+ }
+ iface->hello_interval = $2;
+ }
+ | RETRANSMITINTERVAL number nl {
+ if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
+ yyerror("retransmit-interval out of range "
+ "(%d-%d)", MIN_RXMT_INTERVAL,
+ MAX_RXMT_INTERVAL);
+ YYERROR;
+ }
+ iface->rxmt_interval = $2;
+ }
+ ;
+
+%%
+
+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},
+ {"auth-key", AUTHKEY},
+ {"auth-type", AUTHTYPE},
+ {"fibupdate", FIBUPDATE},
+ {"hello-interval", HELLOINTERVAL},
+ {"interface", INTERFACE},
+ {"metric", METRIC},
+ {"passive", PASSIVE},
+ {"retransmit-interval", RETRANSMITINTERVAL},
+ {"router-dead-time", ROUTERDEADTIME},
+ {"router-id", ROUTERID},
+ {"router-priority", ROUTERPRIORITY},
+ {"transmit-delay", TRANSMITDELAY},
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p) {
+ if (pdebug > 1)
+ fprintf(stderr, "%s: %d\n", s, p->k_val);
+ return (p->k_val);
+ } else {
+ if (pdebug > 1)
+ fprintf(stderr, "string: %s\n", s);
+ return (STRING);
+ }
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(FILE *f)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ while ((c = getc(f)) == '\\') {
+ next = getc(f);
+ if (next != '\n') {
+ if (isspace(next))
+ yyerror("whitespace after \\");
+ ungetc(next, f);
+ break;
+ }
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == '\t' || c == ' ') {
+ /* Compress blanks to a single space. */
+ do {
+ c = getc(f);
+ } while (c == '\t' || c == ' ');
+ ungetc(c, f);
+ c = ' ';
+ }
+
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(fin);
+ if (c == '\n') {
+ lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int endc, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(fin)) == ' ')
+ ; /* nothing */
+
+ yylval.lineno = lineno;
+ if (c == '#')
+ while ((c = lgetc(fin)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ endc = c;
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+ if (c == endc) {
+ *p = '\0';
+ break;
+ }
+ if (c == '\n') {
+ lineno++;
+ continue;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ errx(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+struct ospfd_conf *
+parse_config(char *filename, int opts)
+{
+ struct sym *sym, *next;
+
+ if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL) {
+ errx(1, "parse_config calloc");
+ return (NULL);
+ }
+
+ defaults.dead_interval = DEFAULT_RTR_DEAD_TIME;
+ defaults.transfer_delay = DEFAULT_TRANSIT_DELAY;
+ defaults.hello_interval = DEFAULT_HELLO_INTERVAL;
+ defaults.rxmt_interval = DEFAULT_RXMT_INTERVAL;
+ defaults.metric = DEFAULT_METRIC;
+ defaults.priority = DEFAULT_PRIORITY;
+
+ conf->options = OSPF_OPTION_E;
+
+ if ((fin = fopen(filename, "r")) == NULL) {
+ warn("%s", filename);
+ free(conf);
+ return (NULL);
+ }
+ infile = filename;
+
+ conf->opts = opts;
+ LIST_INIT(&conf->area_list);
+
+ 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);
+ }
+ }
+
+ if (errors) {
+ clear_config(conf);
+ return (NULL);
+ }
+
+ return (conf);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entries))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entries);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ errx(1, "cmdline_symset: malloc");
+
+ strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entries)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+int
+atoul(char *s, u_long *ulvalp)
+{
+ u_long ulval;
+ char *ep;
+
+ errno = 0;
+ ulval = strtoul(s, &ep, 0);
+ if (s[0] == '\0' || *ep != '\0')
+ return (-1);
+ if (errno == ERANGE && ulval == ULONG_MAX)
+ return (-1);
+ *ulvalp = ulval;
+ return (0);
+}
+
+struct 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->dead_interval = defaults.dead_interval;
+ a->transfer_delay = defaults.transfer_delay;
+ a->hello_interval = defaults.hello_interval;
+ a->rxmt_interval = defaults.rxmt_interval;
+ a->metric = defaults.metric;
+ a->priority = defaults.priority;
+
+ a->id.s_addr = id.s_addr;
+
+ return (a);
+}
+
+struct iface *
+conf_get_if(char *name, unsigned int idx)
+{
+ struct area *a;
+ struct iface *i;
+
+ LIST_FOREACH(a, &conf->area_list, entry)
+ LIST_FOREACH(i, &a->iface_list, entry)
+ if (i->ifindex == idx) {
+ yyerror("interface %s already configured",
+ name);
+ return (NULL);
+ }
+
+ i = if_new(name, idx);
+ i->dead_interval = area->dead_interval;
+ i->transfer_delay = area->transfer_delay;
+ i->hello_interval = area->hello_interval;
+ i->rxmt_interval = area->rxmt_interval;
+ i->metric = area->metric;
+ i->priority = area->priority;
+
+ return (i);
+}
+
+void
+clear_config(struct ospfd_conf *xconf)
+{
+ /* XXX clear conf */
+ /* ... */
+}
diff --git a/usr.sbin/ospfd/rde.c b/usr.sbin/ospfd/rde.c
new file mode 100644
index 00000000000..c968bc89cdc
--- /dev/null
+++ b/usr.sbin/ospfd/rde.c
@@ -0,0 +1,555 @@
+/* $OpenBSD: rde.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com>
+ * 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 "ospf.h"
+#include "ospfd.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_nbr_init(u_int32_t);
+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 *);
+
+volatile sig_atomic_t rde_quit = 0;
+struct ospfd_conf *rdeconf = NULL;
+struct imsgbuf *ibuf_ospfe;
+struct imsgbuf *ibuf_main;
+
+void
+rde_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * signal handler rules don't apply, libevent decouples for us
+ */
+
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ rde_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+/* route decision engine */
+pid_t
+rde(struct ospfd_conf *xconf, int pipe_parent2rde[2], int pipe_ospfe2rde[2],
+ int pipe_parent2ospfe[2])
+{
+ 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);
+ }
+
+ rdeconf = xconf;
+
+ if ((pw = getpwnam(OSPFD_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) ||
+ setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
+ seteuid(pw->pw_uid) || setuid(pw->pw_uid)) {
+ fatal("can't drop privileges");
+ }
+
+ endpwent();
+
+ event_init();
+ rde_nbr_init(NBR_HASHSIZE);
+ lsa_init(&rdeconf->lsa_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);
+
+ /* 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_imsg);
+
+ /* 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);
+
+ event_dispatch();
+
+ rde_shutdown();
+ /* NOTREACHED */
+
+ return (0);
+}
+
+void
+rde_shutdown(void)
+{
+
+ /* ... */
+
+ msgbuf_write(&ibuf_ospfe->w);
+ msgbuf_clear(&ibuf_ospfe->w);
+ free(ibuf_ospfe);
+ msgbuf_write(&ibuf_main->w);
+ msgbuf_clear(&ibuf_main->w);
+ free(ibuf_main);
+
+ log_info("route decision engine exiting");
+ _exit(0);
+}
+
+/* imesg */
+int
+rde_imsg_compose_parent(int type, pid_t pid, void *data, u_int16_t datalen)
+{
+ return (imsg_compose(ibuf_main, type, 0, pid, -1, data, datalen));
+}
+
+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, -1, data, datalen));
+}
+
+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 lsa *lsa;
+ struct area *area;
+ struct vertex *v;
+ char *buf;
+ time_t now;
+ int r, n, state, self;
+ u_int16_t l;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ fatalx("pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ now = time(NULL);
+
+ 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)
+ fatalx("rde_dispatch_imsg: "
+ "neighbor does not exist");
+ nbr->state = state;
+ break;
+ case IMSG_DB_SNAPSHOT:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ fatalx("rde_dispatch_imsg: "
+ "neighbor does not exist");
+ log_debug("rde_dispatch_imsg: IMSG_DB_SNAPSHOT, "
+ "neighbor %s", inet_ntoa(nbr->id));
+
+ lsa_snap(nbr->area, imsg.hdr.peerid);
+
+ imsg_compose(ibuf_ospfe, IMSG_DB_END, imsg.hdr.peerid,
+ 0, -1, NULL, 0);
+ break;
+ case IMSG_DD:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ fatalx("rde_dispatch_imsg: "
+ "neighbor does not exist");
+ log_debug("rde_dispatch_imsg: IMSG_DD, "
+ "neighbor %s", inet_ntoa(nbr->id));
+
+ 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 LSA's that are
+ * newer or missing
+ */
+ /* XXX add to local REQ list */
+ imsg_compose(ibuf_ospfe, IMSG_DD,
+ imsg.hdr.peerid, 0, -1,
+ &lsa_hdr, sizeof(lsa_hdr));
+ }
+ if (l != 0)
+ log_warnx("rde_dispatch_imsg: peerid %lu, "
+ "trailing garbage in Database Description "
+ "packet", imsg.hdr.peerid);
+ break;
+ case IMSG_LS_REQ:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ fatalx("rde_dispatch_imsg: "
+ "neighbor does not exist");
+ log_debug("rde_dispatch_imsg: IMSG_LS_REQ, "
+ "neighbor %s len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ 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, -1, NULL, 0);
+ continue;
+ }
+ imsg_compose(ibuf_ospfe, IMSG_LS_UPD,
+ imsg.hdr.peerid, 0, -1,
+ 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)
+ fatalx("rde_dispatch_imsg: "
+ "neighbor does not exist");
+ log_debug("rde_dispatch_imsg: IMSG_LS_UPD, "
+ "neighbor %s len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ 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))
+ 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;
+
+ 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)
+ break;
+ if (!(self = lsa_self(nbr, lsa, v)))
+ lsa_add(nbr, lsa);
+
+ /* flood and perhaps ack LSA */
+ imsg_compose(ibuf_ospfe, IMSG_LS_FLOOD,
+ imsg.hdr.peerid, 0, -1,
+ lsa, ntohs(lsa->hdr.len));
+
+ /* reflood self originated LSA */
+ if (self && v)
+ imsg_compose(ibuf_ospfe, IMSG_LS_FLOOD,
+ v->nbr->peerid, 0, -1,
+ v->lsa, ntohs(v->lsa->hdr.len));
+ /* TODO LSA on req list -> BadLSReq */
+ } else if (r < 0) {
+ /* 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, -1,
+ v->lsa, ntohs(v->lsa->hdr.len));
+ } else {
+ /* LSA equal send direct ack */
+ imsg_compose(ibuf_ospfe, IMSG_LS_ACK,
+ imsg.hdr.peerid, 0, -1, &lsa->hdr,
+ sizeof(lsa->hdr));
+ }
+ break;
+ case IMSG_LS_MAXAGE:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL)
+ fatalx("rde_dispatch_imsg: "
+ "neighbor does not exist");
+ log_debug("rde_dispatch_imsg: IMSG_LS_MAXAGE, "
+ "neighbor %s len %d", inet_ntoa(nbr->id),
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ 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;
+
+ lsa_del(nbr, &lsa_hdr);
+ break;
+ case IMSG_CTL_SHOW_DATABASE:
+ 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, -1, area,
+ sizeof(*area));
+ lsa_dump(&area->lsa_tree, imsg.hdr.pid);
+ }
+ lsa_dump(&rdeconf->lsa_tree, 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, -1, area,
+ sizeof(*area));
+ lsa_dump(&area->lsa_tree, imsg.hdr.pid);
+ if (!area->stub)
+ lsa_dump(&rdeconf->lsa_tree,
+ imsg.hdr.pid);
+ }
+ }
+ imsg_compose(ibuf_ospfe, IMSG_CTL_END, 0, imsg.hdr.pid,
+ -1, NULL, 0);
+ break;
+ default:
+ log_debug("rde_dispatch_msg: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+u_int32_t
+rde_router_id(void)
+{
+ return (rdeconf->rtr_id.s_addr);
+}
+
+
+
+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)
+{
+ 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;
+}
+
+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;
+
+ 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;
+
+ LIST_REMOVE(nbr, entry);
+ LIST_REMOVE(nbr, hash);
+
+ free(nbr);
+}
+
+int
+rde_nbr_loading(struct area *area)
+{
+ struct rde_nbr *nbr;
+
+ LIST_FOREACH(nbr, &area->nbr_list, entry) {
+ if (nbr->self)
+ continue;
+ if (nbr->state & NBR_STA_XCHNG ||
+ nbr->state & NBR_STA_LOAD)
+ return (1);
+ }
+ return (0);
+}
+
diff --git a/usr.sbin/ospfd/rde.h b/usr.sbin/ospfd/rde.h
new file mode 100644
index 00000000000..25be7f683c4
--- /dev/null
+++ b/usr.sbin/ospfd/rde.h
@@ -0,0 +1,81 @@
+/* $OpenBSD: rde.h,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * 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 vertex {
+ RB_ENTRY(vertex) entry;
+ struct event ev;
+ struct in_addr nexthop;
+ struct vertex *prev;
+ struct rde_nbr *nbr;
+ struct lsa *lsa;
+ time_t changed;
+ time_t stamp;
+ u_int32_t cost;
+ u_int32_t ls_id;
+ u_int32_t adv_rtr;
+ u_int8_t type;
+ u_int8_t flooded;
+};
+
+/* just the info RDE needs */
+struct rde_nbr {
+ LIST_ENTRY(rde_nbr) entry, hash;
+ struct in_addr id;
+ struct in_addr area_id;
+ struct lsa_head ls_req_list;
+ struct area *area;
+ u_int32_t peerid; /* unique ID in DB */
+ int state;
+ int self;
+};
+
+/* rde.c */
+pid_t rde(struct ospfd_conf *, int [2], int [2], int [2]);
+int rde_imsg_compose_parent(int, pid_t, void *, u_int16_t);
+int rde_imsg_compose_ospfe(int, u_int32_t, pid_t, void *,
+ u_int16_t);
+u_int32_t rde_router_id(void);
+void rde_nbr_del(struct rde_nbr *);
+int rde_nbr_loading(struct area *);
+
+/* rde_spf.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 *);
+void lsa_add(struct rde_nbr *, struct lsa *);
+void lsa_del(struct rde_nbr *, struct lsa_hdr *);
+struct vertex *lsa_find(struct area *, u_int8_t, u_int32_t, u_int32_t);
+void lsa_snap(struct area *, u_int32_t);
+void lsa_dump(struct lsa_tree *, pid_t);
+
+RB_PROTOTYPE(lsa_tree, vertex, entry, lsa_compare)
+
+#endif /* _RDE_H_ */
diff --git a/usr.sbin/ospfd/rde_lsdb.c b/usr.sbin/ospfd/rde_lsdb.c
new file mode 100644
index 00000000000..d9ab39b85df
--- /dev/null
+++ b/usr.sbin/ospfd/rde_lsdb.c
@@ -0,0 +1,490 @@
+/* $OpenBSD: rde_lsdb.c,v 1.1 2005/01/28 14:05:40 claudio 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 "ospf.h"
+#include "ospfd.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);
+int lsa_asext_check(struct area *, struct lsa *, u_int16_t);
+void lsa_age(struct vertex *);
+void lsa_timeout(int, short, void *);
+void lsa_refresh(struct vertex *);
+
+struct lsa_tree *global_lsa_tree;
+
+RB_GENERATE(lsa_tree, vertex, entry, lsa_compare)
+
+void
+lsa_init(struct lsa_tree *t)
+{
+ global_lsa_tree = t;
+ RB_INIT(global_lsa_tree);
+}
+
+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;
+
+ if ((v = calloc(1, sizeof(struct vertex))) == NULL)
+ fatal(NULL);
+ v->nbr = nbr;
+ v->lsa = lsa;
+ v->changed = v->stamp = time(NULL);
+ 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 */
+
+ evtimer_set(&v->ev, lsa_timeout, v);
+
+ return (v);
+}
+
+void
+vertex_free(struct vertex *v)
+{
+ if (v == NULL)
+ return;
+
+ 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);
+
+ 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 (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;
+
+ if (len < sizeof(lsa->hdr)) {
+ log_warnx("lsa_check: invalid packet size");
+ return (0);
+ }
+ if (ntohs(lsa->hdr.len) != len) {
+ log_warnx("lsa_check: invalid packet size");
+ return (0);
+ }
+
+ if (iso_cksum(lsa, len, 0)) {
+ log_warnx("lsa_check: invalid packet checksum");
+ return (0);
+ }
+
+ /* invalid ages */
+ if ((ntohs(lsa->hdr.age) < 1 && !nbr->self) ||
+ ntohs(lsa->hdr.age) > MAX_AGE) {
+ log_debug("lsa_check: invalid age");
+ return (0);
+ }
+
+ /* invalid sequence number */
+ if ((ntohl(lsa->hdr.seq_num) == 0x80000000)) {
+ log_debug("ls_check: invalid 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 & 0x03) ||
+ len < sizeof(lsa->hdr) + sizeof(u_int32_t)) {
+ log_warnx("lsa_check: invalid LSA network packet");
+ return (0);
+ }
+ break;
+ case LSA_TYPE_SUM_NETWORK:
+ case LSA_TYPE_SUM_ROUTER:
+ if (len != sizeof(struct lsa_hdr) + sizeof(struct lsa_sum)) {
+ log_warnx("lsa_check: invalid LSA summary packet");
+ return (0);
+ }
+ break;
+ case LSA_TYPE_EXTERNAL:
+ if (!lsa_asext_check(area, lsa, len))
+ return (0);
+ break;
+ default:
+ log_warnx("lsa_check: unknown type %u", lsa->hdr.type);
+ return (0);
+ }
+
+ /* MaxAge handling */
+ if (ntohs(lsa->hdr.age) == MAX_AGE && 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_asext_check(struct area *area, struct lsa *lsa, u_int16_t len)
+{
+ if (area->stub)
+ /* AS-external-LSA are discarded in stub areas */
+ return (0);
+
+ /* XXX check size */
+ return (1);
+}
+
+int
+lsa_self(struct rde_nbr *nbr, struct lsa *new, struct vertex *v)
+{
+ struct iface *iface;
+ struct lsa *dummy;
+
+ 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:
+ 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.
+ */
+ lsa_add(nbr, 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);
+}
+
+void
+lsa_add(struct rde_nbr *nbr, struct lsa *lsa)
+{
+ struct lsa_tree *tree;
+ struct vertex *new, *old;
+ struct timeval tv;
+
+ if (lsa->hdr.type == LSA_TYPE_EXTERNAL)
+ tree = global_lsa_tree;
+ else
+ tree = &nbr->area->lsa_tree;
+
+ new = vertex_get(lsa, nbr);
+ old = RB_INSERT(lsa_tree, tree, new);
+ if (old != NULL) {
+ RB_REMOVE(lsa_tree, tree, old);
+ vertex_free(old);
+ RB_INSERT(lsa_tree, tree, new);
+ }
+
+ /* timeout handling either MAX_AGE or LS_REFRESH_TIME */
+ timerclear(&tv);
+
+ if (nbr->self)
+ 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");
+}
+
+void
+lsa_del(struct rde_nbr *nbr, struct lsa_hdr *lsa)
+{
+ struct lsa_tree *tree;
+ struct vertex *v;
+
+ v = lsa_find(nbr->area, lsa->type, lsa->ls_id, lsa->adv_rtr);
+ if (v == NULL) {
+ log_warnx("lsa_del: LSA no longer in table");
+ return;
+ }
+
+ if (lsa->type == LSA_TYPE_EXTERNAL)
+ tree = global_lsa_tree;
+ else
+ tree = &nbr->area->lsa_tree;
+
+ RB_REMOVE(lsa_tree, tree, v);
+ vertex_free(v);
+}
+
+void
+lsa_age(struct vertex *v)
+{
+ time_t now;
+ int d;
+ u_int16_t age;
+
+ now = time(NULL);
+ d = now - v->stamp;
+ 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);
+ v->stamp = now;
+}
+
+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 = global_lsa_tree;
+ else
+ tree = &area->lsa_tree;
+
+ v = RB_FIND(lsa_tree, tree, &key);
+ if (v)
+ lsa_age(v);
+
+ return (v);
+}
+
+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) {
+ 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 = global_lsa_tree;
+ } while (1);
+}
+
+void
+lsa_dump(struct lsa_tree *tree, pid_t pid)
+{
+ struct vertex *v;
+
+ RB_FOREACH(v, lsa_tree, tree) {
+ lsa_age(v);
+ rde_imsg_compose_ospfe(IMSG_CTL_SHOW_DATABASE, 0, pid,
+ &v->lsa->hdr, ntohs(v->lsa->hdr.len));
+ }
+}
+
+void
+lsa_timeout(int fd, short event, void *bula)
+{
+ struct vertex *v = bula;
+
+ lsa_age(v);
+
+ log_debug("lsa_timeout: REFLOOD");
+
+ if (v->nbr->self)
+ lsa_refresh(v);
+
+ rde_imsg_compose_ospfe(IMSG_LS_FLOOD, v->nbr->peerid, 0,
+ v->lsa, ntohs(v->lsa->hdr.len));
+}
+
+void
+lsa_refresh(struct vertex *v)
+{
+ struct timeval tv;
+ u_int32_t seqnum;
+ u_int16_t len;
+
+ /* refresh LSA by increasing sequnce number by one */
+ v->lsa->hdr.age = ntohs(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));
+
+ v->changed = time(NULL);
+ timerclear(&tv);
+ tv.tv_sec = LS_REFRESH_TIME;
+ evtimer_add(&v->ev, &tv);
+}