summaryrefslogtreecommitdiff
path: root/usr.sbin/sasyncd
diff options
context:
space:
mode:
authorHakan Olsson <ho@cvs.openbsd.org>2005-03-30 18:44:50 +0000
committerHakan Olsson <ho@cvs.openbsd.org>2005-03-30 18:44:50 +0000
commit1e42720c7d8b17ab1489a1fac2f86ba12229ccf1 (patch)
treea6dc00bfa0b4344cecdbd733263eee02a88b53ec /usr.sbin/sasyncd
parentc2c1ef90c70e5f9accfa8a65b07648fae651be03 (diff)
Move sasyncd(8), for IPsec SA synchronization, in-tree. Work in progress.
deraadt@ ok.
Diffstat (limited to 'usr.sbin/sasyncd')
-rw-r--r--usr.sbin/sasyncd/Makefile19
-rw-r--r--usr.sbin/sasyncd/carp.c176
-rw-r--r--usr.sbin/sasyncd/conf.c275
-rw-r--r--usr.sbin/sasyncd/log.c98
-rw-r--r--usr.sbin/sasyncd/net.c529
-rw-r--r--usr.sbin/sasyncd/net.h69
-rw-r--r--usr.sbin/sasyncd/net_ctl.c197
-rw-r--r--usr.sbin/sasyncd/net_ssl.c239
-rw-r--r--usr.sbin/sasyncd/pfkey.c359
-rw-r--r--usr.sbin/sasyncd/sasyncd.8124
-rw-r--r--usr.sbin/sasyncd/sasyncd.c208
-rw-r--r--usr.sbin/sasyncd/sasyncd.conf.5104
-rw-r--r--usr.sbin/sasyncd/sasyncd.h142
-rw-r--r--usr.sbin/sasyncd/timer.c143
14 files changed, 2682 insertions, 0 deletions
diff --git a/usr.sbin/sasyncd/Makefile b/usr.sbin/sasyncd/Makefile
new file mode 100644
index 00000000000..617d4c14740
--- /dev/null
+++ b/usr.sbin/sasyncd/Makefile
@@ -0,0 +1,19 @@
+# $Id: Makefile,v 1.1 2005/03/30 18:44:49 ho Exp $
+
+PROG= sasyncd
+SRCS= sasyncd.c carp.c conf.c log.c net.c net_ctl.c net_ssl.c \
+ pfkey.c timer.c
+MAN= sasyncd.8 sasyncd.conf.5
+
+# ElectricFence
+#LDADD+= -L/usr/local/lib -lefence
+
+# Boehms GC
+#LDADD+= -L/usr/local/lib -lgc
+#CFLAGS+= -DGC_DEBUG
+
+CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes \
+ -Wmissing-declarations
+LDADD+= -lcrypto -lssl
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sasyncd/carp.c b/usr.sbin/sasyncd/carp.c
new file mode 100644
index 00000000000..06391e52499
--- /dev/null
+++ b/usr.sbin/sasyncd/carp.c
@@ -0,0 +1,176 @@
+/* $OpenBSD: carp.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/ip_carp.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sasyncd.h"
+
+/* For some reason, ip_carp.h does not define this. */
+#define CARP_INIT 0
+#define CARP_BACKUP 1
+#define CARP_MASTER 2
+
+/* Returns 1 for the CARP MASTER, 0 for BACKUP/INIT, -1 on error. */
+static enum RUNSTATE
+carp_get_state(char *ifname)
+{
+ struct ifreq ifr;
+ struct carpreq carp;
+ int s, saved_errno;
+ char *state;
+
+ if (!ifname || !*ifname) {
+ errno = ENOENT;
+ return FAIL;
+ }
+
+ memset(&ifr, 0, sizeof ifr);
+ strlcpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ return FAIL;
+
+ ifr.ifr_data = (caddr_t)&carp;
+ if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) {
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ return FAIL;
+ }
+ close(s);
+
+ switch (carp.carpr_state) {
+ case CARP_INIT:
+ state = "INIT";
+ break;
+
+ case CARP_BACKUP:
+ state = "BACKUP";
+ break;
+
+ case CARP_MASTER:
+ state = "MASTER";
+ break;
+
+ default:
+ state = "<unknown>";
+ break;
+ }
+
+ log_msg(4, "carp_get_state: %s vhid %u state %s(%d)", ifname,
+ carp.carpr_vhid, state, carp.carpr_state);
+
+ if (carp.carpr_vhid > 0)
+ return carp.carpr_state == CARP_MASTER ? MASTER : SLAVE;
+ else
+ return FAIL;
+}
+
+void
+carp_check_state(void)
+{
+ enum RUNSTATE current_state = carp_get_state(cfgstate.carp_ifname);
+ static char *carpstate[] = CARPSTATES;
+
+ if (current_state < 0 || current_state > FAIL) {
+ log_err("carp_state_tracker: invalid result on interface "
+ "%s, abort", cfgstate.carp_ifname);
+ cfgstate.runstate = FAIL;
+ return;
+ }
+
+ if (current_state != cfgstate.runstate) {
+ log_msg(1, "carp_state_tracker: switching state to %s",
+ carpstate[current_state]);
+ cfgstate.runstate = current_state;
+ if (current_state == MASTER)
+ pfkey_set_promisc();
+ net_ctl_update_state();
+ }
+}
+
+static void
+carp_state_tracker(void *v_arg)
+{
+ static int failures = 0;
+ u_int32_t next_check;
+
+ carp_check_state();
+
+ if (cfgstate.runstate == FAIL)
+ if (++failures < 3)
+ log_err("carp_state_tracker");
+
+ if (failures > 5)
+ next_check = 600;
+ else
+ next_check = cfgstate.carp_check_interval + failures * 10;
+
+ if (timer_add("carp_state_tracker", next_check, carp_state_tracker,
+ NULL))
+ log_msg(0, "carp_state_tracker: failed to renew event");
+ return;
+}
+
+/* Initialize the CARP state tracker. */
+int
+carp_init(void)
+{
+ enum RUNSTATE initial_state;
+
+ if (cfgstate.lockedstate != INIT) {
+ cfgstate.runstate = cfgstate.lockedstate;
+ log_msg(1, "carp_init: locking runstate to %s",
+ cfgstate.runstate == MASTER ? "MASTER" : "SLAVE");
+ return 0;
+ }
+
+ initial_state = carp_get_state(cfgstate.carp_ifname);
+ if (initial_state == FAIL) {
+ fprintf(stderr, "Failed to check interface \"%s\".\n",
+ cfgstate.carp_ifname);
+ fprintf(stderr, "Correct or manually select runstate.\n");
+ return -1;
+ }
+
+ return timer_add("carp_state_tracker", 0, carp_state_tracker, NULL);
+}
diff --git a/usr.sbin/sasyncd/conf.c b/usr.sbin/sasyncd/conf.c
new file mode 100644
index 00000000000..80a7a8c8723
--- /dev/null
+++ b/usr.sbin/sasyncd/conf.c
@@ -0,0 +1,275 @@
+/* $OpenBSD: conf.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sasyncd.h"
+#include "net.h"
+
+/* Global configuration context. */
+struct cfgstate cfgstate;
+
+static int
+conf_parse_file(char *cfgfile)
+{
+ struct syncpeer *peer;
+ FILE *fp = fopen(cfgfile, "r");
+ int lineno = 0, dup;
+ char linebuf[1024], name[1024], *p;
+
+ if (!fp) {
+ log_err("failed to open \"%s\"", cfgfile);
+ return 1;
+ }
+
+ while(fgets(linebuf, sizeof linebuf, fp)) {
+ lineno++;
+
+ /* Strip comments and remove \n. */
+ for (p = linebuf; *p; p++)
+ if (*p == '\n' || *p == '#') {
+ *p = 0;
+ break;
+ }
+
+ if (*linebuf == 0)
+ continue;
+
+ if (sscanf(linebuf, "listen on %1023s", name) == 1) {
+ if (cfgstate.listen_on)
+ free(cfgstate.listen_on);
+ cfgstate.listen_on = strdup(name);
+ if (!cfgstate.listen_on) {
+ log_err("config: strdup() failed");
+ return 1;
+ }
+ log_msg(2, "config(line %02d): listen on %s", lineno,
+ cfgstate.listen_on);
+ continue;
+ }
+
+ if (sscanf(linebuf, "listen port %1023s", name) == 1) {
+ cfgstate.listen_port = atoi(name);
+ if (cfgstate.listen_port < 1 ||
+ cfgstate.listen_port > 65534) {
+ cfgstate.listen_port = SASYNCD_DEFAULT_PORT;
+ log_msg(0, "config: bad value line %d, "
+ "listen-port reset to %u", lineno,
+ SASYNCD_DEFAULT_PORT);
+ } else
+ log_msg(2, "config(line %02d): listen port %u",
+ cfgstate.listen_port);
+ continue;
+ }
+
+ if (sscanf(linebuf, "peer %1023s", name) == 1) {
+ dup = 0;
+ for (peer = LIST_FIRST(&cfgstate.peerlist); peer;
+ peer = LIST_NEXT(peer, link))
+ if (strcmp(name, peer->name) == 0) {
+ dup++;
+ break;
+ }
+ if (dup)
+ continue;
+ peer = (struct syncpeer *)calloc(1, sizeof *peer);
+ if (!peer) {
+ log_err("config: calloc(1, %lu) failed",
+ sizeof *peer);
+ return 1;
+ }
+ peer->name = strdup(name);
+ if (!peer->name) {
+ log_err("config: strdup() failed");
+ return 1;
+ }
+ LIST_INSERT_HEAD(&cfgstate.peerlist, peer, link);
+ log_msg(2, "config(line %02d): add peer %s", lineno,
+ peer->name);
+ continue;
+ }
+
+ if (sscanf(linebuf, "carp interface %1023s", name) == 1) {
+ if (cfgstate.carp_ifname)
+ free(cfgstate.carp_ifname);
+ cfgstate.carp_ifname = strdup(name);
+ if (!cfgstate.carp_ifname) {
+ log_err("config: strdup failed");
+ return 1;
+ }
+ log_msg(2, "config(line %02d): carp interface %s",
+ lineno, cfgstate.carp_ifname);
+ continue;
+ }
+
+ if (sscanf(linebuf, "carp interval %1023s", name) == 1) {
+ cfgstate.carp_check_interval = atoi(name);
+ if (cfgstate.carp_check_interval < 1) {
+ cfgstate.carp_check_interval =
+ CARP_DEFAULT_INTERVAL;
+ log_msg(0, "config: bad value line %d, "
+ "carp-interval reset to %d", lineno,
+ CARP_DEFAULT_INTERVAL);
+ } else
+ log_msg(2, "config(line %02d): carp interval "
+ "%d", lineno,
+ cfgstate.carp_check_interval);
+ continue;
+ }
+
+ if (sscanf(linebuf, "CAcertificate file %1023s", name) == 1) {
+ if (cfgstate.cafile)
+ free(cfgstate.cafile);
+ cfgstate.cafile = strdup(name);
+ if (!cfgstate.cafile) {
+ log_err("config: strdup failed");
+ return 1;
+ }
+ log_msg(2, "config(line %02d): CAcertificate file $s",
+ lineno, cfgstate.cafile);
+ continue;
+ }
+
+ if (sscanf(linebuf, "certificate file %1023s", name) == 1) {
+ if (cfgstate.certfile)
+ free(cfgstate.certfile);
+ cfgstate.certfile = strdup(name);
+ if (!cfgstate.certfile) {
+ log_err("config: strdup failed");
+ return 1;
+ }
+ log_msg(2, "config(line %02d): certificate file $s",
+ lineno, cfgstate.certfile);
+ continue;
+ }
+
+ if (sscanf(linebuf, "private key file %1023s", name) == 1) {
+ if (cfgstate.privkeyfile)
+ free(cfgstate.privkeyfile);
+ cfgstate.privkeyfile = strdup(name);
+ if (!cfgstate.privkeyfile) {
+ log_err("config: strdup failed");
+ return 1;
+ }
+ log_msg(2, "config(line %02d): private key file $s",
+ lineno, cfgstate.privkeyfile);
+ continue;
+ }
+
+ if (sscanf(linebuf, "run as %1023s", name) == 1) {
+ if (strcasecmp(name, "SLAVE") == 0)
+ cfgstate.lockedstate = SLAVE;
+ else if (strcasecmp(name, "MASTER") == 0)
+ cfgstate.lockedstate = MASTER;
+ else {
+ log_msg(0, "config(line %02d): unknown state "
+ "%s", lineno, name);
+ return 1;
+ }
+ log_msg(2, "config(line %02d): runstate locked to %s",
+ lineno, cfgstate.lockedstate == MASTER ? "MASTER" :
+ "SLAVE");
+ continue;
+ }
+ }
+
+ /* Sanity checks. */
+ if (LIST_EMPTY(&cfgstate.peerlist)) {
+ log_msg(0, "config: no peers defined");
+ return 1;
+ }
+
+ if (!cfgstate.carp_ifname && cfgstate.lockedstate == INIT) {
+ log_msg(0, "config: no carp interface or runstate defined");
+ return 1;
+ }
+
+ /* Success. */
+ return 0;
+}
+
+int
+conf_init(int argc, char **argv)
+{
+ char *cfgfile = 0;
+ int ch;
+
+ memset(&cfgstate, 0, sizeof cfgstate);
+ cfgstate.runstate = INIT;
+ LIST_INIT(&cfgstate.peerlist);
+
+ cfgstate.carp_check_interval = CARP_DEFAULT_INTERVAL;
+ cfgstate.listen_port = SASYNCD_DEFAULT_PORT;
+
+ while ((ch = getopt(argc, argv, "c:dv")) != -1) {
+ switch (ch) {
+ case 'c':
+ if (cfgfile)
+ return 2;
+ cfgfile = optarg;
+ break;
+ case 'd':
+ cfgstate.debug++;
+ break;
+ case 'v':
+ cfgstate.verboselevel++;
+ break;
+ default:
+ return 2;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ return 2;
+
+ if (!cfgfile)
+ cfgfile = SASYNCD_CFGFILE;
+
+ if (conf_parse_file(cfgfile) == 0) {
+ if (!cfgstate.certfile)
+ cfgstate.certfile = SASYNCD_CERTFILE;
+ if (!cfgstate.privkeyfile)
+ cfgstate.privkeyfile = SASYNCD_PRIVKEY;
+ if (!cfgstate.cafile)
+ cfgstate.cafile = SASYNCD_CAFILE;
+ return 0;
+ } else
+ return 1;
+}
diff --git a/usr.sbin/sasyncd/log.c b/usr.sbin/sasyncd/log.c
new file mode 100644
index 00000000000..239f764b008
--- /dev/null
+++ b/usr.sbin/sasyncd/log.c
@@ -0,0 +1,98 @@
+/* $OpenBSD: log.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <errno.h>
+#include <time.h>
+
+#include "sasyncd.h"
+
+static char logbuf[2048];
+
+void
+log_init(char *pname)
+{
+ tzset();
+ openlog(pname, LOG_CONS, LOG_DAEMON);
+}
+
+static void
+log_output(char *msg)
+{
+ if (cfgstate.debug)
+ fprintf(stderr, "%s\n", msg);
+ else
+ syslog(LOG_CRIT, msg);
+}
+
+void
+log_err(const char *fmt, ...)
+{
+ va_list ap;
+ int off = 0;
+
+ if (cfgstate.debug) {
+ snprintf(logbuf, sizeof logbuf, "%s: ", __progname);
+ off = strlen(logbuf);
+ }
+
+ va_start(ap, fmt);
+ (void)vsnprintf(logbuf + off, sizeof logbuf - off, fmt, ap);
+ va_end(ap);
+
+ strlcat(logbuf, ": ", sizeof logbuf);
+ strlcat(logbuf, strerror (errno), sizeof logbuf);
+
+ log_output(logbuf);
+ return;
+}
+
+void
+log_msg (int minlevel, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (cfgstate.verboselevel < minlevel)
+ return;
+
+ va_start(ap, fmt);
+ (void)vsnprintf(logbuf, sizeof logbuf, fmt, ap);
+ va_end(ap);
+
+ log_output(logbuf);
+ return;
+}
diff --git a/usr.sbin/sasyncd/net.c b/usr.sbin/sasyncd/net.c
new file mode 100644
index 00000000000..8cb3dddc0e7
--- /dev/null
+++ b/usr.sbin/sasyncd/net.c
@@ -0,0 +1,529 @@
+/* $OpenBSD: net.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sasyncd.h"
+#include "net.h"
+
+struct msg {
+ u_int8_t *buf;
+ u_int8_t *obuf; /* Original buf w/o offset. */
+ u_int32_t len;
+ u_int32_t type;
+ int refcnt;
+};
+
+struct qmsg {
+ SIMPLEQ_ENTRY(qmsg) next;
+ struct msg *msg;
+};
+
+int listen_socket;
+
+/* Local prototypes. */
+static u_int8_t *net_read(struct syncpeer *, u_int32_t *, u_int32_t *);
+static int net_set_sa(struct sockaddr *, char *, in_port_t);
+static void net_check_peers(void *);
+
+int
+net_init(void)
+{
+ struct sockaddr_storage sa_storage;
+ struct sockaddr *sa = (struct sockaddr *)&sa_storage;
+ struct syncpeer *p;
+ int r;
+
+ if (net_SSL_init())
+ return -1;
+
+ /* Setup listening socket. */
+ memset(&sa_storage, 0, sizeof sa_storage);
+ if (net_set_sa(sa, cfgstate.listen_on, cfgstate.listen_port)) {
+ perror("inet_pton");
+ return -1;
+ }
+ listen_socket = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (listen_socket < 0) {
+ perror("socket()");
+ close(listen_socket);
+ return -1;
+ }
+ r = 1;
+ if (setsockopt(listen_socket, SOL_SOCKET,
+ cfgstate.listen_on ? SO_REUSEADDR : SO_REUSEPORT, (void *)&r,
+ sizeof r)) {
+ perror("setsockopt()");
+ close(listen_socket);
+ return -1;
+ }
+ if (bind(listen_socket, sa, sizeof(struct sockaddr_in))) {
+ perror("bind()");
+ close(listen_socket);
+ return -1;
+ }
+ if (listen(listen_socket, 10)) {
+ perror("listen()");
+ close(listen_socket);
+ return -1;
+ }
+ log_msg(2, "listening on port %u fd %d", cfgstate.listen_port,
+ listen_socket);
+
+ for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
+ p->socket = -1;
+ SIMPLEQ_INIT(&p->msgs);
+ }
+
+ net_check_peers(0);
+ return 0;
+}
+
+static void
+net_enqueue(struct syncpeer *p, struct msg *m)
+{
+ struct qmsg *qm;
+
+ if (p->socket < 0)
+ return;
+
+ if (!p->ssl)
+ if (net_SSL_connect(p))
+ return;
+
+ qm = (struct qmsg *)malloc(sizeof *qm);
+ if (!qm) {
+ log_err("malloc()");
+ return;
+ }
+
+ memset(qm, 0, sizeof *qm);
+ qm->msg = m;
+ m->refcnt++;
+
+ SIMPLEQ_INSERT_TAIL(&p->msgs, qm, next);
+ return;
+}
+
+/*
+ * Queue a message for transmission to a particular peer,
+ * or to all peers if no peer is specified.
+ */
+int
+net_queue(struct syncpeer *p0, u_int32_t msgtype, u_int8_t *buf,
+ u_int32_t offset, u_int32_t len)
+{
+ struct syncpeer *p = p0;
+ struct msg *m;
+
+ m = (struct msg *)malloc(sizeof *m);
+ if (!m) {
+ log_err("malloc()");
+ free(buf);
+ return -1;
+ }
+ memset(m, 0, sizeof *m);
+ m->obuf = buf;
+ m->buf = buf + offset;
+ m->len = len;
+ m->type = msgtype;
+
+ if (p)
+ net_enqueue(p, m);
+ else
+ for (p = LIST_FIRST(&cfgstate.peerlist); p;
+ p = LIST_NEXT(p, link))
+ net_enqueue(p, m);
+
+ if (!m->refcnt) {
+ free(m->obuf);
+ free(m);
+ }
+
+ return 0;
+}
+
+/* Set all write pending filedescriptors. */
+int
+net_set_pending_wfds(fd_set *fds)
+{
+ struct syncpeer *p;
+ int max_fd = -1;
+
+ for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link))
+ if (p->socket > -1 && SIMPLEQ_FIRST(&p->msgs)) {
+ FD_SET(p->socket, fds);
+ if (p->socket > max_fd)
+ max_fd = p->socket;
+ }
+ return max_fd + 1;
+}
+
+/*
+ * Set readable filedescriptors. They are basically the same as for write,
+ * plus the listening socket.
+ */
+int
+net_set_rfds(fd_set *fds)
+{
+ struct syncpeer *p;
+ int max_fd = -1;
+
+ for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
+ if (p->socket > -1)
+ FD_SET(p->socket, fds);
+ if (p->socket > max_fd)
+ max_fd = p->socket;
+ }
+ FD_SET(listen_socket, fds);
+ if (listen_socket > max_fd)
+ max_fd = listen_socket;
+ return max_fd + 1;
+}
+
+void
+net_handle_messages(fd_set *fds)
+{
+ struct sockaddr_storage sa_storage, sa_storage2;
+ struct sockaddr *sa = (struct sockaddr *)&sa_storage;
+ struct sockaddr *sa2 = (struct sockaddr *)&sa_storage2;
+ socklen_t socklen;
+ struct syncpeer *p;
+ u_int8_t *msg;
+ u_int32_t msgtype, msglen;
+ int newsock, found;
+
+ if (FD_ISSET(listen_socket, fds)) {
+ /* Accept a new incoming connection */
+ socklen = sizeof sa_storage;
+ newsock = accept(listen_socket, sa, &socklen);
+ if (newsock > -1) {
+ /* Setup the syncpeer structure */
+ found = 0;
+ for (p = LIST_FIRST(&cfgstate.peerlist); p && !found;
+ p = LIST_NEXT(p, link)) {
+ struct sockaddr_in *sin, *sin2;
+ struct sockaddr_in6 *sin6, *sin62;
+
+ /* Match? */
+ if (net_set_sa(sa2, p->name, 0))
+ continue;
+ if (sa->sa_family != sa2->sa_family)
+ continue;
+ if (sa->sa_family == AF_INET) {
+ sin = (struct sockaddr_in *)sa;
+ sin2 = (struct sockaddr_in *)sa2;
+ if (memcmp(&sin->sin_addr,
+ &sin2->sin_addr,
+ sizeof(struct in_addr)))
+ continue;
+ } else {
+ sin6 = (struct sockaddr_in6 *)sa;
+ sin62 = (struct sockaddr_in6 *)sa2;
+ if (memcmp(&sin6->sin6_addr,
+ &sin62->sin6_addr,
+ sizeof(struct in6_addr)))
+ continue;
+ }
+ /* Match! */
+ found++;
+ p->socket = newsock;
+ p->ssl = NULL;
+ log_msg(1, "peer \"%s\" connected", p->name);
+ }
+ if (!found) {
+ log_msg(1, "Found no matching peer for "
+ "accepted socket, closing.");
+ close(newsock);
+ }
+ } else
+ log_err("accept()");
+ }
+
+ for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
+ if (p->socket < 0 || !FD_ISSET(p->socket, fds))
+ continue;
+ msg = net_read(p, &msgtype, &msglen);
+ if (!msg)
+ continue;
+
+ /* XXX check message validity. */
+
+ log_msg(4, "net_handle_messages: got msg type %u len %u from "
+ "peer %s", msgtype, msglen, p->name);
+
+ switch (msgtype) {
+ case MSG_SYNCCTL:
+ net_ctl_handle_msg(p, msg, msglen);
+ free(msg);
+ break;
+
+ case MSG_PFKEYDATA:
+ if (p->runstate != MASTER ||
+ cfgstate.runstate == MASTER) {
+ log_msg(0, "got PFKEY message from non-MASTER "
+ "peer");
+ free(msg);
+ if (cfgstate.runstate == MASTER)
+ net_ctl_send_state(p);
+ else
+ net_ctl_send_error(p, 0);
+ } else if (pfkey_queue_message(msg, msglen))
+ free(msg);
+ break;
+
+ default:
+ log_msg(0, "Got unknown message type %u len %u from "
+ "peer %s", msgtype, msglen, p->name);
+ free(msg);
+ net_ctl_send_error(p, 0);
+ }
+ }
+}
+
+void
+net_send_messages(fd_set *fds)
+{
+ struct syncpeer *p;
+ struct qmsg *qm;
+ struct msg *m;
+ u_int32_t v;
+
+ for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
+ if (p->socket < 0 || !FD_ISSET(p->socket, fds))
+ continue;
+
+ qm = SIMPLEQ_FIRST(&p->msgs);
+ if (!qm) {
+ /* XXX Log */
+ continue;
+ }
+ m = qm->msg;
+
+ log_msg(4, "sending msg %p (qm %p ref %d) to peer %s", m, qm,
+ m->refcnt, p->name);
+
+ /* Send the message. */
+ v = htonl(m->type);
+ if (net_SSL_write(p, &v, sizeof v))
+ continue;
+
+ v = htonl(m->len);
+ if (net_SSL_write(p, &v, sizeof v))
+ continue;
+
+ (void)net_SSL_write(p, m->buf, m->len);
+
+ /* Cleanup. */
+ SIMPLEQ_REMOVE_HEAD(&p->msgs, next);
+ free(qm);
+
+ if (--m->refcnt < 1) {
+ log_msg(4, "freeing msg %p", m);
+ free(m->obuf);
+ free(m);
+ }
+ }
+ return;
+}
+
+void
+net_disconnect_peer(struct syncpeer *p)
+{
+ net_SSL_disconnect(p);
+ if (p->socket > -1)
+ close(p->socket);
+ p->socket = -1;
+}
+
+void
+net_shutdown(void)
+{
+ struct syncpeer *p;
+ struct qmsg *qm;
+ struct msg *m;
+
+ while ((p = LIST_FIRST(&cfgstate.peerlist))) {
+ while ((qm = SIMPLEQ_FIRST(&p->msgs))) {
+ SIMPLEQ_REMOVE_HEAD(&p->msgs, next);
+ m = qm->msg;
+ if (--m->refcnt < 1) {
+ free(m->obuf);
+ free(m);
+ }
+ free(qm);
+ }
+ net_disconnect_peer(p);
+ if (p->name)
+ free(p->name);
+ LIST_REMOVE(p, link);
+ free(p);
+ }
+
+ if (listen_socket > -1)
+ close(listen_socket);
+ net_SSL_shutdown();
+}
+
+/*
+ * Helper functions (local) below here.
+ */
+
+static u_int8_t *
+net_read(struct syncpeer *p, u_int32_t *msgtype, u_int32_t *msglen)
+{
+ u_int8_t *msg;
+ u_int32_t v;
+
+ if (net_SSL_read(p, &v, sizeof v))
+ return NULL;
+ *msgtype = ntohl(v);
+
+ if (*msgtype > MSG_MAXTYPE)
+ return NULL;
+
+ if (net_SSL_read(p, &v, sizeof v))
+ return NULL;
+ *msglen = ntohl(v);
+
+ /* XXX msglen sanity */
+
+ msg = (u_int8_t *)malloc(*msglen);
+ memset(msg, 0, *msglen);
+ if (net_SSL_read(p, msg, *msglen)) {
+ free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+static int
+net_set_sa(struct sockaddr *sa, char *name, in_port_t port)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ if (name) {
+ if (inet_pton(AF_INET, name, &sin->sin_addr) == 1) {
+ sa->sa_family = AF_INET;
+ sin->sin_port = htons(port);
+ sin->sin_len = sizeof *sin;
+ return 0;
+ }
+
+ if (inet_pton(AF_INET6, name, &sin6->sin6_addr) == 1) {
+ sa->sa_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ sin6->sin6_len = sizeof *sin6;
+ return 0;
+ }
+ } else {
+ /* XXX Assume IPv4 */
+ sa->sa_family = AF_INET;
+ sin->sin_port = htons(port);
+ sin->sin_len = sizeof *sin;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+got_sigalrm(int s)
+{
+ return;
+}
+
+void
+net_connect_peers(void)
+{
+ struct sockaddr_storage sa_storage;
+ struct itimerval iv;
+ struct sockaddr *sa = (struct sockaddr *)&sa_storage;
+ struct syncpeer *p;
+
+ signal(SIGALRM, got_sigalrm);
+ memset(&iv, 0, sizeof iv);
+ iv.it_value.tv_sec = 5;
+ iv.it_interval.tv_sec = 5;
+ setitimer(ITIMER_REAL, &iv, NULL);
+
+ for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
+ if (p->ssl || p->socket > -1)
+ continue;
+
+ memset(sa, 0, sizeof sa_storage);
+ if (net_set_sa(sa, p->name, cfgstate.listen_port))
+ continue;
+ p->socket = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (p->socket < 0) {
+ log_err("peer \"%s\": socket()", p->name);
+ continue;
+ }
+ if (connect(p->socket, sa, sa->sa_len)) {
+ log_msg(1, "peer \"%s\" not ready yet", p->name);
+ net_disconnect_peer(p);
+ continue;
+ }
+ if (net_ctl_send_state(p)) {
+ log_msg(0, "peer \"%s\" failed", p->name);
+ net_disconnect_peer(p);
+ continue;
+ }
+ log_msg(1, "peer \"%s\" connected", p->name);
+ }
+
+ timerclear(&iv.it_value);
+ timerclear(&iv.it_interval);
+ setitimer(ITIMER_REAL, &iv, NULL);
+ signal(SIGALRM, SIG_IGN);
+
+ return;
+}
+
+static void
+net_check_peers(void *arg)
+{
+ net_connect_peers();
+
+ (void)timer_add("peer recheck", 600, net_check_peers, 0);
+}
+
diff --git a/usr.sbin/sasyncd/net.h b/usr.sbin/sasyncd/net.h
new file mode 100644
index 00000000000..b3a5569a187
--- /dev/null
+++ b/usr.sbin/sasyncd/net.h
@@ -0,0 +1,69 @@
+/* $OpenBSD: net.h,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <openssl/ssl.h>
+
+struct qmsg;
+struct syncpeer {
+ LIST_ENTRY(syncpeer) link;
+
+ char *name; /* FQDN or an IP, from conf */
+ SSL *ssl;
+ int socket;
+ enum RUNSTATE runstate;
+
+ SIMPLEQ_HEAD(, qmsg) msgs;
+};
+
+/* Control message types. */
+enum CTLTYPE { RESERVED = 0, CTL_STATE, CTL_ERROR, CTL_ACK, CTL_UNKNOWN };
+#define CTLTYPES { \
+ "RESERVED", "CTL_STATE", "CTL_ERROR", "CTL_ACK", "CTL_UNKNOWN" \
+};
+
+/* net.c */
+void net_connect_peers(void);
+void net_disconnect_peer(struct syncpeer *);
+
+/* net_ctl.c */
+void net_ctl_handle_msg(struct syncpeer *, u_int8_t *, u_int32_t);
+int net_ctl_send_ack(struct syncpeer *, enum CTLTYPE, u_int32_t);
+int net_ctl_send_error(struct syncpeer *, enum CTLTYPE);
+int net_ctl_send_state(struct syncpeer *);
+
+/* net_ssl.c */
+int net_SSL_init(void);
+int net_SSL_connect(struct syncpeer *);
+void net_SSL_disconnect(struct syncpeer *);
+int net_SSL_read(struct syncpeer *, void *, u_int32_t);
+int net_SSL_write(struct syncpeer *, void *, u_int32_t);
+void net_SSL_shutdown(void);
diff --git a/usr.sbin/sasyncd/net_ctl.c b/usr.sbin/sasyncd/net_ctl.c
new file mode 100644
index 00000000000..97cabfa97f4
--- /dev/null
+++ b/usr.sbin/sasyncd/net_ctl.c
@@ -0,0 +1,197 @@
+/* $OpenBSD: net_ctl.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sasyncd.h"
+#include "net.h"
+
+struct ctlmsg {
+ u_int32_t type;
+ u_int32_t data;
+ u_int32_t data2;
+};
+
+int listen_socket;
+
+static int
+net_ctl_check_state(struct syncpeer *p, enum RUNSTATE nstate)
+{
+ static char *runstate[] = CARPSTATES;
+
+ if (nstate < INIT || nstate > FAIL) {
+ log_msg(0, "got bad state %d from peer \"%s\"", nstate,
+ p->name);
+ net_ctl_send_error(p, CTL_STATE);
+ return -1;
+ }
+ if (cfgstate.runstate == MASTER &&
+ nstate == MASTER) {
+ log_msg(0, "got bad state MASTER from peer \"%s\"",
+ p->name);
+ net_ctl_send_error(p, CTL_STATE);
+ return -1;
+ }
+ if (p->runstate != nstate) {
+ p->runstate = nstate;
+ log_msg(1, "peer \"%s\" state change to %s", p->name,
+ runstate[nstate]);
+ }
+ return 0;
+}
+
+void
+net_ctl_handle_msg(struct syncpeer *p, u_int8_t *msg, u_int32_t msglen)
+{
+ struct ctlmsg *ctl = (struct ctlmsg *)msg;
+ enum RUNSTATE nstate;
+ enum CTLTYPE ctype;
+ static char *ct, *ctltype[] = CTLTYPES;
+
+ if (msglen < sizeof *ctl) {
+ log_msg(0, "got invalid control message from peer \"%s\"",
+ p->name);
+ net_ctl_send_error(p, CTL_UNKNOWN);
+ return;
+ }
+
+ switch (ntohl(ctl->type)) {
+ case CTL_STATE:
+ log_msg(3, "got CTL_STATE from peer \"%s\"", p->name);
+ nstate = (enum RUNSTATE)ntohl(ctl->data);
+ if (net_ctl_check_state(p, nstate) == 0)
+ net_ctl_send_ack(p, CTL_STATE, cfgstate.runstate);
+ break;
+
+ case CTL_ERROR:
+ log_msg(1, "got ERROR from peer \"%s\"", p->name);
+
+ switch (ntohl(ctl->data)) {
+ case RESERVED: /* PFKEY -- nothing to do here for now */
+ break;
+
+ case CTL_STATE:
+ nstate = cfgstate.runstate;
+ carp_check_state();
+ if (nstate != cfgstate.runstate)
+ net_ctl_send_state(p);
+ break;
+
+ case CTL_UNKNOWN:
+ default:
+ break;
+ }
+ break;
+
+ case CTL_ACK:
+ ctype = (enum CTLTYPE)ntohl(ctl->data);
+ if (ctype < RESERVED || ctype > CTL_UNKNOWN)
+ ct = "<unknown>";
+ else
+ ct = ctltype[ctype];
+ log_msg(3, "got %s ACK from peer \"%s\"", ct, p->name);
+ if (ctype == CTL_STATE) {
+ nstate = (enum RUNSTATE)ntohl(ctl->data2);
+ net_ctl_check_state(p, nstate);
+ }
+ break;
+
+ case CTL_UNKNOWN:
+ default:
+ log_msg(1, "got unknown msg type %u from peer \"%s\"",
+ ntohl(ctl->type), p->name);
+ break;
+ }
+}
+
+static int
+net_ctl_send(struct syncpeer *p, u_int32_t type, u_int32_t d, u_int32_t d2)
+{
+ struct ctlmsg *m = (struct ctlmsg *)malloc(sizeof *m);
+
+ if (!m) {
+ log_err("malloc(%u)", sizeof *m);
+ return -1;
+ }
+
+ memset(m, 0, sizeof *m);
+ m->type = htonl(type);
+ m->data = htonl(d);
+ m->data2 = htonl(d2);
+
+ return net_queue(p, MSG_SYNCCTL, (u_int8_t *)m, 0, sizeof *m);
+}
+
+int
+net_ctl_send_ack(struct syncpeer *p, enum CTLTYPE prevtype, u_int32_t code)
+{
+ return net_ctl_send(p, CTL_ACK, (u_int32_t)prevtype, code);
+}
+
+int
+net_ctl_send_state(struct syncpeer *p)
+{
+ return net_ctl_send(p, CTL_STATE, (u_int32_t)cfgstate.runstate, 0);
+}
+
+int
+net_ctl_send_error(struct syncpeer *p, enum CTLTYPE prevtype)
+{
+ return net_ctl_send(p, CTL_ERROR, (u_int32_t)prevtype, 0);
+}
+
+/* After a CARP tracker state change, send an state ctl msg to all peers. */
+void
+net_ctl_update_state(void)
+{
+ struct syncpeer *p;
+ static char *carpstate[] = CARPSTATES;
+
+ /* We may have new peers available. */
+ net_connect_peers();
+
+ for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
+ if (p->socket == -1)
+ continue;
+ log_msg(2, "sending my state %s to peer \"%s\"",
+ carpstate[cfgstate.runstate], p->name);
+ net_ctl_send_state(p);
+ }
+}
diff --git a/usr.sbin/sasyncd/net_ssl.c b/usr.sbin/sasyncd/net_ssl.c
new file mode 100644
index 00000000000..7cef23bf112
--- /dev/null
+++ b/usr.sbin/sasyncd/net_ssl.c
@@ -0,0 +1,239 @@
+/* $OpenBSD: net_ssl.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/types.h>
+
+#include <openssl/ssl.h>
+#include <openssl/tls1.h>
+#include <openssl/err.h>
+
+#include <string.h>
+
+#include "sasyncd.h"
+#include "net.h"
+
+/* Global SSL context. */
+SSL_CTX *ctx;
+
+static void net_SSL_dump_stack(int);
+static void net_SSL_print_error(int, int);
+
+int
+net_SSL_init(void)
+{
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ if (!ctx)
+ return -1;
+
+ (void)SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2);
+
+ /* Load CA cert. */
+ if (!SSL_CTX_load_verify_locations(ctx, cfgstate.cafile, NULL)) {
+ net_SSL_dump_stack(0);
+ fprintf(stderr, "cannot read \"%s\": %s\n", cfgstate.cafile,
+ strerror(errno));
+ return -1;
+ }
+
+ /* Load our certificate. */
+ if (!SSL_CTX_use_certificate_chain_file(ctx, cfgstate.certfile)) {
+ net_SSL_dump_stack(0);
+ fprintf(stderr, "cannot read \"%s\": %s\n", cfgstate.certfile,
+ strerror(errno));
+ return -1;
+ }
+
+ /* Load and check private key. */
+ if (!SSL_CTX_use_PrivateKey_file(ctx, cfgstate.privkeyfile,
+ SSL_FILETYPE_PEM)) {
+ net_SSL_dump_stack(0);
+ if (ERR_GET_REASON(ERR_peek_error() == EVP_R_BAD_DECRYPT)) {
+ fprintf(stderr, "bad pass phrase\n");
+ return -1;
+ } else {
+ fprintf(stderr, "cannot read \"%s\": %s\n",
+ cfgstate.privkeyfile, strerror(errno));
+ return -1;
+ }
+ }
+ if (!SSL_CTX_check_private_key(ctx)) {
+ net_SSL_dump_stack(0);
+ fprintf(stderr, "Private key does not match certificate\n");
+ return -1;
+ }
+ return 0;
+}
+
+int
+net_SSL_connect(struct syncpeer *p)
+{
+ int r, err;
+
+ p->ssl = SSL_new(ctx);
+ if (!p->ssl)
+ return -1;
+ SSL_set_fd(p->ssl, p->socket);
+ r = SSL_connect(p->ssl);
+ if (r != 1) {
+ err = SSL_get_error(p->ssl, r);
+ net_SSL_print_error(err, r);
+ return -1;
+ }
+ log_msg(2, "TLS connection established with peer "
+ "\"%s\"", p->name);
+ return 0;
+}
+
+void
+net_SSL_disconnect(struct syncpeer *p)
+{
+ if (p->ssl) {
+ SSL_shutdown(p->ssl);
+ SSL_free(p->ssl);
+ }
+ p->ssl = NULL;
+}
+
+static void
+net_SSL_dump_stack(int level)
+{
+ int err;
+
+ while ((err = ERR_get_error()) != 0)
+ log_msg(level, "%s", ERR_error_string(err, NULL));
+}
+
+static void
+net_SSL_print_error(int r, int prev)
+{
+ char *msg;
+
+ switch (r) {
+ case SSL_ERROR_NONE:
+ msg = "SSL_ERROR_NONE";
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ msg = "SSL_ERROR_ZERO_RETURN";
+ break;
+ case SSL_ERROR_WANT_READ:
+ msg = "SSL_ERROR_WANT_READ";
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ msg = "SSL_ERROR_WANT_WRITE";
+ break;
+ case SSL_ERROR_WANT_CONNECT:
+ msg = "SSL_ERROR_WANT_CONNECT";
+ break;
+ case SSL_ERROR_WANT_ACCEPT:
+ msg = "SSL_ERROR_WANT_ACCEPT";
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ msg = "SSL_ERROR_WANT_X509_LOOKUP";
+ break;
+ case SSL_ERROR_SYSCALL:
+ msg = "SSL_ERROR_SYSCALL";
+ break;
+ case SSL_ERROR_SSL:
+ msg = "SSL_ERROR_SSL";
+ break;
+ default:
+ msg = "<unknown error>";
+ break;
+ }
+
+ log_msg(3, "SSL: \"%s\" original code = %d", msg, prev);
+
+ net_SSL_dump_stack(3);
+}
+
+static int
+net_SSL_io(struct syncpeer *p, void *buf, u_int32_t len, int writeflag)
+{
+ int ret, e;
+
+ retry:
+ if (writeflag)
+ ret = SSL_write(p->ssl, buf, len);
+ else
+ ret = SSL_read(p->ssl, buf, len);
+ if (ret == (int)len)
+ return 0;
+
+ e = SSL_get_error(p->ssl, ret);
+ net_SSL_print_error(e, ret);
+
+ if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE)
+ goto retry; /* Enough to just retry here? XXX */
+
+ log_msg(1, "peer \"%s\" disconnected", p->name);
+ net_disconnect_peer(p);
+ return 1;
+}
+
+int
+net_SSL_read(struct syncpeer *p, void *buf, u_int32_t len)
+{
+ int r, err;
+
+ if (!p->ssl) {
+ p->ssl = SSL_new(ctx);
+ if (!p->ssl) {
+ log_msg(0, "SSL_new() failed");
+ return NULL;
+ }
+ SSL_set_fd(p->ssl, p->socket);
+ r = SSL_accept(p->ssl);
+ if (r != 1) {
+ err = SSL_get_error(p->ssl, r);
+ net_SSL_print_error(err, r);
+ return NULL;
+ }
+ }
+
+ return net_SSL_io(p, buf, len, 0);
+}
+
+int
+net_SSL_write(struct syncpeer *p, void *buf, u_int32_t len)
+{
+ return net_SSL_io(p, buf, len, 1);
+}
+
+void
+net_SSL_shutdown(void)
+{
+ ERR_free_strings();
+ SSL_CTX_free(ctx);
+}
diff --git a/usr.sbin/sasyncd/pfkey.c b/usr.sbin/sasyncd/pfkey.c
new file mode 100644
index 00000000000..6ec09d5c327
--- /dev/null
+++ b/usr.sbin/sasyncd/pfkey.c
@@ -0,0 +1,359 @@
+/* $OpenBSD: pfkey.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <net/pfkeyv2.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sasyncd.h"
+
+struct pfkey_msg
+{
+ SIMPLEQ_ENTRY(pfkey_msg) next;
+
+ u_int8_t *buf;
+ u_int32_t len;
+};
+
+SIMPLEQ_HEAD(, pfkey_msg) pfkey_msglist;
+
+static const char *msgtypes[] = {
+ "RESERVED", "GETSPI", "UPDATE", "ADD", "DELETE", "GET", "ACQUIRE",
+ "REGISTER", "EXPIRE", "FLUSH", "DUMP", "X_PROMISC", "X_ADDFLOW",
+ "X_DELFLOW", "X_GRPSPIS", "X_ASKPOLICY"
+};
+
+#define CHUNK sizeof(u_int64_t)
+
+static const char *pfkey_print_type(struct sadb_msg *msg);
+
+static int
+pfkey_write(u_int8_t *buf, ssize_t len)
+{
+ struct sadb_msg *msg = (struct sadb_msg *)buf;
+
+ if (cfgstate.pfkey_socket == -1)
+ return 0;
+
+ if (write(cfgstate.pfkey_socket, buf, len) != len) {
+ log_err("pfkey: msg %s write() failed",
+ pfkey_print_type(msg), cfgstate.pfkey_socket);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+pfkey_set_promisc(void)
+{
+ struct sadb_msg msg;
+ static u_int32_t seq = 1;
+
+ memset(&msg, 0, sizeof msg);
+ msg.sadb_msg_version = PF_KEY_V2;
+ msg.sadb_msg_seq = seq++;
+ msg.sadb_msg_satype = 1; /* Special; 1 to enable, 0 to disable */
+ msg.sadb_msg_type = SADB_X_PROMISC;
+ msg.sadb_msg_pid = getpid();
+ msg.sadb_msg_len = sizeof msg / CHUNK;
+
+ return pfkey_write((u_int8_t *)&msg, sizeof msg);
+}
+
+static const char *
+pfkey_print_type(struct sadb_msg *msg)
+{
+ if (msg->sadb_msg_type < sizeof msgtypes / sizeof msgtypes[0])
+ return msgtypes[msg->sadb_msg_type];
+ else
+ return "<unknown>";
+}
+
+static int
+pfkey_handle_message(struct sadb_msg *m)
+{
+ struct sadb_msg *msg = m;
+
+ /*
+ * Report errors, but ignore for DELETE (both isakmpd and kernel will
+ * expire the SA, if the kernel is first, DELETE returns failure).
+ */
+ if (msg->sadb_msg_errno && msg->sadb_msg_type != SADB_DELETE) {
+ errno = msg->sadb_msg_errno;
+ log_err("pfkey error (%s)", pfkey_print_type(msg));
+ }
+
+ /* We only want promiscuous messages here, skip all others. */
+ if (msg->sadb_msg_type != SADB_X_PROMISC ||
+ (msg->sadb_msg_len * CHUNK) <= 2 * sizeof *msg) {
+ free(m);
+ return 0;
+ }
+ msg++;
+
+ /*
+ * We should not listen to PFKEY messages when we are not running
+ * as MASTER, or the pid is our own.
+ */
+ if (cfgstate.runstate != MASTER ||
+ msg->sadb_msg_pid == (u_int32_t)getpid()) {
+ free(m);
+ return 0;
+ }
+
+ log_msg(3, "pfkey: got %s len %u seq %d", pfkey_print_type(msg),
+ msg->sadb_msg_len * CHUNK, msg->sadb_msg_seq);
+
+ switch (msg->sadb_msg_type) {
+ case SADB_X_PROMISC:
+ case SADB_DUMP:
+ case SADB_GET:
+ case SADB_GETSPI:
+ /* Some messages should not be synced. */
+ free(m);
+ break;
+
+ case SADB_UPDATE:
+ /*
+ * Tweak -- the peers do not have a larval SA to update, so
+ * instead we ADD it here.
+ */
+ msg->sadb_msg_type = SADB_ADD;
+ /* FALLTHROUGH */
+
+ default:
+ /* The rest should just be passed along to our peers. */
+ return net_queue(NULL, MSG_PFKEYDATA, (u_int8_t *)m, sizeof *m,
+ msg->sadb_msg_len * CHUNK);
+ }
+
+ return 0;
+}
+
+static int
+pfkey_read(void)
+{
+ struct sadb_msg hdr, *msg;
+ u_int8_t *data;
+ ssize_t datalen;
+ int fd = cfgstate.pfkey_socket;
+
+ if (recv(fd, &hdr, sizeof hdr, MSG_PEEK) != sizeof hdr) {
+ log_err("pfkey_read: recv() failed");
+ return -1;
+ }
+ datalen = hdr.sadb_msg_len * CHUNK;
+ data = (u_int8_t *)malloc(datalen);
+ if (!data) {
+ log_err("pfkey_read: malloc(%lu) failed", datalen);
+ return -1;
+ }
+ msg = (struct sadb_msg *)data;
+
+ if (read(fd, data, datalen) != datalen) {
+ log_err("pfkey_read: read() failed, %lu bytes", datalen);
+ free(data);
+ return -1;
+ }
+
+ return pfkey_handle_message(msg);
+}
+
+int
+pfkey_init(int reinit)
+{
+ int fd;
+
+ fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
+ if (fd == -1) {
+ perror("failed to open PF_KEY socket");
+ return -1;
+ }
+ cfgstate.pfkey_socket = fd;
+
+ if (reinit) {
+ if (cfgstate.runstate == MASTER)
+ pfkey_set_promisc();
+ return (fd > -1 ? 0 : -1);
+ }
+
+ SIMPLEQ_INIT(&pfkey_msglist);
+ return 0;
+}
+
+void
+pfkey_set_rfd(fd_set *fds)
+{
+ if (cfgstate.pfkey_socket != -1)
+ FD_SET(cfgstate.pfkey_socket, fds);
+}
+
+void
+pfkey_set_pending_wfd(fd_set *fds)
+{
+ if (cfgstate.pfkey_socket != -1 && SIMPLEQ_FIRST(&pfkey_msglist))
+ FD_SET(cfgstate.pfkey_socket, fds);
+}
+
+void
+pfkey_read_message(fd_set *fds)
+{
+ if (cfgstate.pfkey_socket != -1)
+ if (FD_ISSET(cfgstate.pfkey_socket, fds))
+ (void)pfkey_read();
+}
+
+void
+pfkey_send_message(fd_set *fds)
+{
+ struct pfkey_msg *pmsg = SIMPLEQ_FIRST(&pfkey_msglist);
+
+ if (!pmsg || !FD_ISSET(cfgstate.pfkey_socket, fds))
+ return;
+
+ if (cfgstate.pfkey_socket == -1)
+ if (pfkey_init(1)) /* Reinit socket */
+ return;
+
+ (void)pfkey_write(pmsg->buf, pmsg->len);
+
+ SIMPLEQ_REMOVE_HEAD(&pfkey_msglist, next);
+ free(pmsg->buf);
+ free(pmsg);
+ return;
+}
+
+int
+pfkey_queue_message(u_int8_t *data, u_int32_t datalen)
+{
+ struct pfkey_msg *pmsg;
+ struct sadb_msg *sadb = (struct sadb_msg *)data;
+ static u_int32_t seq = 1;
+
+ pmsg = (struct pfkey_msg *)malloc(sizeof *pmsg);
+ if (!pmsg) {
+ log_err("malloc()");
+ return -1;
+ }
+ memset(pmsg, 0, sizeof *pmsg);
+
+ pmsg->buf = data;
+ pmsg->len = datalen;
+
+ sadb->sadb_msg_pid = getpid();
+ sadb->sadb_msg_seq = seq++;
+ log_msg(3, "sync: pfkey %s len %d seq %d", pfkey_print_type(sadb),
+ sadb->sadb_msg_len * CHUNK, sadb->sadb_msg_seq);
+
+ SIMPLEQ_INSERT_TAIL(&pfkey_msglist, pmsg, next);
+ return 0;
+}
+
+void
+pfkey_shutdown(void)
+{
+ struct pfkey_msg *p = SIMPLEQ_FIRST(&pfkey_msglist);
+
+ while ((p = SIMPLEQ_FIRST(&pfkey_msglist))) {
+ SIMPLEQ_REMOVE_HEAD(&pfkey_msglist, next);
+ free(p->buf);
+ free(p);
+ }
+
+ if (cfgstate.pfkey_socket > -1)
+ close(cfgstate.pfkey_socket);
+}
+
+/* ------------------------------------------------------------------------- */
+
+void
+pfkey_snapshot(void *v)
+{
+ struct sadb_msg *m;
+ struct sadb_ext *e;
+ u_int8_t *buf;
+ size_t sz, mlen, elen;
+ int mib[5];
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_KEY;
+ mib[2] = PF_KEY_V2;
+ mib[3] = NET_KEY_SADB_DUMP;
+ mib[4] = 0; /* Unspec SA type */
+
+ if (timer_add("pfkey_snapshot", 60, pfkey_snapshot, NULL))
+ log_err("pfkey_snapshot: failed to renew event");
+
+ if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1
+ || sz == 0)
+ return;
+ if ((buf = malloc(sz)) == NULL) {
+ log_err("malloc");
+ return;
+ }
+ if (sysctl(mib, sizeof mib / sizeof mib[0], buf, &sz, NULL, 0) == -1) {
+ log_err("sysctl");
+ return;
+ }
+
+ m = (struct sadb_msg *)buf;
+ while (m < (struct sadb_msg *)(buf + sz) && m->sadb_msg_len > 0) {
+ mlen = m->sadb_msg_len * CHUNK;
+
+ fprintf(stderr, "snapshot: sadb_msg %p type %s len %u\n",
+ m, pfkey_print_type(m), mlen);
+
+ e = (struct sadb_ext *)(m + 1);
+ while ((u_int8_t *)e - (u_int8_t *)m < mlen &&
+ e->sadb_ext_len > 0) {
+ elen = e->sadb_ext_len * CHUNK;
+ fprintf(stderr, "ext %p len %u\n", e, elen);
+ e = (struct sadb_ext *)((u_int8_t *)e + elen);
+ }
+ /* ... */
+ m = (struct sadb_msg *)((u_int8_t *)m + mlen);
+ }
+ memset(buf, 0, sz);
+ free(buf);
+ return;
+}
diff --git a/usr.sbin/sasyncd/sasyncd.8 b/usr.sbin/sasyncd/sasyncd.8
new file mode 100644
index 00000000000..415eed160d5
--- /dev/null
+++ b/usr.sbin/sasyncd/sasyncd.8
@@ -0,0 +1,124 @@
+.\" $OpenBSD: sasyncd.8,v 1.1 2005/03/30 18:44:49 ho Exp $
+.\"
+.\" Copyright (c) 2005 Håkan Olsson. All rights reserved.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\" This code was written under funding by Multicom Security AB.
+.\"
+.\" Manual page for sasyncd
+.\"
+.Dd August 07, 2004
+.Dt SASYNCD 8
+.Os
+.Sh NAME
+.Nm sasyncd
+.Nd IPSec SA synchronization daemon for failover gateways
+.Sh SYNOPSIS
+.Nm
+.Op Fl dv
+.Op Fl c Ar config-file
+.Sh DESCRIPTION
+The
+.Nm
+daemon synchronizes IPSec SA information between a number of failover
+IPsec gateways.
+The most typical scenario is to run
+.Nm
+on hosts also running
+.Xr isakmpd 8
+and sharing a common IP-address using
+.Xr carp 4 .
+.Pp
+The daemon runs either in master or slave mode, in which the master
+tracks all local IPsec SA changes and sends this information along to
+all slaves so they will have the same data.
+.Ss Failover
+.Nm
+does not itself do any failover processing; the normal mode of
+operation is to track state changes on a specified
+.Xr carp 4
+interface.
+Whenever it changes,
+.Nm
+will follow suit.
+It is possible to
+.Qq lock
+the daemon to a particular state; see
+.Xr sasyncd.conf 5 .
+.Ss Host to host communication
+All
+.Nm
+host to host communication is protected by
+.Xr ssl 8 ,
+so the daemon requires certificates configured for each host.
+Normally
+.Pa /etc/ssl/sasyncd.crt
+and
+.Pa /etc/ssl/private/sasyncd.key
+are used, although alternate locations may be specified in the
+configuration file.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c Ar config-file
+If given, the
+.Fl c
+option specifies an alternate configuration file instead of
+.Pa /etc/sasyncd.conf .
+.It Fl d
+The
+.Fl d
+option causes the daemon to run in the foreground, logging to stderr.
+.It Fl v
+The
+.Fl v
+option increases the verbosity level of the daemon.
+This option can be specified several times.
+.El
+.Sh FILES
+.Bl -tag -width /etc/ssl/private/sasyncd.key -compact
+.It Pa /etc/sasyncd.conf
+The default
+.Nm
+configuration file.
+.It Pa /etc/ssl/sascynd.crt
+The default certificate.
+.It Pa /etc/ssl/private/sasyncd.key
+The default private key.
+.It Pa /etc/ssl/ca.crt
+The default CA certificate.
+.El
+.Sh SEE ALSO
+.Xr openssl 1 ,
+.Xr carp 4 ,
+.Xr ipsec 4 ,
+.Xr sasyncd.conf 5 ,
+.Xr isakmpd 8 ,
+.Xr ssl 8
+.Sh HISTORY
+The
+.Nm
+daemon first appeared in
+.Ox 3.7 .
+It was written in 2004 by Hakan Olsson, in part sponsored by
+Multicom Security AB, Sweden.
diff --git a/usr.sbin/sasyncd/sasyncd.c b/usr.sbin/sasyncd/sasyncd.c
new file mode 100644
index 00000000000..b59fc818025
--- /dev/null
+++ b/usr.sbin/sasyncd/sasyncd.c
@@ -0,0 +1,208 @@
+/* $OpenBSD: sasyncd.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "sasyncd.h"
+
+static int
+privdrop(void)
+{
+ struct passwd *pw = getpwnam(SASYNCD_USER);
+
+ if (!pw) {
+ log_err("%s: getpwnam(\"%s\") failed", __progname,
+ SASYNCD_USER);
+ return -1;
+ }
+
+ if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) {
+ log_err("%s: chroot failed", __progname);
+ return -1;
+ }
+
+ if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) ||
+ setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) {
+ log_err("%s: failed to drop privileges", __progname);
+ return -1;
+ }
+
+ return 0;
+}
+
+volatile int daemon_shutdown = 0;
+
+/* Called by signal handler for controlled daemon shutdown. */
+static void
+sasyncd_stop(int s)
+{
+ daemon_shutdown++;
+}
+
+static int
+sasyncd_run(void)
+{
+ struct timeval *timeout, tv;
+ fd_set *rfds, *wfds;
+ size_t fdsetsize;
+ int maxfd, n;
+
+ n = getdtablesize();
+ fdsetsize = howmany(n, NFDBITS) * sizeof(fd_mask);
+
+ rfds = (fd_set *)malloc(fdsetsize);
+ if (!rfds) {
+ log_err("malloc(%lu) failed", (unsigned long)fdsetsize);
+ return -1;
+ }
+
+ wfds = (fd_set *)malloc(fdsetsize);
+ if (!wfds) {
+ log_err("malloc(%lu) failed", (unsigned long)fdsetsize);
+ return -1;
+ }
+
+ signal(SIGINT, sasyncd_stop);
+ signal(SIGTERM, sasyncd_stop);
+ signal(SIGHUP, sasyncd_stop);
+
+ while (!daemon_shutdown) {
+ memset(rfds, 0, fdsetsize);
+ memset(wfds, 0, fdsetsize);
+ maxfd = net_set_rfds(rfds);
+ n = net_set_pending_wfds(wfds);
+ if (n > maxfd)
+ maxfd = n;
+
+ pfkey_set_rfd(rfds);
+ pfkey_set_pending_wfd(wfds);
+ if (cfgstate.pfkey_socket + 1 > maxfd)
+ maxfd = cfgstate.pfkey_socket + 1;
+
+ timeout = &tv;
+ timer_next_event(&tv);
+
+ n = select(maxfd, rfds, wfds, 0, timeout);
+ if (n == -1) {
+ if (errno != EINTR) {
+ log_err("select()");
+ sleep(1);
+ }
+ } else if (n) {
+ net_handle_messages(rfds);
+ net_send_messages(wfds);
+ pfkey_read_message(rfds);
+ pfkey_send_message(wfds);
+ }
+ timer_run();
+ }
+ free(rfds);
+ free(wfds);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int r;
+
+ /* Init. */
+ closefrom(STDERR_FILENO + 1);
+ for (r = 0; r <= 2; r++)
+ if (fcntl(r, F_GETFL, 0) == -1 && errno == EBADF)
+ (void)open("/dev/null", r ? O_WRONLY : O_RDONLY, 0);
+
+ for (r = 1; r < _NSIG; r++)
+ signal(r, SIG_DFL);
+
+ log_init(__progname);
+ timer_init();
+
+ pfkey_snapshot(0);
+ exit(1);
+
+ r = conf_init(argc, argv);
+ if (r > 1) {
+ fprintf(stderr, "Usage: %s [-c config-file] [-d] [-v[v]]\n",
+ __progname);
+ fprintf(stderr, "Default configuration file is %s\n",
+ SASYNCD_CFGFILE);
+ }
+ if (r)
+ return 1;
+
+ if (carp_init())
+ return 1;
+ if (pfkey_init(0))
+ return 1;
+ if (net_init())
+ return 1;
+
+ /* Drop privileges. */
+ privdrop();
+
+ if (!cfgstate.debug)
+ if (daemon(1, 0)) {
+ perror("daemon()");
+ exit(1);
+ }
+
+ /* Main loop. */
+ sasyncd_run();
+
+ /* Shutdown. */
+ log_msg(0, "shutting down...");
+
+ net_shutdown();
+ pfkey_shutdown();
+ return 0;
+}
+
+/* Special for compiling with Boehms GC. See Makefile and sasyncd.h */
+#if defined (GC_DEBUG)
+char *
+gc_strdup(const char *x)
+{
+ char *strcpy(char *,const char *);
+ char *y = malloc(strlen(x) + 1);
+ return strcpy(y,x);
+}
+#endif
diff --git a/usr.sbin/sasyncd/sasyncd.conf.5 b/usr.sbin/sasyncd/sasyncd.conf.5
new file mode 100644
index 00000000000..4b70f469645
--- /dev/null
+++ b/usr.sbin/sasyncd/sasyncd.conf.5
@@ -0,0 +1,104 @@
+.\" $OpenBSD: sasyncd.conf.5,v 1.1 2005/03/30 18:44:49 ho Exp $
+.\"
+.\" Copyright (c) 2005 Håkan Olsson. All rights reserved.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\" This code was written under funding by Multicom Security AB.
+.\"
+.\" Manual page for sasyncd.conf
+.\"
+.Dd August 07, 2004
+.Dt SASYNCD.CONF 5
+.Os
+.Sh NAME
+.Nm sasyncd.conf
+.Nd configuration file for sasyncd
+.Sh DESCRIPTION
+.Nm
+is the configuration file for the
+.Xr sasyncd 8
+daemon.
+.Pp
+Comments can be put anywhere in the file using a hash mark
+.Pq Sq # ,
+and extends to the end of the current line.
+.Pp
+The following configuration settings are understood:
+.Bl -tag -width Ds
+.It Ic CAcertificate file Ar filename
+Specify a file containing the CA certificate.
+The default is
+.Pa /etc/ssl/ca.crt .
+.It Ic carp interface Ar interface
+Specify which
+.Xr carp 4
+interface
+.Nm sasyncd
+should track master/slave state on.
+.It Ic carp interval Ar seconds
+Specify how often the daemon should check the above interface for
+state changes.
+Defaults to once every 10 seconds.
+.It Ic certificate file Ar filename
+Specify a file containing our certificate.
+The default is
+.Pa /etc/ssl/sasyncd.crt .
+.It Ic listen on Ar address
+Specify a local IP address, hostname, or interface the
+.Xr sasyncd 8
+daemon should listen on.
+The default is to listen on all local addresses.
+.It Ic listen port Ar port
+Specify a local port the
+.Xr sasyncd 8
+daemon should listen on.
+The default is to listen on port 501.
+.It Ic peer Ar address
+Specify a
+.Xr sasyncd 8
+peer IP address or hostname.
+May be specified multiple times.
+For example:
+.Bd -literal -offset indent
+peer 10.0.0.2
+peer 10.0.0.3
+peer 10.0.0.4
+.Ed
+.It Ic private key file Ar filename
+Specify a file containing the private key matching the certificate
+specified using
+.Ar ceritificate file .
+The default is
+.Pa /etc/ssl/private/sasyncd.key .
+.It Ic run as master | slave
+Force the daemon to run as master or slave.
+Normally only intended for debugging use.
+.El
+.Sh SEE ALSO
+.Xr carp 4 ,
+.Xr sasyncd 8
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 3.7 .
diff --git a/usr.sbin/sasyncd/sasyncd.h b/usr.sbin/sasyncd/sasyncd.h
new file mode 100644
index 00000000000..aa37ff3c161
--- /dev/null
+++ b/usr.sbin/sasyncd/sasyncd.h
@@ -0,0 +1,142 @@
+/* $OpenBSD: sasyncd.h,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/queue.h>
+
+enum RUNSTATE { INIT = 0, SLAVE, MASTER, FAIL };
+#define CARPSTATES { "INIT", "SLAVE", "MASTER", "FAIL" }
+
+struct syncpeer;
+struct timeval;
+
+struct cfgstate {
+ enum RUNSTATE runstate;
+ enum RUNSTATE lockedstate;
+ int debug;
+ int verboselevel;
+
+ char *carp_ifname;
+ int carp_check_interval;
+
+ char *cafile;
+ char *certfile;
+ char *privkeyfile;
+
+ int pfkey_socket;
+
+ char *listen_on;
+ in_port_t listen_port;
+
+ LIST_HEAD(, syncpeer) peerlist;
+};
+
+extern struct cfgstate cfgstate;
+extern char *__progname;
+
+#define SASYNCD_USER "_isakmpd"
+#define SASYNCD_CFGFILE "/etc/sasyncd.conf"
+
+#define CARP_DEFAULT_INTERVAL 10
+
+#define SASYNCD_DEFAULT_PORT 501
+#define SASYNCD_CAFILE "/etc/ssl/ca.crt"
+#define SASYNCD_CERTFILE "/etc/ssl/sasyncd.crt"
+#define SASYNCD_PRIVKEY "/etc/ssl/private/sasyncd.key"
+
+/*
+ * sasyncd "protocol" definition
+ *
+ * Message format:
+ * u_int32_t type
+ * u_int32_t len
+ * raw data
+ */
+
+/* sasyncd protocol message types */
+#define MSG_SYNCCTL 0
+#define MSG_PFKEYDATA 1
+#define MSG_MAXTYPE 1 /* Increase when new types are added. */
+
+/* conf.c */
+int conf_init(int, char **);
+
+/* carp.c */
+void carp_check_state(void);
+int carp_init(void);
+
+/* log.c */
+void log_init(char *);
+void log_msg(int, const char *, ...);
+void log_err(const char *, ...);
+
+/* net.c */
+void net_ctl_update_state(void);
+int net_init(void);
+void net_handle_messages(fd_set *);
+int net_queue(struct syncpeer *, u_int32_t, u_int8_t *, u_int32_t,
+ u_int32_t);
+void net_send_messages(fd_set *);
+int net_set_rfds(fd_set *);
+int net_set_pending_wfds(fd_set *);
+void net_shutdown(void);
+
+/* pfkey.c */
+int pfkey_init(int);
+int pfkey_queue_message(u_int8_t *, u_int32_t);
+void pfkey_read_message(fd_set *);
+void pfkey_send_message(fd_set *);
+void pfkey_set_rfd(fd_set *);
+void pfkey_set_pending_wfd(fd_set *);
+int pfkey_set_promisc(void);
+void pfkey_shutdown(void);
+void pfkey_snapshot(void *);
+
+/* timer.c */
+void timer_init(void);
+void timer_next_event(struct timeval *);
+void timer_run(void);
+int timer_add(char *, u_int32_t, void (*)(void *), void *);
+
+#if defined (GC_DEBUG)
+/* Boehms GC */
+void *GC_debug_malloc(size_t, char *, int);
+void *GC_debug_realloc(void *, size_t, char *, int);
+void GC_debug_free(void *);
+char *gc_strdup(const char *);
+
+#define malloc(x) GC_debug_malloc ((x), __FILE__, __LINE__)
+#define realloc(x,y) GC_debug_realloc ((x), (y), __FILE__, __LINE__)
+#define free(x) GC_debug_free (x)
+#define calloc(x,y) malloc((x) * (y))
+#define strdup(x) gc_strdup((x))
+
+#endif /* WITH_BOEHM_GC */
diff --git a/usr.sbin/sasyncd/timer.c b/usr.sbin/sasyncd/timer.c
new file mode 100644
index 00000000000..5582d6918ce
--- /dev/null
+++ b/usr.sbin/sasyncd/timer.c
@@ -0,0 +1,143 @@
+/* $OpenBSD: timer.c,v 1.1 2005/03/30 18:44:49 ho Exp $ */
+
+/*
+ * Copyright (c) 2005 Håkan Olsson. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Multicom Security AB.
+ */
+
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "sasyncd.h"
+
+/*
+ * All events have a name, an expiration time and a function to be called
+ * at this time. The queue is always sorted so the next event to happen is
+ * first in the queue.
+ */
+struct event {
+ TAILQ_ENTRY (event) next;
+ struct timeval expire;
+ char *name;
+ void (*fun) (void *);
+ void *arg;
+};
+
+static TAILQ_HEAD (event_head, event) events;
+
+/* Initialize timer event queue. */
+void
+timer_init(void)
+{
+ TAILQ_INIT(&events);
+}
+
+/*
+ * Return the number of seconds until the next event happens. Used for
+ * the select() call in the main loop.
+ */
+void
+timer_next_event(struct timeval *tv)
+{
+ struct timeval now;
+ struct event *e = TAILQ_FIRST(&events);
+
+ if (e) {
+ gettimeofday(&now, 0);
+ if (timercmp(&now, &e->expire, >=))
+ timerclear(tv);
+ else
+ timersub(&e->expire, &now, tv);
+ } else {
+ tv->tv_sec = 60; /* "Best guess". */
+ tv->tv_usec = 0;
+ }
+}
+
+/*
+ * Whenever select() times out, we have an event that should happen and this
+ * routine gets called. Handle and remove all pending events.
+ */
+void
+timer_run(void)
+{
+ struct timeval now;
+ struct event *e;
+
+ gettimeofday(&now, 0);
+ for (e = TAILQ_FIRST(&events); e && timercmp(&now, &e->expire, >=);
+ e = TAILQ_FIRST(&events)) {
+ TAILQ_REMOVE(&events, e, next);
+ log_msg(4, "timer_run: event \"%s\"",
+ e->name ? e->name : "<unknown>");
+ (*e->fun)(e->arg);
+ if (e->name)
+ free(e->name);
+ free(e);
+ }
+}
+
+/* Add a new event. */
+int
+timer_add(char *name, u_int32_t when, void (*function)(void *), void *arg)
+{
+ struct timeval now, tmp;
+ struct event *e, *new;
+
+ new = (struct event *)calloc(1, sizeof *new);
+ if (!new) {
+ log_err("timer_add: calloc (1, %u) failed", sizeof *new);
+ return -1;
+ }
+
+ new->name = strdup(name); /* We handle failures here. */
+ new->fun = function;
+ new->arg = arg;
+
+ memset(&tmp, 0, sizeof tmp);
+ tmp.tv_sec = when;
+ gettimeofday(&now, 0);
+ timeradd(&now, &tmp, &new->expire);
+
+ log_msg(4, "timer_add: new event \"%s\" (expiring in %us)",
+ name ? name : "<unknown>", when);
+
+ /* Insert the new event in the queue so it's always sorted. */
+ for (e = TAILQ_FIRST(&events); e; e = TAILQ_NEXT(e, next)) {
+ if (timercmp(&new->expire, &e->expire, >=))
+ continue;
+ TAILQ_INSERT_BEFORE(e, new, next);
+ return 0;
+ }
+ TAILQ_INSERT_TAIL(&events, new, next);
+ return 0;
+}