Commit Diff


commit - dbef7d4f82e95c334a131206f7d2ac02d3ef21bd
commit + 591e6ab59d9901a8a5a1eec8f4593ffc39656829
blob - 158491944da3175eea0b36906f2bc3308f1e4a12
blob + 0ae2acc345b861080a38dfedca582ad28093bb61
--- Makefile
+++ Makefile
@@ -1,10 +1,10 @@
-#	$OpenBSD: Makefile,v 1.21 2011/05/09 12:08:47 reyk Exp $
+#	$OpenBSD: Makefile,v 1.22 2011/05/19 08:56:49 reyk Exp $
 
 PROG=		relayd
 SRCS=		parse.y log.c control.c ssl.c ssl_privsep.c \
 		relayd.c pfe.c pfe_filter.c pfe_route.c hce.c relay.c \
 		relay_udp.c carp.c check_icmp.c check_tcp.c check_script.c \
-		name2id.c snmp.c shuffle.c proc.c
+		name2id.c snmp.c shuffle.c proc.c config.c
 MAN=		relayd.8 relayd.conf.5
 
 LDADD=		-levent -lssl -lcrypto -lutil
blob - 92bd2cef6f2845b02f1d5ccd45a7a678f44bde90
blob + d960f0aa6a73342dce84bdb9fa3da5e90a5ccbea
--- check_tcp.c
+++ check_tcp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: check_tcp.c,v 1.40 2011/05/05 12:01:43 reyk Exp $	*/
+/*	$OpenBSD: check_tcp.c,v 1.41 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -38,7 +38,8 @@
 #include "relayd.h"
 
 void	tcp_write(int, short, void *);
-void	tcp_host_up(int, struct ctl_tcp_event *);
+void	tcp_host_up(struct ctl_tcp_event *);
+void	tcp_close(struct ctl_tcp_event *, int);
 void	tcp_send_req(int, short, void *);
 void	tcp_read_buf(int, short, void *);
 
@@ -99,14 +100,14 @@ check_tcp(struct ctl_tcp_event *cte)
 
 	cte->buf = NULL;
 	cte->host->up = HOST_UP;
+	cte->s = s;
 	event_del(&cte->ev);
 	event_set(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_write, cte);
 	event_add(&cte->ev, &tv);
 	return;
 
 bad:
-	close(s);
-	cte->host->up = HOST_DOWN;
+	tcp_close(cte, HOST_DOWN);
 	hce_notify_done(cte->host, he);
 }
 
@@ -118,8 +119,7 @@ tcp_write(int s, short event, void *arg)
 	socklen_t		 len;
 
 	if (event == EV_TIMEOUT) {
-		close(s);
-		cte->host->up = HOST_DOWN;
+		tcp_close(cte, HOST_DOWN);
 		hce_notify_done(cte->host, HCE_TCP_CONNECT_TIMEOUT);
 		return;
 	}
@@ -128,26 +128,36 @@ tcp_write(int s, short event, void *arg)
 	if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len))
 		fatal("tcp_write: getsockopt");
 	if (err != 0) {
-		close(s);
-		cte->host->up = HOST_DOWN;
+		tcp_close(cte, HOST_DOWN);
 		hce_notify_done(cte->host, HCE_TCP_CONNECT_FAIL);
 		return;
 	}
 
 	cte->host->up = HOST_UP;
-	tcp_host_up(s, cte);
+	tcp_host_up(cte);
 }
 
 void
-tcp_host_up(int s, struct ctl_tcp_event *cte)
+tcp_close(struct ctl_tcp_event *cte, int status)
 {
-	cte->s = s;
+	close(cte->s);
+	cte->s = -1;
+	if (status != 0)
+		cte->host->up = status;
+	if (cte->buf) {
+		ibuf_free(cte->buf);
+		cte->buf = NULL;
+	}
+}
 
+void
+tcp_host_up(struct ctl_tcp_event *cte)
+{
 	switch (cte->table->conf.check) {
 	case CHECK_TCP:
 		if (cte->table->conf.flags & F_SSL)
 			break;
-		close(s);
+		tcp_close(cte, 0);
 		hce_notify_done(cte->host, HCE_TCP_CONNECT_OK);
 		return;
 	case CHECK_HTTP_CODE:
@@ -171,14 +181,14 @@ tcp_host_up(int s, struct ctl_tcp_event *cte)
 
 	if (cte->table->sendbuf != NULL) {
 		cte->req = cte->table->sendbuf;
-		event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req,
+		event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, tcp_send_req,
 		    &cte->tv_start, &cte->table->conf.timeout, cte);
 		return;
 	}
 
 	if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
 		fatalx("tcp_host_up: cannot create dynamic buffer");
-	event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf,
+	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, tcp_read_buf,
 	    &cte->tv_start, &cte->table->conf.timeout, cte);
 }
 
@@ -190,8 +200,7 @@ tcp_send_req(int s, short event, void *arg)
 	int			 len;
 
 	if (event == EV_TIMEOUT) {
-		cte->host->up = HOST_DOWN;
-		close(cte->s);
+		tcp_close(cte, HOST_DOWN);
 		hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT);
 		return;
 	}
@@ -202,8 +211,7 @@ tcp_send_req(int s, short event, void *arg)
 			if (errno == EAGAIN || errno == EINTR)
 				goto retry;
 			log_warnx("%s: cannot send request", __func__);
-			cte->host->up = HOST_DOWN;
-			close(cte->s);
+			tcp_close(cte, HOST_DOWN);
 			hce_notify_done(cte->host, HCE_TCP_WRITE_FAIL);
 			return;
 		}
@@ -230,9 +238,7 @@ tcp_read_buf(int s, short event, void *arg)
 	struct ctl_tcp_event	*cte = arg;
 
 	if (event == EV_TIMEOUT) {
-		cte->host->up = HOST_DOWN;
-		ibuf_free(cte->buf);
-		close(s);
+		tcp_close(cte, HOST_DOWN);
 		hce_notify_done(cte->host, HCE_TCP_READ_TIMEOUT);
 		return;
 	}
@@ -243,16 +249,13 @@ tcp_read_buf(int s, short event, void *arg)
 	case -1:
 		if (errno == EAGAIN || errno == EINTR)
 			goto retry;
-		cte->host->up = HOST_DOWN;
-		ibuf_free(cte->buf);
-		close(cte->s);
+		tcp_close(cte, HOST_DOWN);
 		hce_notify_done(cte->host, HCE_TCP_READ_FAIL);
 		return;
 	case 0:
 		cte->host->up = HOST_DOWN;
 		(void)cte->validate_close(cte);
-		close(cte->s);
-		ibuf_free(cte->buf);
+		tcp_close(cte, 0);
 		hce_notify_done(cte->host, cte->host->he);
 		return;
 	default:
@@ -261,9 +264,7 @@ tcp_read_buf(int s, short event, void *arg)
 		if (cte->validate_read != NULL) {
 			if (cte->validate_read(cte) != 0)
 				goto retry;
-
-			close(cte->s);
-			ibuf_free(cte->buf);
+			tcp_close(cte, 0);
 			hce_notify_done(cte->host, cte->host->he);
 			return;
 		}
blob - 9c9a3cc02e48c12fb72d8d325335093a1522a2f1
blob + 7397c34cc013a41c1b724196ba4f90529e8b9c3e
--- control.c
+++ control.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: control.c,v 1.38 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: control.c,v 1.39 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -327,8 +327,8 @@ control_dispatch_imsg(int fd, short event, void *arg)
 			}
 			break;
 		case IMSG_CTL_SHUTDOWN:
-			imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
-			    0, 0, -1, NULL, 0);
+		case IMSG_CTL_RELOAD:
+			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
 			break;
 		case IMSG_CTL_POLL:
 			proc_compose_imsg(env->sc_ps, PROC_HCE, -1,
@@ -336,25 +336,6 @@ control_dispatch_imsg(int fd, short event, void *arg)
 			imsg_compose_event(&c->iev, IMSG_CTL_OK,
 			    0, 0, -1, NULL, 0);
 			break;
-		case IMSG_CTL_RELOAD:
-			if (env->sc_prefork_relay > 0) {
-				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
-				    0, 0, -1, NULL, 0);
-				break;
-			}
-			proc_compose_imsg(env->sc_ps, PROC_PARENT, -1, IMSG_CTL_RELOAD,
-			    -1, NULL, 0);
-			/*
-			 * we unconditionnaly return a CTL_OK imsg because
-			 * we have no choice.
-			 *
-			 * so in this case, the reply relayctl gets means
-			 * that the reload command has been set,
-			 * it doesn't say whether the command succeeded or not.
-			 */
-			imsg_compose_event(&c->iev, IMSG_CTL_OK,
-			    0, 0, -1, NULL, 0);
-			break;
 		case IMSG_CTL_NOTIFY:
 			if (c->flags & CTL_CONN_NOTIFY) {
 				log_debug("%s: "
@@ -371,8 +352,8 @@ control_dispatch_imsg(int fd, short event, void *arg)
 
 			memcpy(&verbose, imsg.data, sizeof(verbose));
 
-			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, 0);
-			proc_forward_imsg(env->sc_ps, &imsg, PROC_HCE, 0);
+			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
+			proc_forward_imsg(env->sc_ps, &imsg, PROC_HCE, -1);
 			proc_forward_imsg(env->sc_ps, &imsg, PROC_RELAY, -1);
 
 			memcpy(imsg.data, &verbose, sizeof(verbose));
blob - /dev/null
blob + bb91b88c3646185a20c2f2aa4263e388e52fa452 (mode 644)
--- /dev/null
+++ config.c
@@ -0,0 +1,898 @@
+/*	$OpenBSD: config.c,v 1.1 2011/05/19 08:56:49 reyk Exp $	*/
+
+/*
+ * Copyright (c) 2011 Reyk Floeter <reyk@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/stat.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <net/route.h>
+
+#include <ctype.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <ifaddrs.h>
+
+#include <openssl/ssl.h>
+
+#include "relayd.h"
+
+int
+config_init(struct relayd *env)
+{
+	struct privsep	*ps = env->sc_ps;
+	u_int		 what;
+
+	/* Global configuration */
+	if (privsep_process == PROC_PARENT) {
+		env->sc_timeout.tv_sec = CHECK_TIMEOUT / 1000;
+		env->sc_timeout.tv_usec = (CHECK_TIMEOUT % 1000) * 1000;
+		env->sc_interval.tv_sec = CHECK_INTERVAL;
+		env->sc_interval.tv_usec = 0;
+		env->sc_prefork_relay = RELAY_NUMPROC;
+		env->sc_statinterval.tv_sec = RELAY_STATINTERVAL;
+
+		ps->ps_what[PROC_PARENT] = CONFIG_ALL;
+		ps->ps_what[PROC_PFE] = CONFIG_ALL & ~CONFIG_PROTOS;
+		ps->ps_what[PROC_HCE] = CONFIG_TABLES;
+		ps->ps_what[PROC_RELAY] =
+		    CONFIG_TABLES|CONFIG_RELAYS|CONFIG_PROTOS;
+	}
+
+	/* Other configuration */
+	what = ps->ps_what[privsep_process];
+	if (what & CONFIG_TABLES) {
+		if ((env->sc_tables =
+		    calloc(1, sizeof(*env->sc_tables))) == NULL)
+			return (-1);
+		TAILQ_INIT(env->sc_tables);
+
+		memset(&env->sc_empty_table, 0, sizeof(env->sc_empty_table));
+		env->sc_empty_table.conf.id = EMPTY_TABLE;
+		env->sc_empty_table.conf.flags |= F_DISABLE;
+		(void)strlcpy(env->sc_empty_table.conf.name, "empty",
+		    sizeof(env->sc_empty_table.conf.name));
+
+	}
+	if (what & CONFIG_RDRS) {
+		if ((env->sc_rdrs =
+		    calloc(1, sizeof(*env->sc_rdrs))) == NULL)
+			return (-1);
+		TAILQ_INIT(env->sc_rdrs);
+
+	}
+	if (what & CONFIG_RELAYS) {
+		if ((env->sc_relays =
+		    calloc(1, sizeof(*env->sc_relays))) == NULL)
+			return (-1);
+		TAILQ_INIT(env->sc_relays);
+	}
+	if (what & CONFIG_PROTOS) {
+		if ((env->sc_protos =
+		    calloc(1, sizeof(*env->sc_protos))) == NULL)
+			return (-1);
+		TAILQ_INIT(env->sc_protos);
+
+		bzero(&env->sc_proto_default, sizeof(env->sc_proto_default));
+		env->sc_proto_default.id = EMPTY_ID;
+		env->sc_proto_default.flags = F_USED;
+		env->sc_proto_default.cache = RELAY_CACHESIZE;
+		env->sc_proto_default.tcpflags = TCPFLAG_DEFAULT;
+		env->sc_proto_default.tcpbacklog = RELAY_BACKLOG;
+		env->sc_proto_default.sslflags = SSLFLAG_DEFAULT;
+		(void)strlcpy(env->sc_proto_default.sslciphers,
+		    SSLCIPHERS_DEFAULT,
+		    sizeof(env->sc_proto_default.sslciphers));
+		env->sc_proto_default.type = RELAY_PROTO_TCP;
+		(void)strlcpy(env->sc_proto_default.name, "default",
+		    sizeof(env->sc_proto_default.name));
+		RB_INIT(&env->sc_proto_default.request_tree);
+		RB_INIT(&env->sc_proto_default.response_tree);
+	}
+	if (what & CONFIG_RTS) {
+		if ((env->sc_rts =
+		    calloc(1, sizeof(*env->sc_rts))) == NULL)
+			return (-1);
+		TAILQ_INIT(env->sc_rts);
+	}
+	if (what & CONFIG_ROUTES) {
+		if ((env->sc_routes =
+		    calloc(1, sizeof(*env->sc_routes))) == NULL)
+			return (-1);
+		TAILQ_INIT(env->sc_routes);
+	}
+
+	return (0);
+}
+
+void
+config_purge(struct relayd *env, u_int reset)
+{
+	struct privsep		*ps = env->sc_ps;
+	struct table		*table;
+	struct rdr		*rdr;
+	struct address		*virt;
+	struct protocol		*proto;
+	struct relay		*rlay;
+	struct netroute		*nr;
+	struct router		*rt;
+	u_int			 what;
+
+	what = ps->ps_what[privsep_process] & reset;
+
+	if (what & CONFIG_TABLES && env->sc_tables != NULL) {
+		while ((table = TAILQ_FIRST(env->sc_tables)) != NULL)
+			purge_table(env->sc_tables, table);
+		env->sc_tablecount = 0;
+	}
+	if (what & CONFIG_RDRS && env->sc_rdrs != NULL) {
+		while ((rdr = TAILQ_FIRST(env->sc_rdrs)) != NULL) {
+			TAILQ_REMOVE(env->sc_rdrs, rdr, entry);
+			while ((virt = TAILQ_FIRST(&rdr->virts)) != NULL) {
+				TAILQ_REMOVE(&rdr->virts, virt, entry);
+				free(virt);
+			}
+			free(rdr);
+		}
+		env->sc_rdrcount = 0;
+	}
+	if (what & CONFIG_RELAYS && env->sc_relays != NULL) {
+		while ((rlay = TAILQ_FIRST(env->sc_relays)) != NULL)
+			purge_relay(env, rlay);
+		env->sc_relaycount = 0;
+	}
+	if (what & CONFIG_PROTOS && env->sc_protos != NULL) {
+		while ((proto = TAILQ_FIRST(env->sc_protos)) != NULL) {
+			TAILQ_REMOVE(env->sc_protos, proto, entry);
+			purge_tree(&proto->request_tree);
+			purge_tree(&proto->response_tree);
+			if (proto->style != NULL)
+				free(proto->style);
+			free(proto);
+		}
+		env->sc_protocount = 0;
+	}
+	if (what & CONFIG_RTS && env->sc_rts != NULL) {
+		while ((rt = TAILQ_FIRST(env->sc_rts)) != NULL) {
+			TAILQ_REMOVE(env->sc_rts, rt, rt_entry);
+			while ((nr = TAILQ_FIRST(&rt->rt_netroutes)) != NULL) {
+				TAILQ_REMOVE(&rt->rt_netroutes, nr, nr_entry);
+				TAILQ_REMOVE(env->sc_routes, nr, nr_route);
+				free(nr);
+				env->sc_routecount--;
+			}
+			free(rt);
+		}
+		env->sc_routercount = 0;
+	}
+	if (what & CONFIG_ROUTES && env->sc_routes != NULL) {
+		while ((nr = TAILQ_FIRST(env->sc_routes)) != NULL) {
+			if ((rt = nr->nr_router) != NULL)
+				TAILQ_REMOVE(&rt->rt_netroutes, nr, nr_entry);
+			TAILQ_REMOVE(env->sc_routes, nr, nr_route);
+			free(nr);
+		}
+		env->sc_routecount = 0;
+	}
+}
+
+int
+config_setreset(struct relayd *env, u_int reset)
+{
+	struct privsep	*ps = env->sc_ps;
+	int		 id;
+
+	for (id = 0; id < PROC_MAX; id++) {
+		if ((reset & ps->ps_what[id]) == 0 ||
+		    id == privsep_process)
+			continue;
+		proc_compose_imsg(ps, id, -1, IMSG_CTL_RESET, -1,
+		    &reset, sizeof(reset));
+	}
+
+	return (0);
+}
+
+int
+config_getreset(struct relayd *env, struct imsg *imsg)
+{
+	u_int		 mode;
+
+	IMSG_SIZE_CHECK(imsg, &mode);
+	memcpy(&mode, imsg->data, sizeof(mode));
+
+	config_purge(env, mode);
+
+	return (0);
+}
+
+int
+config_getcfg(struct relayd *env, struct imsg *imsg)
+{
+	struct privsep		*ps = env->sc_ps;
+	struct table		*tb;
+	struct host		*h, *ph;
+	struct ctl_flags	 cf;
+
+	if (IMSG_DATA_SIZE(imsg) != sizeof(cf))
+		return (0); /* ignore */
+
+	/* Update runtime flags */
+	memcpy(&cf, imsg->data, sizeof(cf));
+	env->sc_opts = cf.cf_opts;
+	env->sc_flags = cf.cf_flags;
+
+	if (ps->ps_what[privsep_process] & CONFIG_TABLES) {
+		/* Update the tables */
+		TAILQ_FOREACH(tb, env->sc_tables, entry) {
+			TAILQ_FOREACH(h, &tb->hosts, entry) {
+				if (h->conf.parentid && (ph = host_find(env,
+				    h->conf.parentid)) != NULL) {
+					SLIST_INSERT_HEAD(&ph->children,
+					    h, child);
+				}
+			}
+		}
+	}
+
+	if (env->sc_flags & (F_SSL|F_SSLCLIENT))
+		ssl_init(env);
+
+	if (privsep_process != PROC_PARENT)
+		proc_compose_imsg(env->sc_ps, PROC_PARENT, -1,
+		    IMSG_CFG_DONE, -1, NULL, 0);
+
+	return (0);
+}
+
+int
+config_settable(struct relayd *env, struct table *tb)
+{
+	struct privsep	*ps = env->sc_ps;
+	struct host	*host;
+	int		 id, c;
+	struct iovec	 iov[2];
+
+	for (id = 0; id < PROC_MAX; id++) {
+		if ((ps->ps_what[id] & CONFIG_TABLES) == 0 ||
+		    id == privsep_process)
+			continue;
+
+		/* XXX need to send table to pfe for control socket */
+		if (id == PROC_HCE && tb->conf.check == CHECK_NOCHECK)
+			continue;
+
+		DPRINTF("%s: sending table %s %d to %s", __func__,
+		    tb->conf.name, tb->conf.id, env->sc_ps->ps_title[id]);
+
+		c = 0;
+		iov[c].iov_base = &tb->conf;
+		iov[c++].iov_len = sizeof(tb->conf);
+		if (tb->sendbuf != NULL) {
+			iov[c].iov_base = tb->sendbuf;
+			iov[c++].iov_len = strlen(tb->sendbuf);
+		}
+
+		proc_composev_imsg(ps, id, -1, IMSG_CFG_TABLE, -1, iov, c);
+
+		TAILQ_FOREACH(host, &tb->hosts, entry) {
+			proc_compose_imsg(ps, id, -1, IMSG_CFG_HOST, -1,
+			    &host->conf, sizeof(host->conf));
+		}
+	}
+
+	return (0);
+}
+
+int
+config_gettable(struct relayd *env, struct imsg *imsg)
+{
+	struct table		*tb;
+	size_t			 sb;
+	u_int8_t		*p = imsg->data;
+	size_t			 s;
+
+	if ((tb = calloc(1, sizeof(*tb))) == NULL)
+		return (-1);
+
+	IMSG_SIZE_CHECK(imsg, &tb->conf);
+	memcpy(&tb->conf, p, sizeof(tb->conf));
+	s = sizeof(tb->conf);
+
+	sb = IMSG_DATA_SIZE(imsg) - s;
+	if (sb > 0)
+		tb->sendbuf = get_string(p + s, sb);
+
+	TAILQ_INIT(&tb->hosts);
+	TAILQ_INSERT_TAIL(env->sc_tables, tb, entry);
+
+	env->sc_tablecount++;
+
+	DPRINTF("%s: %s %d received table %d (%s)", __func__,
+	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
+	    tb->conf.id, tb->conf.name);
+
+	return (0);
+}
+
+int
+config_gethost(struct relayd *env, struct imsg *imsg)
+{
+	struct table		*tb;
+	struct host		*host;
+
+	if ((host = calloc(1, sizeof(*host))) == NULL)
+		return (-1);
+
+	IMSG_SIZE_CHECK(imsg, &host->conf);
+	memcpy(&host->conf, imsg->data, sizeof(host->conf));
+
+	if (host_find(env, host->conf.id) != NULL) {
+		log_debug("%s: host %d already exists",
+		    __func__, host->conf.id);
+		free(host);
+		return (-1);
+	}
+
+	if ((tb = table_find(env, host->conf.tableid)) == NULL) {
+		log_debug("%s: "
+		    "received host for unknown table %d", __func__,
+		    host->conf.tableid);
+		free(host);
+		return (-1);
+	}
+
+	host->tablename = tb->conf.name;
+	host->cte.s = -1;
+
+	SLIST_INIT(&host->children);
+	TAILQ_INSERT_TAIL(&tb->hosts, host, entry);
+
+	DPRINTF("%s: %s %d received host %s for table %s", __func__,
+	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
+	    host->conf.name, tb->conf.name);
+
+	return (0);
+}
+
+int
+config_setrdr(struct relayd *env, struct rdr *rdr)
+{
+	struct privsep	*ps = env->sc_ps;
+	struct address	*virt;
+	int		 id;
+
+	for (id = 0; id < PROC_MAX; id++) {
+		if ((ps->ps_what[id] & CONFIG_RDRS) == 0 ||
+		    id == privsep_process)
+			continue;
+
+		DPRINTF("%s: sending rdr %s to %s", __func__,
+		    rdr->conf.name, ps->ps_title[id]);
+
+		proc_compose_imsg(ps, id, -1, IMSG_CFG_RDR, -1,
+		    &rdr->conf, sizeof(rdr->conf));
+
+		TAILQ_FOREACH(virt, &rdr->virts, entry) {
+			virt->rdrid = rdr->conf.id;
+			proc_compose_imsg(ps, id, -1, IMSG_CFG_VIRT, -1,
+			    virt, sizeof(*virt));
+		}
+	}
+
+	return (0);
+}
+
+int
+config_getrdr(struct relayd *env, struct imsg *imsg)
+{
+	struct rdr		*rdr;
+
+	if ((rdr = calloc(1, sizeof(*rdr))) == NULL)
+		return (-1);
+
+	IMSG_SIZE_CHECK(imsg, &rdr->conf);
+	memcpy(&rdr->conf, imsg->data, sizeof(rdr->conf));
+
+	if ((rdr->table = table_find(env, rdr->conf.table_id)) == NULL) {
+		log_debug("%s: table not found", __func__);
+		free(rdr);
+		return (-1);
+	}
+	if ((rdr->backup = table_find(env, rdr->conf.backup_id)) == NULL) {
+		rdr->conf.backup_id = EMPTY_TABLE;
+		rdr->backup = &env->sc_empty_table;
+	}
+
+	TAILQ_INIT(&rdr->virts);
+	TAILQ_INSERT_TAIL(env->sc_rdrs, rdr, entry);
+
+	env->sc_rdrcount++;
+
+	DPRINTF("%s: %s %d received rdr %s", __func__,
+	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
+	    rdr->conf.name);
+
+	return (0);
+}
+
+int
+config_getvirt(struct relayd *env, struct imsg *imsg)
+{
+	struct rdr	*rdr;
+	struct address	*virt;
+
+	IMSG_SIZE_CHECK(imsg, virt);
+
+	if ((virt = calloc(1, sizeof(*virt))) == NULL)
+		return (-1);
+	memcpy(virt, imsg->data, sizeof(*virt));
+
+	if ((rdr = rdr_find(env, virt->rdrid)) == NULL) {
+		log_debug("%s: rdr not found", __func__);
+		free(virt);
+		return (-1);
+	}
+
+	TAILQ_INSERT_TAIL(&rdr->virts, virt, entry);
+
+	DPRINTF("%s: %s %d received address for rdr %s", __func__,
+	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
+	    rdr->conf.name);
+
+	return (0);
+}
+
+int
+config_setrt(struct relayd *env, struct router *rt)
+{
+	struct privsep	*ps = env->sc_ps;
+	struct netroute	*nr;
+	int		 id;
+
+	for (id = 0; id < PROC_MAX; id++) {
+		if ((ps->ps_what[id] & CONFIG_RTS) == 0 ||
+		    id == privsep_process)
+			continue;
+
+		DPRINTF("%s: sending router %s to %s tbl %d", __func__,
+		    rt->rt_conf.name, ps->ps_title[id], rt->rt_conf.gwtable);
+
+		proc_compose_imsg(ps, id, -1, IMSG_CFG_ROUTER, -1,
+		    &rt->rt_conf, sizeof(rt->rt_conf));
+
+		TAILQ_FOREACH(nr, &rt->rt_netroutes, nr_entry) {
+			proc_compose_imsg(ps, id, -1, IMSG_CFG_ROUTE, -1,
+			    &nr->nr_conf, sizeof(nr->nr_conf));
+		}
+	}
+
+	return (0);
+}
+
+int
+config_getrt(struct relayd *env, struct imsg *imsg)
+{
+	struct router		*rt;
+
+	if ((rt = calloc(1, sizeof(*rt))) == NULL)
+		return (-1);
+
+	IMSG_SIZE_CHECK(imsg, &rt->rt_conf);
+	memcpy(&rt->rt_conf, imsg->data, sizeof(rt->rt_conf));
+
+	if ((rt->rt_gwtable = table_find(env, rt->rt_conf.gwtable)) == NULL) {
+		log_debug("%s: table not found", __func__);
+		free(rt);
+		return (-1);
+	}
+
+	TAILQ_INIT(&rt->rt_netroutes);
+	TAILQ_INSERT_TAIL(env->sc_rts, rt, rt_entry);
+
+	env->sc_routercount++;
+
+	DPRINTF("%s: %s %d received router %s", __func__,
+	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
+	    rt->rt_conf.name);
+
+	return (0);
+}
+
+int
+config_getroute(struct relayd *env, struct imsg *imsg)
+{
+	struct router		*rt;
+	struct netroute		*nr;
+
+	if ((nr = calloc(1, sizeof(*nr))) == NULL)
+		return (-1);
+
+	IMSG_SIZE_CHECK(imsg, &nr->nr_conf);
+	memcpy(&nr->nr_conf, imsg->data, sizeof(nr->nr_conf));
+
+	if (route_find(env, nr->nr_conf.id) != NULL) {
+		log_debug("%s: route %d already exists",
+		    __func__, nr->nr_conf.id);
+		free(nr);
+		return (-1);
+	}
+
+	if ((rt = router_find(env, nr->nr_conf.routerid)) == NULL) {
+		log_debug("%s: received route for unknown router", __func__);
+		free(nr);
+		return (-1);
+	}
+
+	nr->nr_router = rt;
+
+	TAILQ_INSERT_TAIL(env->sc_routes, nr, nr_route);
+	TAILQ_INSERT_TAIL(&rt->rt_netroutes, nr, nr_entry);
+
+	env->sc_routecount++;
+
+	DPRINTF("%s: %s %d received route %d for router %s", __func__,
+	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
+	    nr->nr_conf.id, rt->rt_conf.name);
+
+	return (0);
+}
+
+int
+config_setproto(struct relayd *env, struct protocol *proto)
+{
+	struct privsep		*ps = env->sc_ps;
+	int			 id;
+	struct iovec		 iov[2];
+	size_t			 c;
+
+	for (id = 0; id < PROC_MAX; id++) {
+		if ((ps->ps_what[id] & CONFIG_PROTOS) == 0 ||
+		    id == privsep_process)
+			continue;
+
+		DPRINTF("%s: sending protocol %s to %s", __func__,
+		    proto->name, ps->ps_title[id]);
+
+		c = 0;
+		iov[c].iov_base = proto;
+		iov[c++].iov_len = sizeof(*proto);
+
+		if (proto->style != NULL) {
+			iov[c].iov_base = proto->style;
+			iov[c++].iov_len = strlen(proto->style);
+		}
+
+		/* XXX struct protocol should be split */
+		proc_composev_imsg(ps, id, -1, IMSG_CFG_PROTO, -1, iov, c);
+
+		/* Now send all the protocol key/value nodes */
+		if (config_setprotonode(env, id, proto,
+		    RELAY_DIR_REQUEST) == -1 ||
+		    config_setprotonode(env, id, proto,
+		    RELAY_DIR_RESPONSE) == -1)
+			return (-1);
+	}
+
+	return (0);
+}
+
+int
+config_getproto(struct relayd *env, struct imsg *imsg)
+{
+	struct protocol		*proto;
+	size_t			 styl;
+
+	if ((proto = calloc(1, sizeof(*proto))) == NULL)
+		return (-1);
+
+	IMSG_SIZE_CHECK(imsg, proto);
+	memcpy(proto, imsg->data, sizeof(*proto));
+
+	styl = IMSG_DATA_SIZE(imsg) - sizeof(*proto);
+	if (styl > 0)
+		proto->style = get_string((char *)imsg->data +
+		    sizeof(*proto), styl);
+
+	proto->request_nodes = 0;
+	proto->response_nodes = 0;
+	RB_INIT(&proto->request_tree);
+	RB_INIT(&proto->response_tree);
+
+	TAILQ_INSERT_TAIL(env->sc_protos, proto, entry);
+
+	env->sc_protocount++;
+
+	DPRINTF("%s: %s %d received protocol %s", __func__,
+	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
+	    proto->name);
+
+	return (0);
+}
+
+int
+config_setprotonode(struct relayd *env, enum privsep_procid id,
+    struct protocol *proto, enum direction dir)
+{
+	struct privsep		*ps = env->sc_ps;
+	struct iovec		 iov[IOV_MAX];
+	size_t			 c, sz;
+	struct protonode	*proot, *pn;
+	struct proto_tree	*tree;
+
+	if (dir == RELAY_DIR_RESPONSE)
+		tree = &proto->response_tree;
+	else
+		tree = &proto->request_tree;
+
+	sz = c = 0;
+	RB_FOREACH(proot, proto_tree, tree) {
+		PROTONODE_FOREACH(pn, proot, entry) {
+			pn->conf.protoid = proto->id;
+			pn->conf.dir = dir;
+			pn->conf.keylen = pn->key ? strlen(pn->key) : 0;
+			pn->conf.valuelen = pn->value ? strlen(pn->value) : 0;
+			pn->conf.len = sizeof(*pn) +
+			    pn->conf.keylen + pn->conf.valuelen;
+
+			if (pn->conf.len > (MAX_IMSGSIZE - IMSG_HEADER_SIZE))
+				return (-1);
+
+			if (c && ((c + 3) >= IOV_MAX || (sz + pn->conf.len) >
+			    (MAX_IMSGSIZE - IMSG_HEADER_SIZE))) {
+				proc_composev_imsg(ps, id, -1,
+				    IMSG_CFG_PROTONODE, -1, iov, c);
+				c = sz = 0;
+			}
+
+			iov[c].iov_base = pn;
+			iov[c++].iov_len = sizeof(*pn);
+			if (pn->conf.keylen) {
+				iov[c].iov_base = pn->key;
+				iov[c++].iov_len = pn->conf.keylen;
+			}
+			if (pn->conf.valuelen) {
+				iov[c].iov_base = pn->value;
+				iov[c++].iov_len = pn->conf.valuelen;
+			}
+			sz += pn->conf.len;
+		}
+	}
+
+	if (c && sz)
+		proc_composev_imsg(ps, id, -1, IMSG_CFG_PROTONODE, -1, iov, c);
+
+	return (0);
+}
+
+int
+config_getprotonode(struct relayd *env, struct imsg *imsg)
+{
+	struct protocol		*proto = NULL;
+	struct protonode	 pn;
+	size_t			 z, s, c = 0;
+	u_int8_t		*p = imsg->data;
+
+	bzero(&pn, sizeof(pn));
+
+	IMSG_SIZE_CHECK(imsg, &pn);
+	for (z = 0; z < (IMSG_DATA_SIZE(imsg) - sizeof(pn)); z += pn.conf.len) {
+		s = z;
+		memcpy(&pn, p + s, sizeof(pn));
+		s += sizeof(pn);
+
+		if ((proto = proto_find(env, pn.conf.protoid)) == NULL) {
+			log_debug("%s: unknown protocol %d", __func__,
+			    pn.conf.protoid);
+			return (-1);
+		}
+
+		pn.key = pn.value = NULL;
+		bzero(&pn.entry, sizeof(pn.entry));
+		bzero(&pn.nodes, sizeof(pn.nodes));
+		bzero(&pn.head, sizeof(pn.head));
+
+		if (pn.conf.keylen) {
+			pn.key = get_string(p + s, pn.conf.keylen);
+			s += pn.conf.keylen;
+		}
+		if (pn.conf.valuelen) {
+			pn.value = get_string(p + s, pn.conf.valuelen);
+			s += pn.conf.valuelen;
+		}
+
+		if (protonode_add(pn.conf.dir, proto, &pn) == -1) {
+			log_debug("%s: failed to add protocol node", __func__);
+			return (-1);
+		}
+		c++;
+	}
+
+	if (!c)
+		return (0);
+
+	DPRINTF("%s: %s %d received %d nodes for protocol %s", __func__,
+	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
+	    c, proto->name);
+
+	return (0);
+}
+
+int
+config_setrelay(struct relayd *env, struct relay *rlay)
+{
+	struct privsep	*ps = env->sc_ps;
+	int		 id;
+	int		 fd, n, m;
+	struct iovec	 iov[4];
+	size_t		 c;
+
+	/* opens listening sockets etc. */
+	if (relay_privinit(rlay) == -1)
+		return (-1);
+
+	for (id = 0; id < PROC_MAX; id++) {
+		if ((ps->ps_what[id] & CONFIG_RELAYS) == 0 ||
+		    id == privsep_process)
+			continue;
+
+		DPRINTF("%s: sending relay %s to %s fd %d", __func__,
+		    rlay->rl_conf.name, ps->ps_title[id], rlay->rl_s);
+
+		c = 0;
+		iov[c].iov_base = &rlay->rl_conf;
+		iov[c++].iov_len = sizeof(rlay->rl_conf);
+		if (rlay->rl_conf.ssl_cert_len) {
+			iov[c].iov_base = rlay->rl_ssl_cert;
+			iov[c++].iov_len = rlay->rl_conf.ssl_cert_len;
+		}
+		if (rlay->rl_conf.ssl_key_len) {
+			iov[c].iov_base = rlay->rl_ssl_key;
+			iov[c++].iov_len = rlay->rl_conf.ssl_key_len;
+		}
+		if (rlay->rl_conf.ssl_ca_len) {
+			iov[c].iov_base = rlay->rl_ssl_ca;
+			iov[c++].iov_len = rlay->rl_conf.ssl_ca_len;
+		}
+
+		if (id == PROC_RELAY) {
+			/* XXX imsg code will close the fd after 1st call */
+			n = -1;
+			proc_range(ps, id, &n, &m);
+			for (n = 0; n < m; n++) {
+				if ((fd = dup(rlay->rl_s)) == -1)
+					return (-1);
+				proc_composev_imsg(ps, id, n,
+				    IMSG_CFG_RELAY, fd, iov, c);
+			}
+		} else {
+			proc_composev_imsg(ps, id, -1, IMSG_CFG_RELAY, -1,
+			    iov, c);
+		}
+	}
+
+	close(rlay->rl_s);
+	rlay->rl_s = -1;
+
+	return (0);
+}
+
+int
+config_getrelay(struct relayd *env, struct imsg *imsg)
+{
+	struct privsep		*ps = env->sc_ps;
+	struct relay		*rlay;
+	u_int8_t		*p = imsg->data;
+	size_t			 s;
+
+	if ((rlay = calloc(1, sizeof(*rlay))) == NULL)
+		return (-1);
+
+	IMSG_SIZE_CHECK(imsg, &rlay->rl_conf);
+	memcpy(&rlay->rl_conf, p, sizeof(rlay->rl_conf));
+	s = sizeof(rlay->rl_conf);
+
+	rlay->rl_s = imsg->fd;
+
+	if (ps->ps_what[privsep_process] & CONFIG_PROTOS) {
+		if (rlay->rl_conf.proto == EMPTY_ID)
+			rlay->rl_proto = &env->sc_proto_default;
+		else if ((rlay->rl_proto =
+		    proto_find(env, rlay->rl_conf.proto)) == NULL) {
+			log_debug("%s: unknown protocol", __func__);
+			goto fail;
+		}
+	}
+
+	if (rlay->rl_conf.dsttable != EMPTY_ID &&
+	    (rlay->rl_dsttable = table_find(env,
+	    rlay->rl_conf.dsttable)) == NULL) {
+		log_debug("%s: unknown table", __func__);
+		goto fail;
+	}
+
+	rlay->rl_backuptable = &env->sc_empty_table;
+	if (rlay->rl_conf.backuptable != EMPTY_ID &&
+	    (rlay->rl_backuptable = table_find(env,
+	    rlay->rl_conf.backuptable)) == NULL) {
+		log_debug("%s: unknown backup table", __func__);
+		goto fail;
+	}
+
+	if ((u_int)(IMSG_DATA_SIZE(imsg) - s) <
+	    (rlay->rl_conf.ssl_cert_len +
+	    rlay->rl_conf.ssl_key_len +
+	    rlay->rl_conf.ssl_ca_len)) {
+		log_debug("%s: invalid message length", __func__);
+		goto fail;
+	}
+
+	if (rlay->rl_conf.ssl_cert_len) {
+		if ((rlay->rl_ssl_cert = get_data(p + s,
+		    rlay->rl_conf.ssl_cert_len)) == NULL)
+			goto fail;
+		s += rlay->rl_conf.ssl_cert_len;
+	}
+	if (rlay->rl_conf.ssl_key_len) {
+		if ((rlay->rl_ssl_key = get_data(p + s,
+		    rlay->rl_conf.ssl_key_len)) == NULL)
+			goto fail;
+		s += rlay->rl_conf.ssl_key_len;
+	}
+	if (rlay->rl_conf.ssl_ca_len) {
+		if ((rlay->rl_ssl_ca = get_data(p + s,
+		    rlay->rl_conf.ssl_ca_len)) == NULL)
+			goto fail;
+		s += rlay->rl_conf.ssl_ca_len;
+	}
+
+	TAILQ_INSERT_TAIL(env->sc_relays, rlay, rl_entry);
+
+	env->sc_relaycount++;
+
+	DPRINTF("%s: %s %d received relay %s", __func__,
+	    ps->ps_title[privsep_process], ps->ps_instance,
+	    rlay->rl_conf.name);
+
+	return (0);
+
+ fail:
+	if (rlay->rl_ssl_cert)
+		free(rlay->rl_ssl_cert);
+	if (rlay->rl_ssl_key)
+		free(rlay->rl_ssl_key);
+	if (rlay->rl_ssl_ca)
+		free(rlay->rl_ssl_ca);
+	close(rlay->rl_s);
+	free(rlay);
+	return (-1);
+}
blob - ab80166c4bb7927bacf7f67946bd0f9f98a0a9d4
blob + 147b272bfdefdfaaa6b6a509b4393b6dd180efc0
--- hce.c
+++ hce.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: hce.c,v 1.59 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: hce.c,v 1.60 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -72,14 +72,15 @@ hce(struct privsep *ps, struct privsep_proc *p)
 void
 hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
 {
-	purge_config(env, PURGE_RDRS|PURGE_RELAYS|PURGE_PROTOS);
+	if (config_init(ps->ps_env) == -1)
+		fatal("failed to initialize configuration");
 
 	env->sc_id = getpid() & 0xffff;
 
 	/* Allow maximum available sockets for TCP checks */
 	socket_rlimit(-1);
 
-	hce_setup_events();
+	snmp_init(env, PROC_PARENT);
 }
 
 void
@@ -88,18 +89,17 @@ hce_setup_events(void)
 	struct timeval	 tv;
 	struct table	*table;
 
-	snmp_init(env, PROC_PARENT);
-
-	if (!TAILQ_EMPTY(env->sc_tables)) {
+	if (!(TAILQ_EMPTY(env->sc_tables) ||
+	    event_initialized(&env->sc_ev))) {
 		evtimer_set(&env->sc_ev, hce_launch_checks, env);
 		bzero(&tv, sizeof(tv));
 		evtimer_add(&env->sc_ev, &tv);
 	}
 
 	if (env->sc_flags & F_SSL) {
-		ssl_init(env);
 		TAILQ_FOREACH(table, env->sc_tables, entry) {
-			if (!(table->conf.flags & F_SSL))
+			if (!(table->conf.flags & F_SSL) ||
+			    table->ssl_ctx != NULL)
 				continue;
 			table->ssl_ctx = ssl_ctx_create(env);
 		}
@@ -116,8 +116,10 @@ hce_disable_events(void)
 	TAILQ_FOREACH(table, env->sc_tables, entry) {
 		TAILQ_FOREACH(host, &table->hosts, entry) {
 			host->he = HCE_ABORT;
-			event_del(&host->cte.ev);
-			close(host->cte.s);
+			if (event_initialized(&host->cte.ev)) {
+				event_del(&host->cte.ev);
+				close(host->cte.s);
+			}
 		}
 	}
 	if (env->sc_has_icmp) {
@@ -146,7 +148,11 @@ hce_launch_checks(int fd, short event, void *arg)
 			if ((host->flags & F_CHECK_DONE) == 0)
 				host->he = HCE_INTERVAL_TIMEOUT;
 			host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
-			event_del(&host->cte.ev);
+			if (event_initialized(&host->cte.ev)) {
+				event_del(&host->cte.ev);
+				close(host->cte.s);
+			}
+			host->cte.s = -1;
 		}
 	}
 
@@ -331,9 +337,6 @@ int
 hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
 	struct ctl_script	 scr;
-	size_t			 len;
-	static struct table	*table = NULL;
-	struct host		*host, *parent;
 
 	switch (imsg->hdr.type) {
 	case IMSG_SCRIPT:
@@ -341,47 +344,22 @@ hce_dispatch_parent(int fd, struct privsep_proc *p, st
 		bcopy(imsg->data, &scr, sizeof(scr));
 		script_done(env, &scr);
 		break;
-	case IMSG_RECONF:
-		IMSG_SIZE_CHECK(imsg, env);
-		log_debug("%s: reloading configuration", __func__);
-		hce_disable_events();
-		purge_config(env, PURGE_TABLES);
-		merge_config(env, (struct relayd *)imsg->data);
-
-		env->sc_tables = calloc(1, sizeof(*env->sc_tables));
-		if (env->sc_tables == NULL)
-			fatal(NULL);
-
-		TAILQ_INIT(env->sc_tables);
+	case IMSG_CFG_TABLE:
+		config_gettable(env, imsg);
 		break;
-	case IMSG_RECONF_TABLE:
-		if ((table = calloc(1, sizeof(*table))) == NULL)
-			fatal(NULL);
-		memcpy(&table->conf, imsg->data, sizeof(table->conf));
-		TAILQ_INIT(&table->hosts);
-		TAILQ_INSERT_TAIL(env->sc_tables, table, entry);
+	case IMSG_CFG_HOST:
+		config_gethost(env, imsg);
 		break;
-	case IMSG_RECONF_SENDBUF:
-		len = imsg->hdr.len - IMSG_HEADER_SIZE;
-		table->sendbuf = calloc(1, len);
-		(void)strlcpy(table->sendbuf, (char *)imsg->data, len);
+	case IMSG_SNMPSOCK:
+		snmp_getsock(env, imsg);
 		break;
-	case IMSG_RECONF_HOST:
-		if ((host = calloc(1, sizeof(*host))) == NULL)
-			fatal(NULL);
-		memcpy(&host->conf, imsg->data, sizeof(host->conf));
-		host->tablename = table->conf.name;
-		TAILQ_INSERT_TAIL(&table->hosts, host, entry);
-		if (host->conf.parentid) {
-			parent = host_find(env, host->conf.parentid);
-			SLIST_INSERT_HEAD(&parent->children,
-			    host, child);
-		}
-		break;
-	case IMSG_RECONF_END:
-		log_warnx("%s: configuration reloaded", __func__);
+	case IMSG_CFG_DONE:
+		config_getcfg(env, imsg);
 		hce_setup_events();
 		break;
+	case IMSG_CTL_RESET:
+		config_getreset(env, imsg);
+		break;
 	default:
 		return (-1);
 	}
blob - c03b54c4264e2e3e86043000ab8664981b16ae50
blob + c3a5b5b5177b3754300a6de674a7ea673d6bd0ee
--- parse.y
+++ parse.y
@@ -1,4 +1,4 @@
-/*	$OpenBSD: parse.y,v 1.155 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: parse.y,v 1.156 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -28,6 +28,7 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/queue.h>
+#include <sys/hash.h>
 
 #include <net/if.h>
 #include <net/pfvar.h>
@@ -86,6 +87,7 @@ char		*symget(const char *);
 
 struct relayd		*conf = NULL;
 static int		 errors = 0;
+static int		 loadcfg = 0;
 objid_t			 last_rdr_id = 0;
 objid_t			 last_table_id = 0;
 objid_t			 last_host_id = 0;
@@ -114,6 +116,7 @@ int		 host_if(const char *, struct addresslist *,
 		    int, struct portrange *, const char *, int);
 int		 host(const char *, struct addresslist *,
 		    int, struct portrange *, const char *, int);
+void		 host_free(struct addresslist *);
 
 struct table	*table_inherit(struct table *);
 struct relay	*relay_inherit(struct relay *, struct relay *);
@@ -323,28 +326,36 @@ varset		: STRING '=' STRING	{
 
 sendbuf		: NOTHING		{
 			table->sendbuf = NULL;
-			table->sendbuf_len = 0;
 		}
 		| STRING		{
 			table->sendbuf = strdup($1);
 			if (table->sendbuf == NULL)
 				fatal("out of memory");
-			table->sendbuf_len = strlen(table->sendbuf);
 			free($1);
 		}
 		;
 
 main		: INTERVAL NUMBER	{
+			if (loadcfg)
+				break;
 			if ((conf->sc_interval.tv_sec = $2) < 0) {
 				yyerror("invalid interval: %d", $2);
 				YYERROR;
 			}
 		}
-		| LOG loglevel		{ conf->sc_opts |= $2; }
+		| LOG loglevel		{
+			if (loadcfg)
+				break;
+			conf->sc_opts |= $2;
+		}
 		| TIMEOUT timeout	{
+			if (loadcfg)
+				break;
 			bcopy(&$2, &conf->sc_timeout, sizeof(struct timeval));
 		}
 		| PREFORK NUMBER	{
+			if (loadcfg)
+				break;
 			if ($2 <= 0 || $2 > RELAY_MAXPROC) {
 				yyerror("invalid number of preforked "
 				    "relays: %d", $2);
@@ -353,6 +364,8 @@ main		: INTERVAL NUMBER	{
 			conf->sc_prefork_relay = $2;
 		}
 		| DEMOTE STRING		{
+			if (loadcfg)
+				break;
 			conf->sc_flags |= F_DEMOTE;
 			if (strlcpy(conf->sc_demote_group, $2,
 			    sizeof(conf->sc_demote_group))
@@ -368,7 +381,11 @@ main		: INTERVAL NUMBER	{
 				YYERROR;
 			}
 		}
-		| SEND TRAP		{ conf->sc_flags |= F_TRAP; }
+		| SEND TRAP		{
+			if (loadcfg)
+				break;
+			conf->sc_flags |= F_TRAP;
+		}
 		;
 
 loglevel	: UPDATES		{ $$ = RELAYD_OPT_LOGUPDATE; }
@@ -379,6 +396,12 @@ rdr		: REDIRECT STRING	{
 			struct rdr *srv;
 
 			conf->sc_flags |= F_NEEDPF;
+
+			if (!loadcfg) {
+				free($2);
+				YYACCEPT;
+			}
+
 			TAILQ_FOREACH(srv, conf->sc_rdrs, entry)
 				if (!strcmp(srv->conf.name, $2))
 					break;
@@ -544,6 +567,11 @@ table		: '<' STRING '>'	{
 tabledef	: TABLE table		{
 			struct table *tb;
 
+			if (!loadcfg) {
+				free($2);
+				YYACCEPT;
+			}
+
 			TAILQ_FOREACH(tb, conf->sc_tables, entry)
 				if (!strcmp(tb->conf.name, $2))
 					break;
@@ -701,7 +729,6 @@ tablecheck	: ICMP			{ table->conf.check = CHECK_ICMP; 
 			free($3);
 			if (table->sendbuf == NULL)
 				fatal("out of memory");
-			table->sendbuf_len = strlen(table->sendbuf);
 		}
 		| http_type STRING hostname digest {
 			if ($1) {
@@ -717,7 +744,6 @@ tablecheck	: ICMP			{ table->conf.check = CHECK_ICMP; 
 			free($3);
 			if (table->sendbuf == NULL)
 				fatal("out of memory");
-			table->sendbuf_len = strlen(table->sendbuf);
 			(void)strlcpy(table->conf.digest, $4.digest,
 			    sizeof(table->conf.digest));
 			table->conf.digest_type = $4.type;
@@ -773,6 +799,11 @@ digest		: DIGEST STRING
 proto		: relay_proto PROTO STRING	{
 			struct protocol *p;
 
+			if (!loadcfg) {
+				free($3);
+				YYACCEPT;
+			}
+
 			if (strcmp($3, "default") == 0) {
 				p = &conf->sc_proto_default;
 			} else {
@@ -936,12 +967,14 @@ sslflags	: SESSION CACHE sslcache	{ proto->cache = $3;
 			free($2);
 		}
 		| CA FILENAME STRING		{
-			if (proto->sslca != NULL) {
-				yyerror("sslca already specified");
+			if (strlcpy(proto->sslca, $3,
+			    sizeof(proto->sslca)) >=
+			    sizeof(proto->sslca)) {
+				yyerror("sslca truncated");
 				free($3);
 				YYERROR;
 			}
-			proto->sslca = $3;
+			free($3);
 		}
 		| NO flag			{ proto->sslflags &= ~($2); }
 		| flag				{ proto->sslflags |= $1; }
@@ -1182,6 +1215,11 @@ sslcache	: NUMBER			{
 relay		: RELAY STRING	{
 			struct relay *r;
 
+			if (!loadcfg) {
+				free($2);
+				YYACCEPT;
+			}
+
 			TAILQ_FOREACH(r, conf->sc_relays, rl_entry)
 				if (!strcmp(r->rl_conf.name, $2))
 					break;
@@ -1302,6 +1340,7 @@ relayoptsl	: LISTEN ON STRING port optssl {
 				conf->sc_flags |= F_SSL;
 			}
 			tableport = h->port.val[0];
+			host_free(&al);
 		}
 		| forwardmode optsslclient TO forwardspec interface dstaf {
 			rlay->rl_conf.fwdmode = $1;
@@ -1384,6 +1423,7 @@ forwardspec	: STRING port retry	{
 			    sizeof(rlay->rl_conf.dstss));
 			rlay->rl_conf.dstport = h->port.val[0];
 			rlay->rl_conf.dstretry = $3;
+			host_free(&al);
 		}
 		| NAT LOOKUP retry	{
 			conf->sc_flags |= F_NEEDPF;
@@ -1398,7 +1438,6 @@ forwardspec	: STRING port retry	{
 		| tablespec	{
 			if (rlay->rl_backuptable) {
 				yyerror("only one backup table is allowed");
-				purge_table(conf->sc_tables, $1);
 				YYERROR;
 			}
 			if (rlay->rl_dsttable) {
@@ -1423,6 +1462,11 @@ dstmode		: /* empty */		{ $$ = RELAY_DSTMODE_DEFAULT; 
 router		: ROUTER STRING		{
 			struct router *rt = NULL;
 
+			if (!loadcfg) {
+				free($2);
+				YYACCEPT;
+			}
+
 			conf->sc_flags |= F_NEEDRT;
 			TAILQ_FOREACH(rt, conf->sc_rts, rt_entry)
 				if (!strcmp(rt->rt_conf.name, $2))
@@ -1478,16 +1522,18 @@ routeopts_l	: routeopts_l routeoptsl nl
 routeoptsl	: ROUTE address '/' NUMBER {
 			struct netroute	*nr;
 
-			if (router->rt_af == AF_UNSPEC)
-				router->rt_af = $2.ss.ss_family;
-			else if (router->rt_af != $2.ss.ss_family) {
+			if (router->rt_conf.af == AF_UNSPEC)
+				router->rt_conf.af = $2.ss.ss_family;
+			else if (router->rt_conf.af != $2.ss.ss_family) {
 				yyerror("router %s address family mismatch",
 				    router->rt_conf.name);
 				YYERROR;
 			}
 
-			if ((router->rt_af == AF_INET && ($4 > 32 || $4 < 0)) ||
-			    (router->rt_af == AF_INET6 && ($4 > 128 || $4 < 0))) {
+			if ((router->rt_conf.af == AF_INET &&
+			    ($4 > 32 || $4 < 0)) ||
+			    (router->rt_conf.af == AF_INET6 &&
+			    ($4 > 128 || $4 < 0))) {
 				yyerror("invalid prefixlen %d", $4);
 				YYERROR;
 			}
@@ -1649,7 +1695,7 @@ hostflags	: RETRY NUMBER		{
 		;
 
 address		: STRING	{
-			struct address *a;
+			struct address *h;
 			struct addresslist al;
 
 			if (strlcpy($$.name, $1,
@@ -1666,9 +1712,9 @@ address		: STRING	{
 				YYERROR;
 			}
 			free($1);
-			a = TAILQ_FIRST(&al);
-			memcpy(&$$.ss, &a->ss, sizeof($$.ss));
-			free(a);
+			h = TAILQ_FIRST(&al);
+			memcpy(&$$.ss, &h->ss, sizeof($$.ss));
+			host_free(&al);
 		}
 		;
 
@@ -2143,37 +2189,55 @@ popfile(void)
 	return (file ? 0 : EOF);
 }
 
-struct relayd *
-parse_config(const char *filename, int opts)
+int
+parse_config(const char *filename, struct relayd *x_conf)
 {
 	struct sym	*sym, *next;
+
+	conf = x_conf;
+	if (config_init(conf) == -1) {
+		log_warn("%s: cannot initialize configuration", __func__);
+		return (-1);
+	}
+
+	errors = 0;
+
+	if ((file = pushfile(filename, 0)) == NULL)
+		return (-1);
+
+	topfile = file;
+	setservent(1);
+
+	yyparse();
+	errors = file->errors;
+	popfile();
+
+	endservent();
+	endprotoent();
+
+	/* Free macros */
+	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+		next = TAILQ_NEXT(sym, entry);
+		if (!sym->persist) {
+			free(sym->nam);
+			free(sym->val);
+			TAILQ_REMOVE(&symhead, sym, entry);
+			free(sym);
+		}
+	}
+
+	return (errors ? -1 : 0);
+}
+
+int
+load_config(const char *filename, struct relayd *x_conf)
+{
+	struct sym	*sym, *next;
 	struct table	*nexttb;
 	struct host	*h, *ph;
 
-	if ((conf = calloc(1, sizeof(*conf))) == NULL ||
-	    (conf->sc_tables = calloc(1, sizeof(*conf->sc_tables))) == NULL ||
-	    (conf->sc_relays = calloc(1, sizeof(*conf->sc_relays))) == NULL ||
-	    (conf->sc_protos = calloc(1, sizeof(*conf->sc_protos))) == NULL ||
-	    (conf->sc_routes = calloc(1, sizeof(*conf->sc_routes))) == NULL ||
-	    (conf->sc_rts = calloc(1, sizeof(*conf->sc_rts))) == NULL ||
-	    (conf->sc_rdrs = calloc(1, sizeof(*conf->sc_rdrs))) == NULL) {
-		if (conf != NULL) {
-			if (conf->sc_tables != NULL)
-				free(conf->sc_tables);
-			if (conf->sc_relays != NULL)
-				free(conf->sc_relays);
-			if (conf->sc_protos != NULL)
-				free(conf->sc_protos);
-			if (conf->sc_rdrs != NULL)
-				free(conf->sc_rdrs);
-			if (conf->sc_rts != NULL)
-				free(conf->sc_rts);
-			free(conf);
-		}
-		log_warn("%s: cannot allocate memory", __func__);
-		return (NULL);
-	}
-
+	conf = x_conf;
+	loadcfg = 1;
 	errors = 0;
 	last_host_id = last_table_id = last_rdr_id = last_proto_id =
 	    last_relay_id = last_rt_id = last_nr_id = 0;
@@ -2184,46 +2248,9 @@ parse_config(const char *filename, int opts)
 	proto = NULL;
 	router = NULL;
 
-	TAILQ_INIT(conf->sc_rdrs);
-	TAILQ_INIT(conf->sc_tables);
-	TAILQ_INIT(conf->sc_protos);
-	TAILQ_INIT(conf->sc_relays);
-	TAILQ_INIT(conf->sc_rts);
-	TAILQ_INIT(conf->sc_routes);
+	if ((file = pushfile(filename, 0)) == NULL)
+		return (-1);
 
-	memset(&conf->sc_empty_table, 0, sizeof(conf->sc_empty_table));
-	conf->sc_empty_table.conf.id = EMPTY_TABLE;
-	conf->sc_empty_table.conf.flags |= F_DISABLE;
-	(void)strlcpy(conf->sc_empty_table.conf.name, "empty",
-	    sizeof(conf->sc_empty_table.conf.name));
-
-	bzero(&conf->sc_proto_default, sizeof(conf->sc_proto_default));
-	conf->sc_proto_default.flags = F_USED;
-	conf->sc_proto_default.cache = RELAY_CACHESIZE;
-	conf->sc_proto_default.tcpflags = TCPFLAG_DEFAULT;
-	conf->sc_proto_default.tcpbacklog = RELAY_BACKLOG;
-	conf->sc_proto_default.sslflags = SSLFLAG_DEFAULT;
-	(void)strlcpy(conf->sc_proto_default.sslciphers, SSLCIPHERS_DEFAULT,
-	    sizeof(conf->sc_proto_default.sslciphers));
-	conf->sc_proto_default.type = RELAY_PROTO_TCP;
-	(void)strlcpy(conf->sc_proto_default.name, "default",
-	    sizeof(conf->sc_proto_default.name));
-	RB_INIT(&conf->sc_proto_default.request_tree);
-	RB_INIT(&conf->sc_proto_default.response_tree);
-
-	conf->sc_timeout.tv_sec = CHECK_TIMEOUT / 1000;
-	conf->sc_timeout.tv_usec = (CHECK_TIMEOUT % 1000) * 1000;
-	conf->sc_interval.tv_sec = CHECK_INTERVAL;
-	conf->sc_interval.tv_usec = 0;
-	conf->sc_prefork_relay = RELAY_NUMPROC;
-	conf->sc_statinterval.tv_sec = RELAY_STATINTERVAL;
-	conf->sc_opts = opts;
-	conf->sc_confpath = filename;
-
-	if ((file = pushfile(filename, 0)) == NULL) {
-		free(conf);
-		return (NULL);
-	}
 	topfile = file;
 	setservent(1);
 
@@ -2322,12 +2349,7 @@ parse_config(const char *filename, int opts)
 		}
 	}
 
-	if (errors) {
-		free(conf);
-		return (NULL);
-	}
-
-	return (conf);
+	return (errors ? -1 : 0);
 }
 
 int
@@ -2494,6 +2516,7 @@ host_dns(const char *s, struct addresslist *al, int ma
 				log_warnx("%s: interface name truncated",
 				    __func__);
 			freeaddrinfo(res0);
+			free(h);
 			return (-1);
 		}
 		if (ipproto != -1)
@@ -2615,6 +2638,7 @@ host(const char *s, struct addresslist *al, int max,
 			    sizeof(h->ifname)) {
 				log_warnx("%s: interface name truncated",
 				    __func__);
+				free(h);
 				return (-1);
 			}
 		}
@@ -2628,6 +2652,17 @@ host(const char *s, struct addresslist *al, int max,
 	return (host_dns(s, al, max, port, ifname, ipproto));
 }
 
+void
+host_free(struct addresslist *al)
+{
+	struct address	 *h;
+
+	while ((h = TAILQ_FIRST(al)) != NULL) {
+		TAILQ_REMOVE(al, h, entry);
+		free(h);
+	}
+}
+
 struct table *
 table_inherit(struct table *tb)
 {
@@ -2638,23 +2673,21 @@ table_inherit(struct table *tb)
 	/* Get the table or table template */
 	if ((dsttb = table_findbyname(conf, tb->conf.name)) == NULL) {
 		yyerror("unknown table %s", tb->conf.name);
-		purge_table(NULL, tb);
-		return (NULL);
+		goto fail;
 	}
 	if (dsttb->conf.port != 0)
 		fatal("invalid table");	/* should not happen */
 
 	if (tb->conf.port == 0) {
 		yyerror("invalid port");
-		purge_table(NULL, tb);
-		return (NULL);
+		goto fail;
 	}
 
 	/* Check if a matching table already exists */
 	if (snprintf(pname, sizeof(pname), "%s:%u",
 	    tb->conf.name, ntohs(tb->conf.port)) >= (int)sizeof(pname)) {
 		yyerror("invalid table name");
-		return (NULL);
+		goto fail;
 	}
 	(void)strlcpy(tb->conf.name, pname, sizeof(tb->conf.name));
 	if ((oldtb = table_findbyconf(conf, tb)) != NULL) {
@@ -2666,8 +2699,7 @@ table_inherit(struct table *tb)
 	tb->conf.id = ++last_table_id;
 	if (last_table_id == INT_MAX) {
 		yyerror("too many tables defined");
-		purge_table(NULL, tb);
-		return (NULL);
+		goto fail;
 	}
 	tb->conf.flags |= dsttb->conf.flags;
 
@@ -2684,8 +2716,7 @@ table_inherit(struct table *tb)
 		h->conf.id = ++last_host_id;
 		if (last_host_id == INT_MAX) {
 			yyerror("too many hosts defined");
-			purge_table(NULL, tb);
-			return (NULL);
+			goto fail;
 		}
 		h->conf.tableid = tb->conf.id;
 		h->tablename = tb->conf.name;
@@ -2697,6 +2728,10 @@ table_inherit(struct table *tb)
 	TAILQ_INSERT_TAIL(conf->sc_tables, tb, entry);
 
 	return (tb);
+
+ fail:
+	purge_table(NULL, tb);
+	return (NULL);
 }
 
 struct relay *
blob - ba3a6260390e37cf546a675d139e7ada36208f62
blob + 4ed78c9f88de32da2593422d4eb71a5f52719717
--- pfe.c
+++ pfe.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pfe.c,v 1.68 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: pfe.c,v 1.69 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -46,7 +46,7 @@ int	 pfe_dispatch_parent(int, struct privsep_proc *, s
 int	 pfe_dispatch_hce(int, struct privsep_proc *, struct imsg *);
 int	 pfe_dispatch_relay(int, struct privsep_proc *, struct imsg *);
 
-static struct relayd	*env = NULL;
+static struct relayd		*env = NULL;
 
 static struct privsep_proc procs[] = {
 	{ "parent",	PROC_PARENT,	pfe_dispatch_parent },
@@ -59,25 +59,23 @@ pfe(struct privsep *ps, struct privsep_proc *p)
 {
 	env = ps->ps_env;
 
-	init_filter(env);
-	init_tables(env);
-
 	return (proc_run(ps, p, procs, nitems(procs), pfe_init, NULL));
 }
 
 void
 pfe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
 {
+	if (config_init(ps->ps_env) == -1)
+		fatal("failed to initialize configuration");
+
 	p->p_shutdown = pfe_shutdown;
-	purge_config(env, PURGE_PROTOS);
-	pfe_setup_events();
-	pfe_sync();
 }
 
 void
 pfe_shutdown(void)
 {
 	flush_rulesets(env);
+	config_purge(env, CONFIG_ALL);
 }
 
 void
@@ -86,9 +84,11 @@ pfe_setup_events(void)
 	struct timeval	 tv;
 
 	/* Schedule statistics timer */
-	evtimer_set(&env->sc_statev, pfe_statistics, NULL);
-	bcopy(&env->sc_statinterval, &tv, sizeof(tv));
-	evtimer_add(&env->sc_statev, &tv);
+	if (!event_initialized(&env->sc_statev)) {
+		evtimer_set(&env->sc_statev, pfe_statistics, NULL);
+		bcopy(&env->sc_statinterval, &tv, sizeof(tv));
+		evtimer_add(&env->sc_statev, &tv);
+	}
 }
 
 void
@@ -172,88 +172,43 @@ pfe_dispatch_hce(int fd, struct privsep_proc *p, struc
 int
 pfe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	static struct rdr	*rdr = NULL;
-	static struct table	*table = NULL;
-	struct host		*host, *parent;
-	struct address		*virt;
-
 	switch (imsg->hdr.type) {
-	case IMSG_RECONF:
-		IMSG_SIZE_CHECK(imsg, env);
-
-		log_debug("%s: reloading configuration", __func__);
-
-		pfe_disable_events();
-		purge_config(env, PURGE_RDRS|PURGE_TABLES);
-		merge_config(env, (struct relayd *)imsg->data);
-
-		/*
-		 * no relays when reconfiguring yet.
-		 */
-		env->sc_relays = NULL;
-		env->sc_protos = NULL;
-
-		env->sc_tables = calloc(1, sizeof(*env->sc_tables));
-		env->sc_rdrs = calloc(1, sizeof(*env->sc_rdrs));
-		if (env->sc_tables == NULL || env->sc_rdrs == NULL)
-			fatal(NULL);
-
-		TAILQ_INIT(env->sc_tables);
-		TAILQ_INIT(env->sc_rdrs);
+	case IMSG_CFG_TABLE:
+		config_gettable(env, imsg);
 		break;
-	case IMSG_RECONF_TABLE:
-		if ((table = calloc(1, sizeof(*table))) == NULL)
-			fatal(NULL);
-		memcpy(&table->conf, imsg->data, sizeof(table->conf));
-		TAILQ_INIT(&table->hosts);
-		TAILQ_INSERT_TAIL(env->sc_tables, table, entry);
+	case IMSG_CFG_HOST:
+		config_gethost(env, imsg);
 		break;
-	case IMSG_RECONF_HOST:
-		if ((host = calloc(1, sizeof(*host))) == NULL)
-			fatal(NULL);
-		memcpy(&host->conf, imsg->data, sizeof(host->conf));
-		host->tablename = table->conf.name;
-		TAILQ_INSERT_TAIL(&table->hosts, host, entry);
-		if (host->conf.parentid) {
-			parent = host_find(env, host->conf.parentid);
-			SLIST_INSERT_HEAD(&parent->children,
-			    host, child);
-		}
+	case IMSG_CFG_RDR:
+		config_getrdr(env, imsg);
 		break;
-	case IMSG_RECONF_RDR:
-		if ((rdr = calloc(1, sizeof(*rdr))) == NULL)
-			fatal(NULL);
-		memcpy(&rdr->conf, imsg->data,
-		    sizeof(rdr->conf));
-		rdr->table = table_find(env,
-		     rdr->conf.table_id);
-		if (rdr->conf.backup_id == EMPTY_TABLE)
-			rdr->backup = &env->sc_empty_table;
-		else
-			rdr->backup = table_find(env,
-			    rdr->conf.backup_id);
-		if (rdr->table == NULL || rdr->backup == NULL)
-			fatal("pfe_dispatch_parent:"
-			    " corrupted configuration");
-		log_debug("%s: redirect table %s, backup %s",
-		    __func__,
-		    rdr->table->conf.name,
-		    rdr->backup->conf.name);
-		TAILQ_INIT(&rdr->virts);
-		TAILQ_INSERT_TAIL(env->sc_rdrs, rdr, entry);
+	case IMSG_CFG_VIRT:
+		config_getvirt(env, imsg);
 		break;
-	case IMSG_RECONF_VIRT:
-		if ((virt = calloc(1, sizeof(*virt))) == NULL)
-			fatal(NULL);
-		memcpy(virt, imsg->data, sizeof(*virt));
-		TAILQ_INSERT_TAIL(&rdr->virts, virt, entry);
+	case IMSG_CFG_ROUTER:
+		config_getrt(env, imsg);
 		break;
-	case IMSG_RECONF_END:
-		log_warnx("%s: configuration reloaded", __func__);
+	case IMSG_CFG_ROUTE:
+		config_getroute(env, imsg);
+		break;
+	case IMSG_CFG_PROTO:
+		config_getproto(env, imsg);
+		break;
+	case IMSG_CFG_PROTONODE:
+		break;
+	case IMSG_CFG_RELAY:
+		config_getrelay(env, imsg);
+		break;
+	case IMSG_CFG_DONE:
+		config_getcfg(env, imsg);
+		init_filter(env, imsg->fd);
 		init_tables(env);
 		pfe_setup_events();
 		pfe_sync();
 		break;
+	case IMSG_CTL_RESET:
+		config_getreset(env, imsg);
+		break;
 	default:
 		return (-1);
 	}
@@ -741,6 +696,9 @@ pfe_sync(void)
 	}
 
 	TAILQ_FOREACH(table, env->sc_tables, entry) {
+		if (table->conf.check == CHECK_NOCHECK)
+			continue;
+
 		/*
 		 * clean up change flag.
 		 */
blob - 2fa53cdd3b857ca53161a8fcaf9b101a85bfc990
blob + 02a528999b5e988b1da6c4ec649bf60c5adc88ef
--- pfe_filter.c
+++ pfe_filter.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pfe_filter.c,v 1.46 2011/05/05 12:01:44 reyk Exp $	*/
+/*	$OpenBSD: pfe_filter.c,v 1.47 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <errno.h>
 
 #include <openssl/ssl.h>
@@ -53,17 +54,21 @@ void	 kill_tables(struct relayd *);
 int	 kill_srcnodes(struct relayd *, struct table *);
 
 void
-init_filter(struct relayd *env)
+init_filter(struct relayd *env, int s)
 {
 	struct pf_status	status;
 
 	if (!(env->sc_flags & F_NEEDPF))
 		return;
 
-	if ((env->sc_pf = calloc(1, sizeof(*(env->sc_pf)))) == NULL)
-		fatal("calloc");
-	if ((env->sc_pf->dev = open(PF_SOCKET, O_RDWR)) == -1)
-		fatal("init_filter: cannot open pf socket");
+	if (s == -1)
+		fatalx("init_filter: invalid socket");
+	if (env->sc_pf == NULL) {
+		if ((env->sc_pf = calloc(1, sizeof(*(env->sc_pf)))) == NULL)
+			fatal("calloc");
+	} else
+		close(env->sc_pf->dev);
+	env->sc_pf->dev = s;
 	if (ioctl(env->sc_pf->dev, DIOCGETSTATUS, &status) == -1)
 		fatal("init_filter: DIOCGETSTATUS");
 	if (!status.running)
@@ -366,7 +371,7 @@ sync_ruleset(struct relayd *env, struct rdr *rdr, int 
 	char			 anchor[PF_ANCHOR_NAME_SIZE];
 	struct table		*t = rdr->table;
 
-	if (!(env->sc_flags & F_NEEDPF))
+	if ((env->sc_flags & F_NEEDPF) == 0)
 		return;
 
 	bzero(anchor, sizeof(anchor));
blob - 0fae0dfd0a065c2b10d47febb03cf2687fb0725c
blob + e0b5066eb404806d8e76813345133c75f60ff43c
--- pfe_route.c
+++ pfe_route.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pfe_route.c,v 1.4 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: pfe_route.c,v 1.5 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2009 Reyk Floeter <reyk@openbsd.org>
@@ -96,9 +96,10 @@ sync_routes(struct relayd *env, struct router *rt)
 			    HOST_ISUP(host->up) ? "up" : "down",
 			    host->conf.priority);
 
-			crt.id = nr->nr_conf.id;
-			crt.hostid = host->conf.id;
 			crt.up = host->up;
+			memcpy(&crt.nr, &nr->nr_conf, sizeof(nr->nr_conf));
+			memcpy(&crt.host, &host->conf, sizeof(host->conf));
+			memcpy(&crt.rt, &rt->rt_conf, sizeof(rt->rt_conf));
 
 			proc_compose_imsg(env->sc_ps, PROC_PARENT, -1,
 			    IMSG_RTMSG, -1, &crt, sizeof(crt));
@@ -115,20 +116,12 @@ pfe_route(struct relayd *env, struct ctl_netroute *crt
 	struct sockaddr_in		*s4;
 	struct sockaddr_in6		*s6;
 	size_t				 len = 0;
-	struct netroute			 *nr;
-	struct host			*host;
 	char				*gwname;
 	int				 i = 0;
 
-	if ((nr = route_find(env, crt->id)) == NULL ||
-	    (host = host_find(env, crt->hostid)) == NULL) {
-		log_debug("%s: invalid host or route id", __func__);
-		return (-1);
-	}
+	gw = &crt->host.ss;
+	gwname = crt->host.name;
 
-	gw = &host->conf.ss;
-	gwname = host->conf.name;
-
 	bzero(&rm, sizeof(rm));
 	bzero(&sr, sizeof(sr));
 
@@ -138,18 +131,18 @@ pfe_route(struct relayd *env, struct ctl_netroute *crt
 	rm.rm_hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY | RTF_MPATH;
 	rm.rm_hdr.rtm_seq = env->sc_rtseq++;
 	rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
-	rm.rm_hdr.rtm_tableid = nr->nr_router->rt_conf.rtable;
-	rm.rm_hdr.rtm_priority = host->conf.priority;
+	rm.rm_hdr.rtm_tableid = crt->rt.rtable;
+	rm.rm_hdr.rtm_priority = crt->host.priority;
 
-	if (strlen(nr->nr_router->rt_conf.label)) {
+	if (strlen(crt->rt.label)) {
 		rm.rm_hdr.rtm_addrs |= RTA_LABEL;
 		sr.sr_len = sizeof(sr);
 		if (snprintf(sr.sr_label, sizeof(sr.sr_label),
-		    "%s", nr->nr_router->rt_conf.label) == -1)
+		    "%s", crt->rt.label) == -1)
 			goto bad;
 	}
 
-	if (nr->nr_conf.ss.ss_family == AF_INET) {
+	if (crt->nr.ss.ss_family == AF_INET) {
 		rm.rm_hdr.rtm_msglen = len =
 		    sizeof(rm.rm_hdr) + sizeof(rm.rm_u.u4);
 
@@ -159,7 +152,7 @@ pfe_route(struct relayd *env, struct ctl_netroute *crt
 		s4->sin_family = AF_INET;
 		s4->sin_len = sizeof(rm.rm_u.u4.rm_dst);
 		s4->sin_addr.s_addr =
-		    ((struct sockaddr_in *)&nr->nr_conf.ss)->sin_addr.s_addr;
+		    ((struct sockaddr_in *)&crt->nr.ss)->sin_addr.s_addr;
 
 		s4 = &rm.rm_u.u4.rm_gateway;
 		s4->sin_family = AF_INET;
@@ -171,19 +164,19 @@ pfe_route(struct relayd *env, struct ctl_netroute *crt
 		s4 = &rm.rm_u.u4.rm_netmask;
 		s4->sin_family = AF_INET;
 		s4->sin_len = sizeof(rm.rm_u.u4.rm_netmask);
-		if (nr->nr_conf.prefixlen)
+		if (crt->nr.prefixlen)
 			s4->sin_addr.s_addr =
-			    htonl(0xffffffff << (32 - nr->nr_conf.prefixlen));
-		else if (nr->nr_conf.prefixlen < 0)
+			    htonl(0xffffffff << (32 - crt->nr.prefixlen));
+		else if (crt->nr.prefixlen < 0)
 			rm.rm_hdr.rtm_flags |= RTF_HOST;
-	} else if (nr->nr_conf.ss.ss_family == AF_INET6) {
+	} else if (crt->nr.ss.ss_family == AF_INET6) {
 		rm.rm_hdr.rtm_msglen = len =
 		    sizeof(rm.rm_hdr) + sizeof(rm.rm_u.u6);
 
 		bcopy(&sr, &rm.rm_u.u6.rm_label, sizeof(sr));
 
 		s6 = &rm.rm_u.u6.rm_dst;
-		bcopy(((struct sockaddr_in6 *)&nr->nr_conf.ss),
+		bcopy(((struct sockaddr_in6 *)&crt->nr.ss),
 		    s6, sizeof(*s6));
 		s6->sin6_family = AF_INET6;
 		s6->sin6_len = sizeof(*s6);
@@ -197,14 +190,14 @@ pfe_route(struct relayd *env, struct ctl_netroute *crt
 		s6 = &rm.rm_u.u6.rm_netmask;
 		s6->sin6_family = AF_INET6;
 		s6->sin6_len = sizeof(*s6);
-		if (nr->nr_conf.prefixlen) {
-			for (i = 0; i < nr->nr_conf.prefixlen / 8; i++)
+		if (crt->nr.prefixlen) {
+			for (i = 0; i < crt->nr.prefixlen / 8; i++)
 				s6->sin6_addr.s6_addr[i] = 0xff;
-			i = nr->nr_conf.prefixlen % 8;
+			i = crt->nr.prefixlen % 8;
 			if (i)
-				s6->sin6_addr.s6_addr[nr->nr_conf.prefixlen
+				s6->sin6_addr.s6_addr[crt->nr.prefixlen
 				    / 8] = 0xff00 >> i;
-		} else if (nr->nr_conf.prefixlen < 0)
+		} else if (crt->nr.prefixlen < 0)
 			rm.rm_hdr.rtm_flags |= RTF_HOST;
 	} else
 		fatal("pfe_route: invalid address family");
blob - 2bfb76a85beb75a49796af5891d0b49ebe88a227
blob + 7a0ddf0512fc590281db3e5b352552a7d1c06660
--- proc.c
+++ proc.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: proc.c,v 1.1 2011/05/09 12:09:58 reyk Exp $	*/
+/*	$OpenBSD: proc.c,v 1.2 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2010,2011 Reyk Floeter <reyk@openbsd.org>
@@ -105,6 +105,8 @@ proc_kill(struct privsep *ps)
 	do {
 		pid = waitpid(WAIT_ANY, NULL, 0);
 	} while (pid != -1 || (pid == -1 && errno == EINTR));
+
+	proc_clear(ps, 1);
 }
 
 void
@@ -134,6 +136,8 @@ proc_setup(struct privsep *ps)
 				fatal(NULL);
 
 			for (n = 0; n < count; n++) {
+				if (ps->ps_noaction)
+					continue;
 				if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
 				    sockpair) == -1)
 					fatal("socketpair");
@@ -175,7 +179,8 @@ proc_config(struct privsep *ps, struct privsep_proc *p
 				    ps->ps_instance == instance)
 					continue;
 
-				close(ps->ps_pipes[i][j][n]);
+				if (!ps->ps_noaction)
+					close(ps->ps_pipes[i][j][n]);
 				ps->ps_pipes[i][j][n] = -1;
 			}
 		}
@@ -221,6 +226,35 @@ proc_config(struct privsep *ps, struct privsep_proc *p
 }
 
 void
+proc_clear(struct privsep *ps, int purge)
+{
+	u_int	 src = privsep_process, dst, n, count;
+
+	if (ps == NULL)
+		return;
+
+	for (dst = 0; dst < PROC_MAX; dst++) {
+		if (src == dst || ps->ps_ievs[dst] == NULL)
+			continue;
+
+		count = ps->ps_instances[src] * ps->ps_instances[dst];
+
+		for (n = 0; n < count; n++) {
+			if (ps->ps_pipes[src][dst][n] == -1)
+				continue;
+			if (purge) {
+				event_del(&(ps->ps_ievs[dst][n].ev));
+				imsg_clear(&(ps->ps_ievs[dst][n].ibuf));
+				close(ps->ps_pipes[src][dst][n]);
+			} else
+				imsg_flush(&(ps->ps_ievs[dst][n].ibuf));
+		}
+		if (purge)
+			free(ps->ps_ievs[dst]);
+	}
+}
+
+void
 proc_shutdown(struct privsep_proc *p)
 {
 	struct privsep	*ps = p->p_ps;
@@ -231,6 +265,8 @@ proc_shutdown(struct privsep_proc *p)
 	if (p->p_shutdown != NULL)
 		(*p->p_shutdown)();
 
+	proc_clear(ps, 1);
+
 	log_info("%s exiting, pid %d", p->p_title, getpid());
 
 	_exit(0);
@@ -268,6 +304,9 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
 	u_int32_t	 seed[256];
 	u_int		 n;
 
+	if (ps->ps_noaction)
+		return (0);
+
 	switch (pid = fork()) {
 	case -1:
 		fatal("run_proc: cannot fork");
@@ -403,7 +442,7 @@ proc_dispatch(int fd, short event, void *arg)
 		if (n == 0)
 			break;
 
-#ifdef DEBUG
+#if DEBUG > 1
 		log_debug("%s: %s %d got imsg %d from %s %d",
 		    __func__, title, ps->ps_instance + 1,
 		    imsg.hdr.type, p->p_title, p->p_instance);
@@ -522,6 +561,7 @@ proc_compose_imsg(struct privsep *ps, enum privsep_pro
 		if (imsg_compose_event(&ps->ps_ievs[id][n],
 		    type, -1, 0, fd, data, datalen) == -1)
 			return (-1);
+
 	return (0);
 }
 
blob - d458a4de7a6ef21a6b30c053be1ee56ce854485c
blob + 00d50e93a797ff994f8e75f64c8afebeb7f4f67d
--- relay.c
+++ relay.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relay.c,v 1.136 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: relay.c,v 1.137 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -54,7 +54,6 @@ int		 relay_dispatch_pfe(int, struct privsep_proc *,
 		    struct imsg *);
 void		 relay_shutdown(void);
 
-void		 relay_privinit(void);
 void		 relay_nodedebug(const char *, struct protonode *);
 void		 relay_protodebug(struct relay *);
 void		 relay_init(struct privsep *, struct privsep_proc *p, void *);
@@ -146,26 +145,13 @@ pid_t
 relay(struct privsep *ps, struct privsep_proc *p)
 {
 	env = ps->ps_env;
-
-	/* Need root privileges for relay initialization */
-	relay_privinit();
-
 	return (proc_run(ps, p, procs, nitems(procs), relay_init, NULL));
 }
 
 void
 relay_shutdown(void)
 {
-	struct rsession	*con;
-
-	struct relay	*rlay;
-	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
-		if (rlay->rl_conf.flags & F_DISABLE)
-			continue;
-		close(rlay->rl_s);
-		while ((con = SPLAY_ROOT(&rlay->rl_sessions)) != NULL)
-			relay_close(con, "shutdown");
-	}
+	config_purge(env, CONFIG_ALL);
 	usleep(200);	/* XXX relay needs to shutdown last */
 }
 
@@ -250,7 +236,8 @@ relay_protodebug(struct relay *rlay)
 	const char		*name;
 	int			 i;
 
-	fprintf(stderr, "protocol %d: name %s\n", proto->id, proto->name);
+	fprintf(stderr, "protocol %d: name %s\n",
+	    proto->id, proto->name);
 	fprintf(stderr, "\tflags: %s, relay flags: %s\n",
 	    printb_flags(proto->flags, F_BITS),
 	    printb_flags(rlay->rl_conf.flags, F_BITS));
@@ -304,106 +291,56 @@ relay_protodebug(struct relay *rlay)
 	}
 }
 
-void
-relay_privinit(void)
+int
+relay_privinit(struct relay *rlay)
 {
-	struct relay	*rlay;
 	extern int	 debug;
 
-	if (env->sc_flags & (F_SSL|F_SSLCLIENT))
-		ssl_init(env);
+	log_debug("%s: adding relay %s", __func__, rlay->rl_conf.name);
 
-	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
-		log_debug("%s: adding relay %s", __func__, rlay->rl_conf.name);
+	if (debug)
+		relay_protodebug(rlay);
 
-		if (debug)
-			relay_protodebug(rlay);
-
-		switch (rlay->rl_proto->type) {
-		case RELAY_PROTO_DNS:
-			relay_udp_privinit(env, rlay);
-			break;
-		case RELAY_PROTO_TCP:
-		case RELAY_PROTO_HTTP:
-			/* Use defaults */
-			break;
-		}
-
-		if (rlay->rl_conf.flags & F_UDP)
-			rlay->rl_s = relay_udp_bind(&rlay->rl_conf.ss,
-			    rlay->rl_conf.port, rlay->rl_proto);
-		else
-			rlay->rl_s = relay_socket_listen(&rlay->rl_conf.ss,
-			    rlay->rl_conf.port, rlay->rl_proto);
-		if (rlay->rl_s == -1)
-			fatal("relay_privinit: failed to listen");
+	switch (rlay->rl_proto->type) {
+	case RELAY_PROTO_DNS:
+		relay_udp_privinit(env, rlay);
+		break;
+	case RELAY_PROTO_TCP:
+	case RELAY_PROTO_HTTP:
+		/* Use defaults */
+		break;
 	}
+
+	if (rlay->rl_conf.flags & F_UDP)
+		rlay->rl_s = relay_udp_bind(&rlay->rl_conf.ss,
+		    rlay->rl_conf.port, rlay->rl_proto);
+	else
+		rlay->rl_s = relay_socket_listen(&rlay->rl_conf.ss,
+		    rlay->rl_conf.port, rlay->rl_proto);
+	if (rlay->rl_s == -1)
+		return (-1);
+
+	return (0);
 }
 
 void
 relay_init(struct privsep *ps, struct privsep_proc *p, void *arg)
 {
-	struct relay	*rlay;
-	struct host	*host;
 	struct timeval	 tv;
 
+	if (config_init(ps->ps_env) == -1)
+		fatal("failed to initialize configuration");
+
 	/* We use a custom shutdown callback */
 	p->p_shutdown = relay_shutdown;
 
-	purge_config(env, PURGE_RDRS);
-
 	/* Unlimited file descriptors (use system limits) */
 	socket_rlimit(-1);
 
-	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
-		if ((rlay->rl_conf.flags & (F_SSL|F_SSLCLIENT)) &&
-		    (rlay->rl_ssl_ctx = relay_ssl_ctx_create(rlay)) == NULL)
-			fatal("relay_init: failed to create SSL context");
-
-		if (rlay->rl_dsttable != NULL) {
-			switch (rlay->rl_conf.dstmode) {
-			case RELAY_DSTMODE_ROUNDROBIN:
-				rlay->rl_dstkey = 0;
-				break;
-			case RELAY_DSTMODE_LOADBALANCE:
-			case RELAY_DSTMODE_HASH:
-				rlay->rl_dstkey =
-				    hash32_str(rlay->rl_conf.name, HASHINIT);
-				rlay->rl_dstkey =
-				    hash32_str(rlay->rl_dsttable->conf.name,
-				    rlay->rl_dstkey);
-				break;
-			}
-			rlay->rl_dstnhosts = 0;
-			TAILQ_FOREACH(host, &rlay->rl_dsttable->hosts, entry) {
-				if (rlay->rl_dstnhosts >= RELAY_MAXHOSTS)
-					fatal("relay_init: "
-					    "too many hosts in table");
-				host->idx = rlay->rl_dstnhosts;
-				rlay->rl_dsthost[rlay->rl_dstnhosts++] = host;
-			}
-			log_info("adding %d hosts from table %s%s",
-			    rlay->rl_dstnhosts, rlay->rl_dsttable->conf.name,
-			    rlay->rl_dsttable->conf.check ? "" : " (no check)");
-		}
-
-		switch (rlay->rl_proto->type) {
-		case RELAY_PROTO_DNS:
-			relay_udp_init(rlay);
-			break;
-		case RELAY_PROTO_TCP:
-		case RELAY_PROTO_HTTP:
-			/* Use defaults */
-			break;
-		}
-	}
-
 	/* Schedule statistics timer */
 	evtimer_set(&env->sc_statev, relay_statistics, NULL);
 	bcopy(&env->sc_statinterval, &tv, sizeof(tv));
 	evtimer_add(&env->sc_statev, &tv);
-
-	relay_launch();
 }
 
 void
@@ -474,10 +411,52 @@ relay_statistics(int fd, short events, void *arg)
 void
 relay_launch(void)
 {
-	struct relay	*rlay;
 	void		(*callback)(int, short, void *);
+	struct relay	*rlay;
+	struct host	*host;
 
 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
+		if ((rlay->rl_conf.flags & (F_SSL|F_SSLCLIENT)) &&
+		    (rlay->rl_ssl_ctx = relay_ssl_ctx_create(rlay)) == NULL)
+			fatal("relay_init: failed to create SSL context");
+
+		if (rlay->rl_dsttable != NULL) {
+			switch (rlay->rl_conf.dstmode) {
+			case RELAY_DSTMODE_ROUNDROBIN:
+				rlay->rl_dstkey = 0;
+				break;
+			case RELAY_DSTMODE_LOADBALANCE:
+			case RELAY_DSTMODE_HASH:
+				rlay->rl_dstkey =
+				    hash32_str(rlay->rl_conf.name, HASHINIT);
+				rlay->rl_dstkey =
+				    hash32_str(rlay->rl_dsttable->conf.name,
+				    rlay->rl_dstkey);
+				break;
+			}
+			rlay->rl_dstnhosts = 0;
+			TAILQ_FOREACH(host, &rlay->rl_dsttable->hosts, entry) {
+				if (rlay->rl_dstnhosts >= RELAY_MAXHOSTS)
+					fatal("relay_init: "
+					    "too many hosts in table");
+				host->idx = rlay->rl_dstnhosts;
+				rlay->rl_dsthost[rlay->rl_dstnhosts++] = host;
+			}
+			log_info("adding %d hosts from table %s%s",
+			    rlay->rl_dstnhosts, rlay->rl_dsttable->conf.name,
+			    rlay->rl_dsttable->conf.check ? "" : " (no check)");
+		}
+
+		switch (rlay->rl_proto->type) {
+		case RELAY_PROTO_DNS:
+			relay_udp_init(rlay);
+			break;
+		case RELAY_PROTO_TCP:
+		case RELAY_PROTO_HTTP:
+			/* Use defaults */
+			break;
+		}
+
 		log_debug("%s: running relay %s", __func__,
 		    rlay->rl_conf.name);
 
@@ -2320,7 +2299,7 @@ relay_close(struct rsession *con, const char *msg)
 	if (con->se_out.bev != NULL)
 		bufferevent_disable(con->se_out.bev, EV_READ|EV_WRITE);
 
-	if (env->sc_opts & RELAYD_OPT_LOGUPDATE) {
+	if ((env->sc_opts & RELAYD_OPT_LOGUPDATE) && msg != NULL) {
 		bzero(&ibuf, sizeof(ibuf));
 		bzero(&obuf, sizeof(obuf));
 		(void)print_host(&con->se_in.ss, ibuf, sizeof(ibuf));
@@ -2531,6 +2510,27 @@ relay_dispatch_parent(int fd, struct privsep_proc *p, 
 		bzero(&tv, sizeof(tv));
 		evtimer_add(&con->se_ev, &tv);
 		break;
+	case IMSG_CFG_TABLE:
+		config_gettable(env, imsg);
+		break;
+	case IMSG_CFG_HOST:
+		config_gethost(env, imsg);
+		break;
+	case IMSG_CFG_PROTO:
+		config_getproto(env, imsg);
+		break;
+	case IMSG_CFG_PROTONODE:
+		return (config_getprotonode(env, imsg));
+	case IMSG_CFG_RELAY:
+		config_getrelay(env, imsg);
+		break;
+	case IMSG_CFG_DONE:
+		config_getcfg(env, imsg);
+		relay_launch();
+		break;
+	case IMSG_CTL_RESET:
+		config_getreset(env, imsg);
+		break;
 	default:
 		return (-1);
 	}
@@ -2578,7 +2578,7 @@ relay_ssl_ctx_create(struct relay *rlay)
 	if ((rlay->rl_conf.flags & F_SSLCLIENT) &&
 	    (rlay->rl_ssl_ca != NULL)) {
 		if (!ssl_ctx_load_verify_memory(ctx,
-		    rlay->rl_ssl_ca, rlay->rl_ssl_ca_len))
+		    rlay->rl_ssl_ca, rlay->rl_conf.ssl_ca_len))
 			goto err;
 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
 	}
@@ -2588,12 +2588,12 @@ relay_ssl_ctx_create(struct relay *rlay)
 
 	log_debug("%s: loading certificate", __func__);
 	if (!ssl_ctx_use_certificate_chain(ctx,
-	    rlay->rl_ssl_cert, rlay->rl_ssl_cert_len))
+	    rlay->rl_ssl_cert, rlay->rl_conf.ssl_cert_len))
 		goto err;
 
 	log_debug("%s: loading private key", __func__);
 	if (!ssl_ctx_use_private_key(ctx, rlay->rl_ssl_key,
-	    rlay->rl_ssl_key_len))
+	    rlay->rl_conf.ssl_key_len))
 		goto err;
 	if (!SSL_CTX_check_private_key(ctx))
 		goto err;
@@ -3070,7 +3070,7 @@ relay_load_file(const char *name, off_t *len)
 
 	close(fd);
 
-	*len = size + 1;
+	*len = size;
 	return (buf);
 
  fail:
@@ -3087,9 +3087,9 @@ relay_load_certfiles(struct relay *rlay)
 	char	 certfile[PATH_MAX];
 	char	 hbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
 
-	if ((rlay->rl_conf.flags & F_SSLCLIENT) && (proto->sslca != NULL)) {
+	if ((rlay->rl_conf.flags & F_SSLCLIENT) && strlen(proto->sslca)) {
 		if ((rlay->rl_ssl_ca = relay_load_file(proto->sslca,
-		    &rlay->rl_ssl_ca_len)) == NULL)
+		    &rlay->rl_conf.ssl_ca_len)) == NULL)
 			return (-1);
 		log_debug("%s: using ca %s", __func__, proto->sslca);
 	}
@@ -3104,7 +3104,7 @@ relay_load_certfiles(struct relay *rlay)
 	    "/etc/ssl/%s.crt", hbuf) == -1)
 		return (-1);
 	if ((rlay->rl_ssl_cert = relay_load_file(certfile,
-	    &rlay->rl_ssl_cert_len)) == NULL)
+	    &rlay->rl_conf.ssl_cert_len)) == NULL)
 		return (-1);
 	log_debug("%s: using certificate %s", __func__, certfile);
 
@@ -3112,7 +3112,7 @@ relay_load_certfiles(struct relay *rlay)
 	    "/etc/ssl/private/%s.key", hbuf) == -1)
 		return -1;
 	if ((rlay->rl_ssl_key = relay_load_file(certfile,
-	    &rlay->rl_ssl_key_len)) == NULL)
+	    &rlay->rl_conf.ssl_key_len)) == NULL)
 		return (-1);
 	log_debug("%s: using private key %s", __func__, certfile);
 
blob - f3a70f57f497e7743103bdba779d769df0319533
blob + 8bc215646f53ca390be3ad305a6ffa82b6628613
--- relayd.c
+++ relayd.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relayd.c,v 1.102 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: relayd.c,v 1.103 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -22,6 +22,7 @@
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <sys/resource.h>
+#include <sys/hash.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
@@ -30,6 +31,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <err.h>
 #include <errno.h>
@@ -46,16 +48,16 @@
 
 __dead void	 usage(void);
 
+int		 parent_configure(struct relayd *);
+void		 parent_reload(struct relayd *, u_int, const char *);
 void		 parent_sig_handler(int, short, void *);
 void		 parent_shutdown(struct relayd *);
 int		 parent_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
 int		 parent_dispatch_hce(int, struct privsep_proc *, struct imsg *);
 int		 parent_dispatch_relay(int, struct privsep_proc *, struct imsg *);
-void		 reconfigure(void);
-void		 purge_tree(struct proto_tree *);
 int		 bindany(struct ctl_bindany *);
 
-struct relayd	*relayd_env;
+struct relayd			*relayd_env;
 
 static struct privsep_proc procs[] = {
 	{ "pfe",	PROC_PFE, parent_dispatch_pfe, pfe },
@@ -112,7 +114,13 @@ parent_sig_handler(int sig, short event, void *arg)
 			parent_shutdown(ps->ps_env);
 		break;
 	case SIGHUP:
-		reconfigure();
+		log_info("%s: reload requested with SIGHUP", __func__);
+
+		/*
+		 * This is safe because libevent uses async signal handlers
+		 * that run in the event loop and not in signal context.
+		 */
+		parent_reload(ps->ps_env, CONFIG_RELOAD, NULL);
 		break;
 	case SIGPIPE:
 		/* ignore */
@@ -137,16 +145,12 @@ int
 main(int argc, char *argv[])
 {
 	int			 c;
-	int			 debug;
-	u_int32_t		 opts;
+	int			 debug = 0, verbose = 0;
+	u_int32_t		 opts = 0;
 	struct relayd		*env;
 	struct privsep		*ps;
-	const char		*conffile;
+	const char		*conffile = CONF_FILE;
 
-	opts = 0;
-	debug = 0;
-	conffile = CONF_FILE;
-
 	while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
 		switch (c) {
 		case 'd':
@@ -165,6 +169,7 @@ main(int argc, char *argv[])
 			conffile = optarg;
 			break;
 		case 'v':
+			verbose++;
 			opts |= RELAYD_OPT_VERBOSE;
 			break;
 		default:
@@ -179,18 +184,19 @@ main(int argc, char *argv[])
 	if (argc > 0)
 		usage();
 
-	if ((env = parse_config(conffile, opts)) == NULL ||
+	if ((env = calloc(1, sizeof(*env))) == NULL ||
 	    (ps = calloc(1, sizeof(*ps))) == NULL)
 		exit(1);
 
 	relayd_env = env;
 	env->sc_ps = ps;
 	ps->ps_env = env;
+	env->sc_conffile = conffile;
+	env->sc_opts = opts;
 
-	if (env->sc_opts & RELAYD_OPT_NOACTION) {
-		fprintf(stderr, "configuration OK\n");
-		exit(0);
-	}
+	if (parse_config(env->sc_conffile, env) == -1)
+		exit(1);
+
 	if (debug)
 		env->sc_opts |= RELAYD_OPT_LOGUPDATE;
 
@@ -203,14 +209,17 @@ main(int argc, char *argv[])
 	/* Configure the control socket */
 	ps->ps_csock.cs_name = RELAYD_SOCKET;
 
-	if (!debug) {
-		log_init(debug);
-		if (daemon(1, 0) == -1)
-			err(1, "failed to daemonize");
-	}
+	log_init(debug);
+	log_verbose(verbose);
 
-	log_info("startup");
+	if (!debug && daemon(1, 0) == -1)
+		err(1, "failed to daemonize");
 
+	if (env->sc_opts & RELAYD_OPT_NOACTION)
+		ps->ps_noaction = 1;
+	else
+		log_info("startup");
+
 	ps->ps_instances[PROC_RELAY] = env->sc_prefork_relay;
 	proc_init(ps, procs, nitems(procs));
 
@@ -231,18 +240,123 @@ main(int argc, char *argv[])
 	signal_add(&ps->ps_evsigpipe, NULL);
 
 	proc_config(ps, procs, nitems(procs));
+
+	if (load_config(env->sc_conffile, env) == -1) {
+		proc_kill(env->sc_ps);
+		exit(1);
+	}
+
+	if (env->sc_opts & RELAYD_OPT_NOACTION) {
+		fprintf(stderr, "configuration OK\n");
+		proc_kill(env->sc_ps);
+		exit(0);
+	}
+
+	if (env->sc_flags & (F_SSL|F_SSLCLIENT))
+		ssl_init(env);
+
+	if (parent_configure(env) == -1)
+		fatalx("configuration failed");
+
 	init_routes(env);
 
 	event_dispatch();
 
 	parent_shutdown(env);
 	/* NOTREACHED */
+
 	return (0);
 }
 
+int
+parent_configure(struct relayd *env)
+{
+	struct table		*tb;
+	struct rdr		*rdr;
+	struct router		*rt;
+	struct protocol		*proto;
+	struct relay		*rlay;
+	int			 id;
+	struct ctl_flags	 cf;
+	int			 s, ret = -1;
+
+	TAILQ_FOREACH(tb, env->sc_tables, entry)
+		config_settable(env, tb);
+	TAILQ_FOREACH(rdr, env->sc_rdrs, entry)
+		config_setrdr(env, rdr);
+	TAILQ_FOREACH(rt, env->sc_rts, rt_entry)
+		config_setrt(env, rt);
+	TAILQ_FOREACH(proto, env->sc_protos, entry)
+		config_setproto(env, proto);
+	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry)
+		config_setrelay(env, rlay);
+
+	for (id = 0; id < PROC_MAX; id++) {
+		if (id == privsep_process)
+			continue;
+		cf.cf_opts = env->sc_opts;
+		cf.cf_flags = env->sc_flags;
+
+		if ((env->sc_flags & F_NEEDPF) && id == PROC_PFE) {
+			/* Send pf socket to the pf engine */
+			if ((s = open(PF_SOCKET, O_RDWR)) == -1) {
+				log_debug("%s: cannot open pf socket",
+				    __func__);
+				goto done;
+			}
+		} else
+			s = -1;
+
+		env->sc_reload++;
+		proc_compose_imsg(env->sc_ps, id, -1, IMSG_CFG_DONE, s,
+		    &cf, sizeof(cf));
+	}
+
+	ret = 0;
+
+ done:
+	config_purge(env, CONFIG_ALL);
+	return (ret);
+}
+
 void
+parent_reload(struct relayd *env, u_int reset, const char *filename)
+{
+	if (env->sc_reload) {
+		log_debug("%s: already in progress: %d pending",
+		    __func__, env->sc_reload);
+		return;
+	}
+
+	/* Switch back to the default config file */
+	if (filename == NULL || *filename == '\0')
+		filename = env->sc_conffile;
+
+	log_debug("%s: level %d config file %s", __func__, reset, filename);
+
+	config_purge(env, CONFIG_ALL);
+
+	if (reset == CONFIG_RELOAD) {
+		if (load_config(filename, env) == -1) {
+			log_debug("%s: failed to load config file %s",
+			    __func__, filename);
+		}
+
+		config_setreset(env, CONFIG_ALL);
+
+		if (parent_configure(env) == -1) {
+			log_debug("%s: failed to commit config from %s",
+			    __func__, filename);
+		}
+	} else
+		config_setreset(env, reset);
+}
+
+void
 parent_shutdown(struct relayd *env)
 {
+	config_purge(env, CONFIG_ALL);
+
 	proc_kill(env->sc_ps);
 	control_cleanup(&env->sc_ps->ps_csock);
 	carp_demote_shutdown();
@@ -257,223 +371,14 @@ parent_shutdown(struct relayd *env)
 	exit(0);
 }
 
-void
-merge_config(struct relayd *env, struct relayd *new_env)
-{
-	env->sc_opts = new_env->sc_opts;
-	env->sc_flags = new_env->sc_flags;
-	env->sc_confpath = new_env->sc_confpath;
-	env->sc_tablecount = new_env->sc_tablecount;
-	env->sc_rdrcount = new_env->sc_rdrcount;
-	env->sc_protocount = new_env->sc_protocount;
-	env->sc_relaycount = new_env->sc_relaycount;
-
-	memcpy(&env->sc_interval, &new_env->sc_interval,
-	    sizeof(env->sc_interval));
-	memcpy(&env->sc_timeout, &new_env->sc_timeout,
-	    sizeof(env->sc_timeout));
-	memcpy(&env->sc_empty_table, &new_env->sc_empty_table,
-	    sizeof(env->sc_empty_table));
-	memcpy(&env->sc_proto_default, &new_env->sc_proto_default,
-	    sizeof(env->sc_proto_default));
-	env->sc_prefork_relay = new_env->sc_prefork_relay;
-	(void)strlcpy(env->sc_demote_group, new_env->sc_demote_group,
-	    sizeof(env->sc_demote_group));
-
-	env->sc_tables = new_env->sc_tables;
-	env->sc_rdrs = new_env->sc_rdrs;
-	env->sc_relays = new_env->sc_relays;
-	env->sc_protos = new_env->sc_protos;
-}
-
-
-void
-reconfigure(void)
-{
-	struct relayd		*env = relayd_env;
-	struct relayd		*new_env = NULL;
-	struct privsep		*ps = env->sc_ps;
-	struct rdr		*rdr;
-	struct address		*virt;
-	struct table            *table;
-	struct host             *host;
-
-	log_info("reloading configuration");
-	if ((new_env = parse_config(env->sc_confpath, env->sc_opts)) == NULL) {
-		log_warnx("configuration reloading FAILED");
-		return;
-	}
-
-	if (!(env->sc_flags & F_NEEDPF) && (new_env->sc_flags & F_NEEDPF)) {
-		log_warnx("new configuration requires pf while it "
-		    "was previously disabled."
-		    "configuration will not be reloaded");
-		purge_config(new_env, PURGE_EVERYTHING);
-		free(new_env);
-		return;
-	}
-
-	purge_config(env, PURGE_EVERYTHING);
-	merge_config(env, new_env);
-	free(new_env);
-	log_info("configuration merge done");
-
-	/*
-	 * first reconfigure pfe
-	 */
-	proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF, -1, env, sizeof(*env));
-	TAILQ_FOREACH(table, env->sc_tables, entry) {
-		proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_TABLE, -1,
-		    &table->conf, sizeof(table->conf));
-		TAILQ_FOREACH(host, &table->hosts, entry) {
-			proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_HOST,
-			    -1, &host->conf, sizeof(host->conf));
-		}
-	}
-	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
-		proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_RDR, -1,
-		    &rdr->conf, sizeof(rdr->conf));
-		TAILQ_FOREACH(virt, &rdr->virts, entry)
-			proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_VIRT,
-			    -1, virt, sizeof(*virt));
-	}
-	proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_END, -1, NULL, 0);
-
-	/*
-	 * then reconfigure hce
-	 */
-	proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF, -1, env, sizeof(*env));
-	TAILQ_FOREACH(table, env->sc_tables, entry) {
-		proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF_TABLE, -1,
-		    &table->conf, sizeof(table->conf));
-		if (table->sendbuf != NULL)
-			proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF_SENDBUF,
-			    -1, table->sendbuf, strlen(table->sendbuf) + 1);
-		TAILQ_FOREACH(host, &table->hosts, entry) {
-			proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF_HOST,
-			    -1, &host->conf, sizeof(host->conf));
-		}
-	}
-	proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF_END, -1, NULL, 0);
-}
-
-void
-purge_config(struct relayd *env, u_int8_t what)
-{
-	struct table		*table;
-	struct rdr		*rdr;
-	struct address		*virt;
-	struct protocol		*proto;
-	struct relay		*rlay;
-	struct rsession		*sess;
-
-	if (what & PURGE_TABLES && env->sc_tables != NULL) {
-		while ((table = TAILQ_FIRST(env->sc_tables)) != NULL)
-			purge_table(env->sc_tables, table);
-		free(env->sc_tables);
-		env->sc_tables = NULL;
-	}
-
-	if (what & PURGE_RDRS && env->sc_rdrs != NULL) {
-		while ((rdr = TAILQ_FIRST(env->sc_rdrs)) != NULL) {
-			TAILQ_REMOVE(env->sc_rdrs, rdr, entry);
-			while ((virt = TAILQ_FIRST(&rdr->virts)) != NULL) {
-				TAILQ_REMOVE(&rdr->virts, virt, entry);
-				free(virt);
-			}
-			free(rdr);
-		}
-		free(env->sc_rdrs);
-		env->sc_rdrs = NULL;
-	}
-
-	if (what & PURGE_RELAYS && env->sc_relays != NULL) {
-		while ((rlay = TAILQ_FIRST(env->sc_relays)) != NULL) {
-			TAILQ_REMOVE(env->sc_relays, rlay, rl_entry);
-			while ((sess =
-			    SPLAY_ROOT(&rlay->rl_sessions)) != NULL) {
-				SPLAY_REMOVE(session_tree,
-				    &rlay->rl_sessions, sess);
-				free(sess);
-			}
-			if (rlay->rl_bev != NULL)
-				bufferevent_free(rlay->rl_bev);
-			if (rlay->rl_dstbev != NULL)
-				bufferevent_free(rlay->rl_dstbev);
-			if (rlay->rl_ssl_ctx != NULL)
-				SSL_CTX_free(rlay->rl_ssl_ctx);
-			free(rlay);
-		}
-		free(env->sc_relays);
-		env->sc_relays = NULL;
-	}
-
-	if (what & PURGE_PROTOS && env->sc_protos != NULL) {
-		while ((proto = TAILQ_FIRST(env->sc_protos)) != NULL) {
-			TAILQ_REMOVE(env->sc_protos, proto, entry);
-			purge_tree(&proto->request_tree);
-			purge_tree(&proto->response_tree);
-			if (proto->style != NULL)
-				free(proto->style);
-			free(proto);
-		}
-		free(env->sc_protos);
-		env->sc_protos = NULL;
-	}
-}
-
-void
-purge_tree(struct proto_tree *tree)
-{
-	struct protonode	*proot, *pn;
-
-	while ((proot = RB_ROOT(tree)) != NULL) {
-		RB_REMOVE(proto_tree, tree, proot);
-		if (proot->key != NULL)
-			free(proot->key);
-		if (proot->value != NULL)
-			free(proot->value);
-		while ((pn = SIMPLEQ_FIRST(&proot->head)) != NULL) {
-			SIMPLEQ_REMOVE_HEAD(&proot->head, entry);
-			if (pn->key != NULL)
-				free(pn->key);
-			if (pn->value != NULL)
-				free(pn->value);
-			if (pn->label != 0)
-				pn_unref(pn->label);
-			free(pn);
-		}
-		free(proot);
-	}
-}
-
-void
-purge_table(struct tablelist *head, struct table *table)
-{
-	struct host		*host;
-
-	while ((host = TAILQ_FIRST(&table->hosts)) != NULL) {
-		TAILQ_REMOVE(&table->hosts, host, entry);
-		if (host->cte.ssl != NULL)
-			SSL_free(host->cte.ssl);
-		free(host);
-	}
-	if (table->sendbuf != NULL)
-		free(table->sendbuf);
-	if (table->conf.flags & F_SSL)
-		SSL_CTX_free(table->ssl_ctx);
-
-	if (head != NULL)
-		TAILQ_REMOVE(head, table, entry);
-	free(table);
-}
-
 int
 parent_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
 	struct relayd		*env = p->p_env;
 	struct ctl_demote	 demote;
 	struct ctl_netroute	 crt;
+	u_int		 	 v;
+	char			*str = NULL;
 
 	switch (imsg->hdr.type) {
 	case IMSG_DEMOTE:
@@ -486,12 +391,25 @@ parent_dispatch_pfe(int fd, struct privsep_proc *p, st
 		memcpy(&crt, imsg->data, sizeof(crt));
 		pfe_route(env, &crt);
 		break;
+	case IMSG_CTL_RESET:
+		IMSG_SIZE_CHECK(imsg, &v);
+		memcpy(&v, imsg->data, sizeof(v));
+		parent_reload(env, v, NULL);
+		break;
 	case IMSG_CTL_RELOAD:
-		/*
-		 * so far we only get here if no L7 (relay) is done.
-		 */
-		reconfigure();
+		if (IMSG_DATA_SIZE(imsg) > 0)
+			str = get_string(imsg->data, IMSG_DATA_SIZE(imsg));
+		parent_reload(env, CONFIG_RELOAD, str);
+		if (str != NULL)
+			free(str);
 		break;
+	case IMSG_CTL_SHUTDOWN:
+		parent_shutdown(env);
+		break;
+	case IMSG_CFG_DONE:
+		if (env->sc_reload)
+			env->sc_reload--;
+		break;
 	default:
 		return (-1);
 	}
@@ -515,8 +433,12 @@ parent_dispatch_hce(int fd, struct privsep_proc *p, st
 		    -1, &scr, sizeof(scr));
 		break;
 	case IMSG_SNMPSOCK:
-		(void)snmp_sendsock(env, p->p_id);
+		(void)snmp_setsock(env, p->p_id);
 		break;
+	case IMSG_CFG_DONE:
+		if (env->sc_reload)
+			env->sc_reload--;
+		break;
 	default:
 		return (-1);
 	}
@@ -552,6 +474,10 @@ parent_dispatch_relay(int fd, struct privsep_proc *p, 
 		proc_compose_imsg(ps, PROC_RELAY, bnd.bnd_proc,
 		    IMSG_BINDANY, s, &bnd.bnd_id, sizeof(bnd.bnd_id));
 		break;
+	case IMSG_CFG_DONE:
+		if (env->sc_reload)
+			env->sc_reload--;
+		break;
 	default:
 		return (-1);
 	}
@@ -559,6 +485,96 @@ parent_dispatch_relay(int fd, struct privsep_proc *p, 
 	return (0);
 }
 
+void
+purge_tree(struct proto_tree *tree)
+{
+	struct protonode	*proot, *pn;
+
+	while ((proot = RB_ROOT(tree)) != NULL) {
+		RB_REMOVE(proto_tree, tree, proot);
+		if (proot->key != NULL)
+			free(proot->key);
+		if (proot->value != NULL)
+			free(proot->value);
+		while ((pn = SIMPLEQ_FIRST(&proot->head)) != NULL) {
+			SIMPLEQ_REMOVE_HEAD(&proot->head, entry);
+			if (pn->key != NULL)
+				free(pn->key);
+			if (pn->value != NULL)
+				free(pn->value);
+			if (pn->label != 0)
+				pn_unref(pn->label);
+			free(pn);
+		}
+		free(proot);
+	}
+}
+
+void
+purge_table(struct tablelist *head, struct table *table)
+{
+	struct host		*host;
+
+	while ((host = TAILQ_FIRST(&table->hosts)) != NULL) {
+		TAILQ_REMOVE(&table->hosts, host, entry);
+		if (event_initialized(&host->cte.ev)) {
+			event_del(&host->cte.ev);
+			close(host->cte.s);
+		}
+		if (host->cte.buf != NULL)
+			ibuf_free(host->cte.buf);
+		if (host->cte.ssl != NULL)
+			SSL_free(host->cte.ssl);
+		free(host);
+	}
+	if (table->sendbuf != NULL)
+		free(table->sendbuf);
+	if (table->conf.flags & F_SSL)
+		SSL_CTX_free(table->ssl_ctx);
+
+	if (head != NULL)
+		TAILQ_REMOVE(head, table, entry);
+	free(table);
+}
+
+void
+purge_relay(struct relayd *env, struct relay *rlay)
+{
+	struct rsession		*con;
+
+	/* shutdown and remove relay */
+	if (event_initialized(&rlay->rl_ev))
+		event_del(&rlay->rl_ev);
+	close(rlay->rl_s);
+	TAILQ_REMOVE(env->sc_relays, rlay, rl_entry);
+
+	/* cleanup sessions */
+	while ((con =
+	    SPLAY_ROOT(&rlay->rl_sessions)) != NULL)
+		relay_close(con, NULL);
+
+	/* cleanup relay */
+	if (rlay->rl_bev != NULL)
+		bufferevent_free(rlay->rl_bev);
+	if (rlay->rl_dstbev != NULL)
+		bufferevent_free(rlay->rl_dstbev);
+
+	if (rlay->rl_ssl_ctx != NULL)
+		SSL_CTX_free(rlay->rl_ssl_ctx);
+	if (rlay->rl_ssl_cert != NULL)
+		free(rlay->rl_ssl_cert);
+	if (rlay->rl_ssl_key != NULL)
+		free(rlay->rl_ssl_key);
+	if (rlay->rl_ssl_ca != NULL)
+		free(rlay->rl_ssl_ca);
+
+	free(rlay);
+}
+
+/*
+ * Utility functions
+ */
+
 struct host *
 host_find(struct relayd *env, objid_t id)
 {
@@ -605,6 +621,17 @@ relay_find(struct relayd *env, objid_t id)
 	return (NULL);
 }
 
+struct protocol *
+proto_find(struct relayd *env, objid_t id)
+{
+	struct protocol	*p;
+
+	TAILQ_FOREACH(p, env->sc_protos, entry)
+		if (p->id == id)
+			return (p);
+	return (NULL);
+}
+
 struct rsession *
 session_find(struct relayd *env, objid_t id)
 {
@@ -629,6 +656,17 @@ route_find(struct relayd *env, objid_t id)
 	return (NULL);
 }
 
+struct router *
+router_find(struct relayd *env, objid_t id)
+{
+	struct router	*rt;
+
+	TAILQ_FOREACH(rt, env->sc_rts, rt_entry)
+		if (rt->rt_conf.id == id)
+			return (rt);
+	return (NULL);
+}
+
 struct host *
 host_findbyname(struct relayd *env, const char *name)
 {
@@ -949,7 +987,6 @@ protonode_add(enum direction dir, struct protocol *pro
 			SIMPLEQ_NEXT(proot, entry) = pn;
 		SIMPLEQ_INSERT_TAIL(&proot->head, pn, entry);
 	}
-
 	if (node->type == NODE_TYPE_COOKIE)
 		pk.key = "Cookie";
 	else if (node->type == NODE_TYPE_URL)
@@ -1126,3 +1163,32 @@ socket_rlimit(int maxfd)
 	if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
 		fatal("socket_rlimit: failed to set resource limit");
 }
+
+char *
+get_string(u_int8_t *ptr, size_t len)
+{
+	size_t	 i;
+	char	*str;
+
+	for (i = 0; i < len; i++)
+		if (!(isprint((char)ptr[i]) || isspace((char)ptr[i])))
+			break;
+
+	if ((str = calloc(1, i + 1)) == NULL)
+		return (NULL);
+	memcpy(str, ptr, i);
+
+	return (str);
+}
+
+void *
+get_data(u_int8_t *ptr, size_t len)
+{
+	u_int8_t	*data;
+
+	if ((data = calloc(1, len)) == NULL)
+		return (NULL);
+	memcpy(data, ptr, len);
+
+	return (data);
+}
blob - 330ad861cd45bb65cdef3bc41e0891384f525abb
blob + 8da1a96285da2f415af4c3bd2eb77c941464660e
--- relayd.h
+++ relayd.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relayd.h,v 1.146 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: relayd.h,v 1.147 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -51,15 +51,18 @@
 #define RELAY_BACKLOG		10
 #define RELAY_MAXLOOKUPLEVELS	5
 
+#define CONFIG_RELOAD		0x00
+#define CONFIG_TABLES		0x01
+#define CONFIG_RDRS		0x02
+#define CONFIG_RELAYS		0x04
+#define CONFIG_PROTOS		0x08
+#define CONFIG_ROUTES		0x10
+#define CONFIG_RTS		0x20
+#define CONFIG_ALL		0xff
+
 #define SMALL_READ_BUF_SIZE	1024
 #define ICMP_BUF_SIZE		64
 
-#define PURGE_TABLES		0x01
-#define PURGE_RDRS		0x02
-#define PURGE_RELAYS		0x04
-#define PURGE_PROTOS		0x08
-#define PURGE_EVERYTHING	0xff
-
 #define SNMP_RECONNECT_TIMEOUT	{ 3, 0 }	/* sec, usec */
 
 #if DEBUG > 1
@@ -77,6 +80,11 @@ struct shuffle {
 
 typedef u_int32_t objid_t;
 
+struct ctl_flags {
+	u_int8_t	 cf_opts;
+	u_int32_t	 cf_flags;
+};
+
 struct ctl_status {
 	objid_t		 id;
 	int		 up;
@@ -100,12 +108,6 @@ struct ctl_demote {
 	int		 level;
 };
 
-struct ctl_netroute {
-	objid_t		 id;
-	objid_t		 hostid;
-	int		 up;
-};
-
 struct ctl_icmp_event {
 	struct relayd		*env;
 	int			 s;
@@ -218,6 +220,7 @@ struct portrange {
 };
 
 struct address {
+	objid_t			 rdrid;
 	struct sockaddr_storage	 ss;
 	int			 ipproto;
 	struct portrange	 port;
@@ -370,7 +373,6 @@ struct table {
 	int			 skipped;
 	struct hostlist		 hosts;
 	SSL_CTX			*ssl_ctx;
-	int			 sendbuf_len;
 	char			*sendbuf;
 };
 TAILQ_HEAD(tablelist, table);
@@ -465,16 +467,26 @@ enum noderesult {
 	PN_FAIL			= -1
 };
 
+struct protonode_config {
+	objid_t			 	 protoid;
+	size_t			 	 keylen;
+	size_t			 	 valuelen;
+	size_t			 	 len;
+	u_int			 	 dir;
+};
+
 struct protonode {
+	struct protonode_config		 conf;
 	objid_t				 id;
-	char				*key;
 	enum nodeaction			 action;
-	char				*value;
 	u_int8_t			 flags;
 	enum nodetype			 type;
 	u_int16_t			 mark;
 	u_int16_t			 label;
 
+	char				*key;
+	char				*value;
+
 	SIMPLEQ_HEAD(, protonode)	 head;
 	SIMPLEQ_ENTRY(protonode)	 entry;
 
@@ -526,7 +538,7 @@ struct protocol {
 	u_int8_t		 tcpipminttl;
 	u_int8_t		 sslflags;
 	char			 sslciphers[768];
-	char			*sslca;
+	char			 sslca[MAXPATHLEN];
 	char			 name[MAX_NAME_SIZE];
 	int			 cache;
 	enum prototype		 type;
@@ -565,6 +577,9 @@ struct relay_config {
 	struct sockaddr_storage	 dstaf;
 	struct timeval		 timeout;
 	enum forwardmode	 fwdmode;
+	off_t			 ssl_cert_len;
+	off_t			 ssl_key_len;
+	off_t			 ssl_ca_len;
 };
 
 struct relay {
@@ -589,11 +604,8 @@ struct relay {
 
 	SSL_CTX			*rl_ssl_ctx;
 	char			*rl_ssl_cert;
-	off_t			 rl_ssl_cert_len;
 	char			*rl_ssl_key;
-	off_t			 rl_ssl_key_len;
 	char			*rl_ssl_ca;
-	off_t			 rl_ssl_ca_len;
 
 	struct ctl_stats	 rl_stats[RELAY_MAXPROC + 1];
 
@@ -635,11 +647,11 @@ struct router_config {
 	objid_t			 gwtable;
 	in_port_t		 gwport;
 	int			 rtable;
+	int			 af;
 };
 
 struct router {
 	struct router_config	 rt_conf;
-	int			 rt_af;
 
 	struct table		*rt_gwtable;
 	struct netroutelist	 rt_netroutes;
@@ -648,6 +660,12 @@ struct router {
 };
 TAILQ_HEAD(routerlist, router);
 
+struct ctl_netroute {
+	int			up;
+	struct host_config	host;
+	struct netroute_config	nr;
+	struct router_config	rt;
+};
 
 /* initially control.h */
 struct control_sock {
@@ -718,6 +736,7 @@ enum imsg_type {
 	IMSG_CTL_HOST_DISABLE,
 	IMSG_CTL_SHUTDOWN,
 	IMSG_CTL_RELOAD,
+	IMSG_CTL_RESET,
 	IMSG_CTL_POLL,
 	IMSG_CTL_NOTIFY,
 	IMSG_CTL_RDR_STATS,
@@ -733,30 +752,28 @@ enum imsg_type {
 	IMSG_NATLOOK,
 	IMSG_DEMOTE,
 	IMSG_STATISTICS,
-	IMSG_RECONF,		/* reconfiguration notifies */
-	IMSG_RECONF_TABLE,
-	IMSG_RECONF_SENDBUF,
-	IMSG_RECONF_HOST,
-	IMSG_RECONF_RDR,
-	IMSG_RECONF_VIRT,
-	IMSG_RECONF_PROTO,
-	IMSG_RECONF_REQUEST_TREE,
-	IMSG_RECONF_RESPONSE_TREE,
-	IMSG_RECONF_PNODE_KEY,
-	IMSG_RECONF_PNODE_VAL,
-	IMSG_RECONF_RELAY,
-	IMSG_RECONF_END,
 	IMSG_SCRIPT,
 	IMSG_SNMPSOCK,
 	IMSG_BINDANY,
-	IMSG_RTMSG		/* from pfe to parent */
+	IMSG_RTMSG,		/* from pfe to parent */
+	IMSG_CFG_TABLE,		/* configuration from parent */
+	IMSG_CFG_HOST,
+	IMSG_CFG_RDR,
+	IMSG_CFG_VIRT,
+	IMSG_CFG_ROUTER,
+	IMSG_CFG_ROUTE,
+	IMSG_CFG_PROTO,
+	IMSG_CFG_PROTONODE,
+	IMSG_CFG_RELAY,
+	IMSG_CFG_DONE
 };
 
 enum privsep_procid {
+	PROC_ALL	= -1,
 	PROC_PARENT	= 0,
-	PROC_PFE,
 	PROC_HCE,
 	PROC_RELAY,
+	PROC_PFE,
 	PROC_MAX
 } privsep_process;
 
@@ -768,6 +785,7 @@ struct privsep {
 	struct imsgev			*ps_ievs[PROC_MAX];
 	const char			*ps_title[PROC_MAX];
 	pid_t				 ps_pid[PROC_MAX];
+	u_int8_t			 ps_what[PROC_MAX];
 
 	u_int				 ps_instances[PROC_MAX];
 	u_int				 ps_instance;
@@ -781,6 +799,7 @@ struct privsep {
 	struct event			 ps_evsighup;
 	struct event			 ps_evsigpipe;
 
+	int				 ps_noaction;
 	struct passwd			*ps_pw;
 	struct relayd			*ps_env;
 };
@@ -802,7 +821,7 @@ struct privsep_proc {
 struct relayd {
 	u_int8_t		 sc_opts;
 	u_int32_t		 sc_flags;
-	const char		*sc_confpath;
+	const char		*sc_conffile;
 	struct pfdata		*sc_pf;
 	int			 sc_rtsock;
 	int			 sc_rtseq;
@@ -842,6 +861,7 @@ struct relayd {
 	struct ctl_icmp_event	 sc_icmp6_recv;
 
 	struct privsep		*sc_ps;
+	int			 sc_reload;
 };
 
 #define RELAYD_OPT_VERBOSE		0x01
@@ -863,8 +883,9 @@ void	 socket_set_blockmode(int, enum blockmodes);
 extern  struct ctl_connlist ctl_conns;
 
 /* parse.y */
-struct relayd	*parse_config(const char *, int);
-int		 cmdline_symset(char *);
+int	 parse_config(const char *, struct relayd *);
+int	 load_config(const char *, struct relayd *);
+int	 cmdline_symset(char *);
 
 /* log.c */
 const char *host_error(enum host_error);
@@ -889,7 +910,7 @@ int	 disable_table(struct ctl_conn *, struct ctl_id *)
 int	 disable_host(struct ctl_conn *, struct ctl_id *, struct host *);
 
 /* pfe_filter.c */
-void	 init_filter(struct relayd *);
+void	 init_filter(struct relayd *, int);
 void	 init_tables(struct relayd *);
 void	 flush_table(struct relayd *, struct rdr *);
 void	 sync_table(struct relayd *, struct rdr *, struct table *);
@@ -910,6 +931,7 @@ void	 hce_notify_done(struct host *, enum host_error);
 
 /* relay.c */
 pid_t	 relay(struct privsep *, struct privsep_proc *);
+int	 relay_privinit(struct relay *);
 void	 relay_notify_done(struct host *, const char *);
 int	 relay_session_cmp(struct rsession *, struct rsession *);
 int	 relay_load_certfiles(struct relay *);
@@ -962,6 +984,7 @@ struct host	*host_find(struct relayd *, objid_t);
 struct table	*table_find(struct relayd *, objid_t);
 struct rdr	*rdr_find(struct relayd *, objid_t);
 struct netroute	*route_find(struct relayd *, objid_t);
+struct router	*router_find(struct relayd *, objid_t);
 struct host	*host_findbyname(struct relayd *, const char *);
 struct table	*table_findbyname(struct relayd *, const char *);
 struct table	*table_findbyconf(struct relayd *, struct table *);
@@ -970,14 +993,15 @@ void		 event_again(struct event *, int, short,
 		    void (*)(int, short, void *),
 		    struct timeval *, struct timeval *, void *);
 struct relay	*relay_find(struct relayd *, objid_t);
+struct protocol	*proto_find(struct relayd *, objid_t);
 struct rsession	*session_find(struct relayd *, objid_t);
 struct relay	*relay_findbyname(struct relayd *, const char *);
 struct relay	*relay_findbyaddr(struct relayd *, struct relay_config *);
 int		 expand_string(char *, size_t, const char *, const char *);
 void		 translate_string(char *);
-void		 purge_config(struct relayd *, u_int8_t);
+void		 purge_tree(struct proto_tree *);
 void		 purge_table(struct tablelist *, struct table *);
-void		 merge_config(struct relayd *, struct relayd *);
+void		 purge_relay(struct relayd *, struct relay *);
 char		*digeststr(enum digest_type, const u_int8_t *, size_t, char *);
 const char	*canonicalize_host(const char *, char *, size_t);
 struct protonode *protonode_header(enum direction, struct protocol *,
@@ -992,6 +1016,8 @@ void		 imsg_event_add(struct imsgev *);
 int	 	 imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
 		    pid_t, int, void *, u_int16_t);
 void		 socket_rlimit(int);
+char		*get_string(u_int8_t *, size_t);
+void		*get_data(u_int8_t *, size_t);
 
 /* carp.c */
 int	 carp_demote_init(char *, int);
@@ -1008,7 +1034,8 @@ void		 pn_ref(u_int16_t);
 
 /* snmp.c */
 void	 snmp_init(struct relayd *, enum privsep_procid);
-int	 snmp_sendsock(struct relayd *, enum privsep_procid);
+int	 snmp_setsock(struct relayd *, enum privsep_procid);
+int	 snmp_getsock(struct relayd *, struct imsg *);
 void	 snmp_hosttrap(struct relayd *, struct table *, struct host *);
 
 /* shuffle.c */
@@ -1028,11 +1055,13 @@ __dead void fatalx(const char *);
 /* proc.c */
 void	 proc_init(struct privsep *, struct privsep_proc *, u_int);
 void	 proc_kill(struct privsep *);
+void	 proc_clear(struct privsep *, int);
 void	 proc_config(struct privsep *, struct privsep_proc *, u_int);
 void	 proc_dispatch(int, short event, void *);
 pid_t	 proc_run(struct privsep *, struct privsep_proc *,
 	    struct privsep_proc *, u_int,
 	    void (*)(struct privsep *, struct privsep_proc *, void *), void *);
+void	 proc_range(struct privsep *, enum privsep_procid, int *, int *);
 int	 proc_compose_imsg(struct privsep *, enum privsep_procid, int,
 	    u_int16_t, int, void *, u_int16_t);
 int	 proc_composev_imsg(struct privsep *, enum privsep_procid, int,
@@ -1047,3 +1076,26 @@ int	 imsg_compose_event(struct imsgev *, u_int16_t, u_
 	    pid_t, int, void *, u_int16_t);
 int	 imsg_composev_event(struct imsgev *, u_int16_t, u_int32_t,
 	    pid_t, int, const struct iovec *, int);
+
+/* config.c */
+int	 config_init(struct relayd *);
+void	 config_purge(struct relayd *, u_int);
+int	 config_setreset(struct relayd *, u_int);
+int	 config_getreset(struct relayd *, struct imsg *);
+int	 config_getcfg(struct relayd *, struct imsg *);
+int	 config_settable(struct relayd *, struct table *);
+int	 config_gettable(struct relayd *, struct imsg *);
+int	 config_gethost(struct relayd *, struct imsg *);
+int	 config_setrdr(struct relayd *, struct rdr *);
+int	 config_getrdr(struct relayd *, struct imsg *);
+int	 config_getvirt(struct relayd *, struct imsg *);
+int	 config_setrt(struct relayd *, struct router *);
+int	 config_getrt(struct relayd *, struct imsg *);
+int	 config_getroute(struct relayd *, struct imsg *);
+int	 config_setproto(struct relayd *env, struct protocol *);
+int	 config_getproto(struct relayd *, struct imsg *);
+int	 config_setprotonode(struct relayd *, enum privsep_procid,
+	    struct protocol *, enum direction);
+int	 config_getprotonode(struct relayd *, struct imsg *);
+int	 config_setrelay(struct relayd *env, struct relay *);
+int	 config_getrelay(struct relayd *, struct imsg *);
blob - f341d74bade61f7ee70a0cac160412171e5bb379
blob + f21f657745507b45df62c770f5e3d28f83318d5e
--- snmp.c
+++ snmp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: snmp.c,v 1.10 2011/05/09 12:08:47 reyk Exp $	*/
+/*	$OpenBSD: snmp.c,v 1.11 2011/05/19 08:56:49 reyk Exp $	*/
 
 /*
  * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org>
@@ -49,7 +49,6 @@ static struct imsgev	*iev_snmp = NULL;
 enum privsep_procid	 snmp_procid;
 
 void	 snmp_sock(int, short, void *);
-int	 snmp_getsock(struct relayd *, enum privsep_procid);
 int	 snmp_element(const char *, enum snmp_type, void *, int64_t);
 
 void
@@ -72,80 +71,59 @@ snmp_init(struct relayd *env, enum privsep_procid id)
 }
 
 int
-snmp_sendsock(struct relayd *env, enum privsep_procid id)
+snmp_setsock(struct relayd *env, enum privsep_procid id)
 {
 	struct imsgev		 tmpiev;
 	struct sockaddr_un	 sun;
 	int			 s = -1;
 
 	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
-		goto fail;
+		goto done;
 
 	bzero(&sun, sizeof(sun));
 	sun.sun_family = AF_UNIX;
 	strlcpy(sun.sun_path, SNMP_SOCKET, sizeof(sun.sun_path));
-	if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1)
-		goto fail;
 
+	if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+		close(s);
+		s = -1;
+		goto done;
+	}
+
 	/* enable restricted snmp socket mode */
 	bzero(&tmpiev, sizeof(tmpiev));
 	imsg_init(&tmpiev.ibuf, s);
 	imsg_compose_event(&tmpiev, IMSG_SNMP_LOCK, 0, 0, -1, NULL, 0);
 
+ done:
 	proc_compose_imsg(env->sc_ps, id, -1, IMSG_SNMPSOCK, s, NULL, 0);
-	proc_flush_imsg(env->sc_ps, id, -1); /* need to send the socket now */
-	close(s);
-	return (0);
-
- fail:
-	if (s != -1)
-		close(s);
-	proc_compose_imsg(env->sc_ps, id, -1, IMSG_NONE, -1, NULL, 0);
 	return (-1);
 }
 
 int
-snmp_getsock(struct relayd *env, enum privsep_procid id)
+snmp_getsock(struct relayd *env, struct imsg *imsg)
 {
-	struct imsg	 imsg;
-	struct imsgbuf	*ibuf;
-	int		 n, s = -1, done = 0;
+	struct timeval	 tv = SNMP_RECONNECT_TIMEOUT;
 
-	ibuf = proc_ibuf(env->sc_ps, id, -1);
-	proc_compose_imsg(env->sc_ps, id, -1, IMSG_SNMPSOCK, -1, NULL, 0);
-	proc_flush_imsg(env->sc_ps, id, -1);
+	if (imsg->fd == -1)
+		goto retry;
 
-	while (!done) {
-		do {
-			if ((n = imsg_read(ibuf)) == -1)
-				fatalx("snmp_getsock: imsg_read error");
-		} while (n == -2); /* handle non-blocking I/O */
-		while (!done) {
-			if ((n = imsg_get(ibuf, &imsg)) == -1)
-				fatal("snmp_getsock: failed to get imsg");
-			if (n == 0)
-				break;
-			done = 1;
-			switch (imsg.hdr.type) {
-			case IMSG_SNMPSOCK:
-				s = imsg.fd;
-				break;
-			default:
-				break;
-			}
-			imsg_free(&imsg);
-		}
-	}
+	env->sc_snmp = imsg->fd;
 
-	if (s != -1) {
-		log_debug("%s: got new snmp socket %d", __func__, s);
-		if (iev_snmp == NULL && (iev_snmp = (struct imsgev *)
-		    calloc(1, sizeof(struct imsgev))) == NULL)
-			fatal("snmp_getsock: calloc");
-		imsg_init(&iev_snmp->ibuf, s);
-	}
+	log_debug("%s: got new snmp socket %d", __func__, imsg->fd);
+	if (iev_snmp == NULL && (iev_snmp = (struct imsgev *)
+	    calloc(1, sizeof(struct imsgev))) == NULL)
+		fatal("snmp_getsock: calloc");
+	imsg_init(&iev_snmp->ibuf, env->sc_snmp);
 
-	return (s);
+	event_set(&env->sc_snmpev, env->sc_snmp,
+	    EV_READ|EV_TIMEOUT, snmp_sock, env);
+	event_add(&env->sc_snmpev, NULL);
+	return (0);
+ retry:
+	evtimer_set(&env->sc_snmpto, snmp_sock, env);
+	evtimer_add(&env->sc_snmpto, &tv);
+	return (0);
 }
 
 void
@@ -164,14 +142,8 @@ snmp_sock(int fd, short event, void *arg)
 		break;
 	}
 
-	if ((env->sc_snmp = snmp_getsock(env, snmp_procid)) == -1) {
-		DPRINTF("%s: failed to open snmp socket", __func__);
-		goto retry;
-	}
-
-	event_set(&env->sc_snmpev, env->sc_snmp,
-	    EV_READ|EV_TIMEOUT, snmp_sock, arg);
-	event_add(&env->sc_snmpev, NULL);
+	proc_compose_imsg(env->sc_ps, snmp_procid, -1,
+	    IMSG_SNMPSOCK, -1, NULL, 0);
 	return;
  retry:
 	evtimer_set(&env->sc_snmpto, snmp_sock, arg);