summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbeck <beck@openbsd.org>2008-05-07 12:19:20 +0000
committerbeck <beck@openbsd.org>2008-05-07 12:19:20 +0000
commit5f515bebfbc9b723b65ade175c407708da91f729 (patch)
tree0fa55cc0770ab12fb466af1503df536b0b0341a1
parentRef to ripd instead of routed. (diff)
downloadwireguard-openbsd-5f515bebfbc9b723b65ade175c407708da91f729.tar.xz
wireguard-openbsd-5f515bebfbc9b723b65ade175c407708da91f729.zip
Add synchronisation support for dhcpd - this allows for two dhcpd's
with the same configuration to be run on the same net and they will keep their lease files/state in synch, and therefore allowing you to run redundant dhcpd's. Synchronization code stolen from spamd, uses an hmac key in /var/db/dhcpd.key if it exists. ok krw@ deraadt@
-rw-r--r--etc/services3
-rw-r--r--usr.sbin/dhcpd/Makefile5
-rw-r--r--usr.sbin/dhcpd/dhcp.c8
-rw-r--r--usr.sbin/dhcpd/dhcpd.883
-rw-r--r--usr.sbin/dhcpd/dhcpd.c39
-rw-r--r--usr.sbin/dhcpd/dhcpd.h3
-rw-r--r--usr.sbin/dhcpd/dispatch.c16
-rw-r--r--usr.sbin/dhcpd/errwarn.c21
-rw-r--r--usr.sbin/dhcpd/memory.c10
-rw-r--r--usr.sbin/dhcpd/sync.c465
-rw-r--r--usr.sbin/dhcpd/sync.h71
11 files changed, 698 insertions, 26 deletions
diff --git a/etc/services b/etc/services
index 68602c5133d..a05047c5db2 100644
--- a/etc/services
+++ b/etc/services
@@ -1,4 +1,4 @@
-# $OpenBSD: services,v 1.68 2008/05/06 22:52:03 sthen Exp $
+# $OpenBSD: services,v 1.69 2008/05/07 12:19:20 beck Exp $
#
# Network services, Internet style
#
@@ -277,6 +277,7 @@ icb 7326/tcp # Internet Citizen's Band
spamd 8025/tcp # spamd(8)
spamd-sync 8025/udp # spamd(8) synchronisation
spamd-cfg 8026/tcp # spamd(8) configuration
+dhcpd-sync 8067/udp # dhcpd(8) synchronisation
hunt 26740/udp # hunt(6)
#
# Appletalk
diff --git a/usr.sbin/dhcpd/Makefile b/usr.sbin/dhcpd/Makefile
index 2429bf89325..341ed8a31d4 100644
--- a/usr.sbin/dhcpd/Makefile
+++ b/usr.sbin/dhcpd/Makefile
@@ -1,13 +1,14 @@
-# $OpenBSD: Makefile,v 1.3 2006/05/31 02:43:15 ckuethe Exp $
+# $OpenBSD: Makefile,v 1.4 2008/05/07 12:19:20 beck Exp $
.include <bsd.own.mk>
SRCS= bootp.c confpars.c db.c dhcp.c dhcpd.c bpf.c packet.c errwarn.c \
dispatch.c print.c memory.c options.c inet.c conflex.c parse.c \
- alloc.c tables.c tree.c hash.c convert.c icmp.c pfutils.c
+ alloc.c tables.c tree.c hash.c convert.c icmp.c pfutils.c sync.c
PROG= dhcpd
MAN= dhcpd.8 dhcpd.conf.5 dhcpd.leases.5 dhcp-options.5
+LDADD+=-lcrypto
CFLAGS+=-Wall
.include <bsd.prog.mk>
diff --git a/usr.sbin/dhcpd/dhcp.c b/usr.sbin/dhcpd/dhcp.c
index b81e1e2330a..c3f5df5901a 100644
--- a/usr.sbin/dhcpd/dhcp.c
+++ b/usr.sbin/dhcpd/dhcp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcp.c,v 1.25 2006/12/15 16:03:16 stevesk Exp $ */
+/* $OpenBSD: dhcp.c,v 1.26 2008/05/07 12:19:20 beck Exp $ */
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999
@@ -39,6 +39,7 @@
*/
#include "dhcpd.h"
+#include "sync.h"
int outstanding_pings;
@@ -334,6 +335,7 @@ dhcprequest(struct packet *packet)
!memcmp(lease->hardware_addr.haddr, packet->raw->chaddr,
packet->raw->hlen)))) {
ack_lease(packet, lease, DHCPACK, 0);
+ sync_lease(lease);
return;
}
@@ -346,8 +348,10 @@ dhcprequest(struct packet *packet)
* The thing we probably should not do is to remain silent.
* For now, we'll just assign the lease to the client anyway.
*/
- if (lease)
+ if (lease) {
ack_lease(packet, lease, DHCPACK, 0);
+ sync_lease(lease);
+ }
}
void
diff --git a/usr.sbin/dhcpd/dhcpd.8 b/usr.sbin/dhcpd/dhcpd.8
index 89121f57ada..6931a2f4157 100644
--- a/usr.sbin/dhcpd/dhcpd.8
+++ b/usr.sbin/dhcpd/dhcpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: dhcpd.8,v 1.15 2007/05/31 19:20:23 jmc Exp $
+.\" $OpenBSD: dhcpd.8,v 1.16 2008/05/07 12:19:20 beck Exp $
.\"
.\" Copyright (c) 1995, 1996 The Internet Software Consortium.
.\" All rights reserved.
@@ -36,7 +36,7 @@
.\" see ``http://www.isc.org/''. To learn more about Vixie
.\" Enterprises, see ``http://www.vix.com''.
.\"
-.Dd $Mdocdate: May 31 2007 $
+.Dd $Mdocdate: May 7 2008 $
.Dt DHCPD 8
.Os
.Sh NAME
@@ -51,6 +51,8 @@
.Op Fl c Ar config-file
.Op Fl L Ar leased_ip_table
.Op Fl l Ar lease-file
+.Op Fl Y Ar synctarget
+.Op Fl y Ar synclisten
.Op Ar if0 Op Ar ... ifN
.Ek
.Sh DESCRIPTION
@@ -237,6 +239,32 @@ for testing lease files in a non-production environment.
.It Fl n
Only test configuration, do not run
.Nm .
+.It Fl Y Ar synctarget
+Add target
+.Ar synctarget
+to receive synchronisation messages.
+.Ar synctarget
+can be either an IPv4 address for unicast messages
+or a network interface name followed optionally by a colon and a numeric TTL
+value for multicast messages to the group 224.0.1.240.
+If the multicast TTL is not specified, a default value of 1 is used.
+This option can be specified multiple times.
+See also
+.Sx SYNCHRONISATION
+below.
+.It Fl y Ar synclisten
+Listen on
+.Ar synclisten
+for incoming synchronisation messages.
+The format for
+.Ar synclisten
+is the same as for
+.Ar synctarget ,
+above.
+This option can be specified only once.
+See also
+.Sx SYNCHRONISATION
+below.
.El
.Sh CONFIGURATION
The syntax of the
@@ -366,6 +394,57 @@ A more complete description of the
.Pa dhcpd.conf
file syntax is provided in
.Xr dhcpd.conf 5 .
+.Sh SYNCHRONISATION
+.Nm
+supports realtime synchronisation of the lease allocations to
+a number of
+.Nm
+daemons running on multiple machines,
+using the
+.Fl Y
+and
+.Fl y
+options.
+.Pp
+The following example will accept incoming multicast and unicast
+synchronisation messages, and send outgoing multicast messages through
+the network interface
+.Ar em0 :
+.Bd -literal -offset indent
+# /usr/sbin/dhcpd -y em0 -Y em0
+.Ed
+.Pp
+The second example will increase the multicast TTL to a value of 2,
+add the unicast targets
+.Ar foo.somewhere.org
+and
+.Ar bar.somewhere.org ,
+and accept incoming unicast messages sent to
+.Ar example.somewhere.org
+only.
+.Bd -literal -offset indent
+# /usr/sbin/dhcpd -y example.somewhere.org -Y em0:2 \e
+ -Y foo.somewhere.org -Y bar.somewhere.org
+.Ed
+.Pp
+If the file
+.Pa /var/db/dhcpd.key
+exists,
+.Nm
+will calculate the message-digest fingerprint (checksum) for the file
+and use it as a shared key to authenticate the synchronisation messages.
+The file itself can contain any data.
+For example, to create a secure random key:
+.Bd -literal -offset indent
+# dd if=/dev/arandom of=/var/db/dhcpd.key bs=2048 count=1
+.Ed
+.Pp
+The file needs to be copied to all hosts
+sending or receiving synchronisation messages.
+.Pp
+All hosts using synchronisation must use the same configuration in the
+.Pa /etc/dhcpd.conf
+file.
.Sh FILES
.Bl -tag -width "/var/db/dhcpd.leases~ " -compact
.It /etc/dhcpd.conf
diff --git a/usr.sbin/dhcpd/dhcpd.c b/usr.sbin/dhcpd/dhcpd.c
index ede422d2266..163cdb59035 100644
--- a/usr.sbin/dhcpd/dhcpd.c
+++ b/usr.sbin/dhcpd/dhcpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcpd.c,v 1.34 2007/12/30 13:38:47 sobrado Exp $ */
+/* $OpenBSD: dhcpd.c,v 1.35 2008/05/07 12:19:20 beck Exp $ */
/*
* Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org>
@@ -40,7 +40,9 @@
*/
#include "dhcpd.h"
+#include "sync.h"
+#include <err.h>
#include <pwd.h>
void usage(void);
@@ -56,24 +58,35 @@ int log_priority;
int log_perror = 0;
int pfpipe[2];
int gotpipe = 0;
+int syncrecv;
+int syncsend;
+int syncfd = -1;
pid_t pfproc_pid = -1;
+u_short sync_port;
char *path_dhcpd_conf = _PATH_DHCPD_CONF;
char *path_dhcpd_db = _PATH_DHCPD_DB;
char *abandoned_tab = NULL;
char *changedmac_tab = NULL;
char *leased_tab = NULL;
+struct syslog_data sdata = SYSLOG_DATA_INIT;
int
main(int argc, char *argv[])
{
int ch, cftest = 0, daemonize = 1;
extern char *__progname;
+ char *sync_iface = NULL;
+ char *sync_baddr = NULL;
+ struct servent *ent;
/* Initially, log errors to stderr as well as to syslogd. */
- openlog(__progname, LOG_NDELAY, DHCPD_LOG_FACILITY);
- setlogmask(LOG_UPTO(LOG_INFO));
+ openlog_r(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY, &sdata);
- while ((ch = getopt(argc, argv, "A:C:L:c:dfl:n")) != -1)
+ if ((ent = getservbyname("dhcpd-sync", "udp")) == NULL)
+ errx(1, "Can't find service \"dhcpd-sync\" in /etc/services");
+ sync_port = ntohs(ent->s_port);
+
+ while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nY:y:")) != -1)
switch (ch) {
case 'A':
abandoned_tab = optarg;
@@ -102,6 +115,15 @@ main(int argc, char *argv[])
cftest = 1;
log_perror = 1;
break;
+ case 'Y':
+ if (sync_addhost(optarg, sync_port) != 0)
+ sync_iface = optarg;
+ syncsend++;
+ break;
+ case 'y':
+ sync_baddr = optarg;
+ syncrecv++;
+ break;
default:
usage();
}
@@ -133,10 +155,17 @@ main(int argc, char *argv[])
if (cftest)
exit(0);
+ if (syncsend || syncrecv) {
+ syncfd = sync_init(sync_iface, sync_baddr, sync_port);
+ if (syncfd == -1)
+ err(1, "sync init");
+ }
+
db_startup();
discover_interfaces();
icmp_startup(1, lease_pinged);
+
if ((pw = getpwnam("_dhcp")) == NULL)
error("user \"_dhcp\" not found");
@@ -190,7 +219,7 @@ usage(void)
fprintf(stderr, "usage: %s [-dfn] [-A abandoned_ip_table]", __progname);
fprintf(stderr, " [-C changed_ip_table]\n");
fprintf(stderr, "\t[-c config-file] [-L leased_ip_table]");
- fprintf(stderr, " [-l lease-file]\n");
+ fprintf(stderr, " [-l lease-file] [-Y synctarget] [-y synclisten]\n");
fprintf(stderr, "\t[if0 [... ifN]]\n");
exit(1);
}
diff --git a/usr.sbin/dhcpd/dhcpd.h b/usr.sbin/dhcpd/dhcpd.h
index 3472ab40f27..46e0f77e3aa 100644
--- a/usr.sbin/dhcpd/dhcpd.h
+++ b/usr.sbin/dhcpd/dhcpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: dhcpd.h,v 1.35 2008/01/18 20:14:03 krw Exp $ */
+/* $OpenBSD: dhcpd.h,v 1.36 2008/05/07 12:19:20 beck Exp $ */
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999
@@ -721,3 +721,4 @@ void pf_kill_state(int, struct in_addr);
size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
#define vwrite (ssize_t (*)(int, void *, size_t))write
void pfmsg(char, struct lease *);
+extern struct syslog_data sdata;
diff --git a/usr.sbin/dhcpd/dispatch.c b/usr.sbin/dhcpd/dispatch.c
index 1d371e3c153..35bedebdbef 100644
--- a/usr.sbin/dhcpd/dispatch.c
+++ b/usr.sbin/dhcpd/dispatch.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dispatch.c,v 1.22 2006/12/12 19:38:55 stevesk Exp $ */
+/* $OpenBSD: dispatch.c,v 1.23 2008/05/07 12:19:20 beck Exp $ */
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999
@@ -39,11 +39,14 @@
*/
#include "dhcpd.h"
+#include "sync.h"
#include <ifaddrs.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <net/if_media.h>
+extern int syncfd;
+
struct interface_info *interfaces;
struct protocol *protocols;
struct dhcpd_timeout *timeouts;
@@ -273,6 +276,8 @@ dispatch(void)
for (nfds = 0, l = protocols; l; l = l->next)
nfds++;
+ if (syncfd != -1)
+ nfds++;
if (nfds > nfds_max) {
fds = realloc(fds, nfds * sizeof(struct pollfd));
if (fds == NULL)
@@ -321,9 +326,16 @@ another:
++i;
}
}
+
if (i == 0)
error("No live interfaces to poll on - exiting.");
+ if (syncfd != -1) {
+ /* add syncer */
+ fds[i].fd = syncfd;
+ fds[i].events = POLLIN;
+ }
+
/* Wait for a packet or a timeout... */
switch (poll(fds, nfds, to_msec)) {
case -1:
@@ -346,6 +358,8 @@ another:
}
++i;
}
+ if ((syncfd != -1) && (fds[i].revents & (POLLIN | POLLHUP)))
+ sync_recv();
interfaces_invalidated = 0;
}
}
diff --git a/usr.sbin/dhcpd/errwarn.c b/usr.sbin/dhcpd/errwarn.c
index 70bc1830594..050eb230076 100644
--- a/usr.sbin/dhcpd/errwarn.c
+++ b/usr.sbin/dhcpd/errwarn.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: errwarn.c,v 1.7 2007/03/02 11:37:53 henning Exp $ */
+/* $OpenBSD: errwarn.c,v 1.8 2008/05/07 12:19:20 beck Exp $ */
/* Errors and warnings... */
@@ -73,13 +73,13 @@ error(char *fmt, ...)
write(STDERR_FILENO, mbuf, strlen(mbuf));
write(STDERR_FILENO, "\n", 1);
} else
- syslog(log_priority | LOG_ERR, "%s", mbuf);
+ syslog_r(log_priority | LOG_ERR, &sdata, "%s", mbuf);
if (log_perror) {
fprintf(stderr, "exiting.\n");
fflush(stderr);
} else
- syslog(LOG_CRIT, "exiting.");
+ syslog_r(LOG_CRIT, &sdata, "exiting.");
exit(1);
}
@@ -102,7 +102,7 @@ warning(char *fmt, ...)
write(STDERR_FILENO, mbuf, strlen(mbuf));
write(STDERR_FILENO, "\n", 1);
} else
- syslog(log_priority | LOG_ERR, "%s", mbuf);
+ syslog_r(log_priority | LOG_ERR, &sdata, "%s", mbuf);
return (0);
}
@@ -125,7 +125,7 @@ note(char *fmt, ...)
write(STDERR_FILENO, mbuf, strlen(mbuf));
write(STDERR_FILENO, "\n", 1);
} else
- syslog(log_priority | LOG_INFO, "%s", mbuf);
+ syslog_r(log_priority | LOG_INFO, &sdata, "%s", mbuf);
return (0);
}
@@ -148,7 +148,7 @@ debug(char *fmt, ...)
write(STDERR_FILENO, mbuf, strlen(mbuf));
write(STDERR_FILENO, "\n", 1);
} else
- syslog(log_priority | LOG_DEBUG, "%s", mbuf);
+ syslog_r(log_priority | LOG_DEBUG, &sdata, "%s", mbuf);
return (0);
}
@@ -168,7 +168,7 @@ do_percentm(char *obuf, size_t size, char *ibuf)
/*
* We wouldn't need this mess if printf handled %m, or if
- * strerror() had been invented before syslog().
+ * strerror() had been invented before syslog_r().
*/
for (fmt_left = size; (ch = *s); ++s) {
if (ch == '%' && s[1] == 'm') {
@@ -226,10 +226,11 @@ parse_warn(char *fmt, ...)
}
writev(STDERR_FILENO, iov, iovcnt);
} else {
- syslog(log_priority | LOG_ERR, "%s", mbuf);
- syslog(log_priority | LOG_ERR, "%s", token_line);
+ syslog_r(log_priority | LOG_ERR, &sdata, "%s", mbuf);
+ syslog_r(log_priority | LOG_ERR, &sdata, "%s", token_line);
if (lexchar < 81)
- syslog(log_priority | LOG_ERR, "%*c", lexchar, '^');
+ syslog_r(log_priority | LOG_ERR, &sdata, "%*c", lexchar,
+ '^');
}
warnings_occurred = 1;
diff --git a/usr.sbin/dhcpd/memory.c b/usr.sbin/dhcpd/memory.c
index a2884ba4689..84714112e7a 100644
--- a/usr.sbin/dhcpd/memory.c
+++ b/usr.sbin/dhcpd/memory.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: memory.c,v 1.14 2006/08/09 22:23:53 cloder Exp $ */
+/* $OpenBSD: memory.c,v 1.15 2008/05/07 12:19:20 beck Exp $ */
/*
* Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
@@ -39,6 +39,7 @@
*/
#include "dhcpd.h"
+#include "sync.h"
struct subnet *subnets;
static struct shared_network *shared_networks;
@@ -52,6 +53,8 @@ static struct lease *dangling_leases;
static struct hash_table *vendor_class_hash;
static struct hash_table *user_class_hash;
+extern int syncsend;
+
void
enter_host(struct host_decl *hd)
{
@@ -843,9 +846,12 @@ write_leases(void)
for (s = shared_networks; s; s = s->next) {
for (l = s->leases; l; l = l->next) {
if (l->hardware_addr.hlen || l->uid_len ||
- (l->flags & ABANDONED_LEASE))
+ (l->flags & ABANDONED_LEASE)) {
if (!write_lease(l))
error("Can't rewrite lease database");
+ if (syncsend)
+ sync_lease(l);
+ }
}
}
if (!commit_leases())
diff --git a/usr.sbin/dhcpd/sync.c b/usr.sbin/dhcpd/sync.c
new file mode 100644
index 00000000000..58c776608c1
--- /dev/null
+++ b/usr.sbin/dhcpd/sync.c
@@ -0,0 +1,465 @@
+/* $OpenBSD: sync.c,v 1.1 2008/05/07 12:19:20 beck Exp $ */
+
+/*
+ * Copyright (c) 2008 Bob Beck <beck@openbsd.org>
+ * Copyright (c) 2006, 2007 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/param.h>
+#include <sys/stdint.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sha1.h>
+#include <syslog.h>
+
+#include <netdb.h>
+
+#include <openssl/hmac.h>
+
+#include "dhcpd.h"
+#include "sync.h"
+
+int sync_debug;
+
+u_int32_t sync_counter;
+int syncfd;
+int sendmcast;
+
+struct sockaddr_in sync_in;
+struct sockaddr_in sync_out;
+static char *sync_key;
+
+struct sync_host {
+ LIST_ENTRY(sync_host) h_entry;
+
+ char *h_name;
+ struct sockaddr_in sh_addr;
+};
+LIST_HEAD(synchosts, sync_host) sync_hosts = LIST_HEAD_INITIALIZER(sync_hosts);
+
+void sync_send(struct iovec *, int);
+void sync_addr(time_t, time_t, char *, u_int16_t);
+
+int
+sync_addhost(const char *name, u_short port)
+{
+ struct addrinfo hints, *res, *res0;
+ struct sync_host *shost;
+ struct sockaddr_in *addr = NULL;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(name, NULL, &hints, &res0) != 0)
+ return (EINVAL);
+ for (res = res0; res != NULL; res = res->ai_next) {
+ if (addr == NULL && res->ai_family == AF_INET) {
+ addr = (struct sockaddr_in *)res->ai_addr;
+ break;
+ }
+ }
+ if (addr == NULL) {
+ freeaddrinfo(res0);
+ return (EINVAL);
+ }
+ if ((shost = (struct sync_host *)
+ calloc(1, sizeof(struct sync_host))) == NULL) {
+ freeaddrinfo(res0);
+ return (ENOMEM);
+ }
+ if ((shost->h_name = strdup(name)) == NULL) {
+ free(shost);
+ freeaddrinfo(res0);
+ return (ENOMEM);
+ }
+
+ shost->sh_addr.sin_family = AF_INET;
+ shost->sh_addr.sin_port = htons(port);
+ shost->sh_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
+ freeaddrinfo(res0);
+
+ LIST_INSERT_HEAD(&sync_hosts, shost, h_entry);
+
+ if (sync_debug)
+ syslog_r(LOG_DEBUG, &sdata, "added dhcp sync host %s "
+ "(address %s, port %d)\n", shost->h_name,
+ inet_ntoa(shost->sh_addr.sin_addr), port);
+
+ return (0);
+}
+
+int
+sync_init(const char *iface, const char *baddr, u_short port)
+{
+ int one = 1;
+ u_int8_t ttl;
+ struct ifreq ifr;
+ struct ip_mreq mreq;
+ struct sockaddr_in *addr;
+ char ifnam[IFNAMSIZ], *ttlstr;
+ const char *errstr;
+ struct in_addr ina;
+
+ if (iface != NULL)
+ sendmcast++;
+
+ bzero(&ina, sizeof(ina));
+ if (baddr != NULL) {
+ if (inet_pton(AF_INET, baddr, &ina) != 1) {
+ ina.s_addr = htonl(INADDR_ANY);
+ if (iface == NULL)
+ iface = baddr;
+ else if (iface != NULL && strcmp(baddr, iface) != 0) {
+ fprintf(stderr, "multicast interface does "
+ "not match");
+ return (-1);
+ }
+ }
+ }
+
+ sync_key = SHA1File(DHCP_SYNC_KEY, NULL);
+ if (sync_key == NULL) {
+ if (errno != ENOENT) {
+ fprintf(stderr, "failed to open sync key: %s\n",
+ strerror(errno));
+ return (-1);
+ }
+ /* Use empty key by default */
+ sync_key = "";
+ }
+
+ syncfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (syncfd == -1)
+ return (-1);
+
+ if (setsockopt(syncfd, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(one)) == -1)
+ goto fail;
+
+ bzero(&sync_out, sizeof(sync_out));
+ sync_out.sin_family = AF_INET;
+ sync_out.sin_len = sizeof(sync_out);
+ sync_out.sin_addr.s_addr = ina.s_addr;
+ if (baddr == NULL && iface == NULL)
+ sync_out.sin_port = 0;
+ else
+ sync_out.sin_port = htons(port);
+
+ if (bind(syncfd, (struct sockaddr *)&sync_out, sizeof(sync_out)) == -1)
+ goto fail;
+
+ /* Don't use multicast messages */
+ if (iface == NULL)
+ return (syncfd);
+
+ strlcpy(ifnam, iface, sizeof(ifnam));
+ ttl = DHCP_SYNC_MCASTTTL;
+ if ((ttlstr = strchr(ifnam, ':')) != NULL) {
+ *ttlstr++ = '\0';
+ ttl = (u_int8_t)strtonum(ttlstr, 1, UINT8_MAX, &errstr);
+ if (errstr) {
+ fprintf(stderr, "invalid multicast ttl %s: %s",
+ ttlstr, errstr);
+ goto fail;
+ }
+ }
+
+ bzero(&ifr, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
+ if (ioctl(syncfd, SIOCGIFADDR, &ifr) == -1)
+ goto fail;
+
+ bzero(&sync_in, sizeof(sync_in));
+ addr = (struct sockaddr_in *)&ifr.ifr_addr;
+ sync_in.sin_family = AF_INET;
+ sync_in.sin_len = sizeof(sync_in);
+ sync_in.sin_addr.s_addr = addr->sin_addr.s_addr;
+ sync_in.sin_port = htons(port);
+
+ bzero(&mreq, sizeof(mreq));
+ sync_out.sin_addr.s_addr = inet_addr(DHCP_SYNC_MCASTADDR);
+ mreq.imr_multiaddr.s_addr = inet_addr(DHCP_SYNC_MCASTADDR);
+ mreq.imr_interface.s_addr = sync_in.sin_addr.s_addr;
+
+ if (setsockopt(syncfd, IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
+ fprintf(stderr, "failed to add multicast membership to %s: %s",
+ DHCP_SYNC_MCASTADDR, strerror(errno));
+ goto fail;
+ }
+ if (setsockopt(syncfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof(ttl)) < 0) {
+ fprintf(stderr, "failed to set multicast ttl to "
+ "%u: %s\n", ttl, strerror(errno));
+ setsockopt(syncfd, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
+ goto fail;
+ }
+
+ if (sync_debug)
+ syslog_r(LOG_DEBUG, &sdata, "using multicast dhcp sync %smode "
+ "(ttl %u, group %s, port %d)\n",
+ sendmcast ? "" : "receive ",
+ ttl, inet_ntoa(sync_out.sin_addr), port);
+
+ return (syncfd);
+
+ fail:
+ close(syncfd);
+ return (-1);
+}
+
+void
+sync_recv(void)
+{
+ struct dhcp_synchdr *hdr;
+ struct sockaddr_in addr;
+ struct dhcp_synctlv_hdr *tlv;
+ struct dhcp_synctlv_lease *lv;
+ struct lease *lease;
+ u_int8_t buf[DHCP_SYNC_MAXSIZE];
+ u_int8_t hmac[2][DHCP_SYNC_HMAC_LEN];
+ struct lease l, *lp;
+ u_int8_t *p;
+ socklen_t addr_len;
+ ssize_t len;
+ u_int hmac_len;
+
+ bzero(&addr, sizeof(addr));
+ bzero(buf, sizeof(buf));
+
+ addr_len = sizeof(addr);
+ if ((len = recvfrom(syncfd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&addr, &addr_len)) < 1)
+ return;
+ if (addr.sin_addr.s_addr != htonl(INADDR_ANY) &&
+ bcmp(&sync_in.sin_addr, &addr.sin_addr,
+ sizeof(addr.sin_addr)) == 0)
+ return;
+
+ /* Ignore invalid or truncated packets */
+ hdr = (struct dhcp_synchdr *)buf;
+ if (len < sizeof(struct dhcp_synchdr) ||
+ hdr->sh_version != DHCP_SYNC_VERSION ||
+ hdr->sh_af != AF_INET ||
+ len < ntohs(hdr->sh_length))
+ goto trunc;
+ len = ntohs(hdr->sh_length);
+
+ /* Compute and validate HMAC */
+ bcopy(hdr->sh_hmac, hmac[0], DHCP_SYNC_HMAC_LEN);
+ bzero(hdr->sh_hmac, DHCP_SYNC_HMAC_LEN);
+ HMAC(EVP_sha1(), sync_key, strlen(sync_key), buf, len,
+ hmac[1], &hmac_len);
+ if (bcmp(hmac[0], hmac[1], DHCP_SYNC_HMAC_LEN) != 0)
+ goto trunc;
+
+ if (sync_debug)
+ syslog_r(LOG_DEBUG, &sdata,
+ "%s(sync): received packet of %d bytes\n",
+ inet_ntoa(addr.sin_addr), (int)len);
+
+ p = (u_int8_t *)(hdr + 1);
+ while (len) {
+ tlv = (struct dhcp_synctlv_hdr *)p;
+
+ if (len < sizeof(struct dhcp_synctlv_hdr) ||
+ len < ntohs(tlv->st_length))
+ goto trunc;
+
+ switch (ntohs(tlv->st_type)) {
+ case DHCP_SYNC_LEASE:
+ lv = (struct dhcp_synctlv_lease *)tlv;
+ if (sizeof(*lv) > ntohs(tlv->st_length))
+ goto trunc;
+ if ((lease = find_lease_by_hw_addr(
+ lv->hardware_addr.haddr,
+ lv->hardware_addr.hlen)) == NULL) {
+ if ((lease = find_lease_by_hw_addr(
+ lv->hardware_addr.haddr,
+ lv->hardware_addr.hlen)) == NULL) {
+ lp = &l;
+ memset(lp, 0, sizeof(*lp));
+ } else
+ lp = lease;
+ } else
+ lp = lease;
+
+ lp = &l;
+ memset(lp, 0, sizeof(*lp));
+ lp->timestamp = ntohl(lv->timestamp);
+ lp->starts = ntohl(lv->starts);
+ lp->ends = ntohl(lv->ends);
+ memcpy(&lp->ip_addr, &lv->ip_addr,
+ sizeof(lp->ip_addr));
+ memcpy(&lp->hardware_addr, &lv->hardware_addr,
+ sizeof(lp->hardware_addr));
+ syslog_r(LOG_DEBUG, &sdata,
+ "DHCP_SYNC_LEASE from %s for hw %s -> ip %s, "
+ "start %d, end %d",
+ inet_ntoa(addr.sin_addr),
+ print_hw_addr(lp->hardware_addr.htype,
+ lp->hardware_addr.hlen,
+ lp->hardware_addr.haddr),
+ piaddr(lp->ip_addr),
+ lp->starts,
+ lp->ends);
+ /* now whack the lease in there */
+ if (lease == NULL) {
+ enter_lease(lp);
+ write_leases();
+ }
+ else if (lease->ends < lp->ends)
+ supersede_lease(lease, lp, 1);
+ else if (lease->ends > lp->ends)
+ /*
+ * our partner sent us a lease
+ * that is older than what we have,
+ * so re-educate them with what we
+ * know is newer.
+ */
+ sync_lease(lease);
+ break;
+ case DHCP_SYNC_END:
+ goto done;
+ default:
+ printf("invalid type: %d\n", ntohs(tlv->st_type));
+ goto trunc;
+ }
+ len -= ntohs(tlv->st_length);
+ p = ((u_int8_t *)tlv) + ntohs(tlv->st_length);
+ }
+
+ done:
+ return;
+
+ trunc:
+ if (sync_debug)
+ syslog_r(LOG_INFO, &sdata,
+ "%s(sync): truncated or invalid packet\n",
+ inet_ntoa(addr.sin_addr));
+}
+
+void
+sync_send(struct iovec *iov, int iovlen)
+{
+ struct sync_host *shost;
+ struct msghdr msg;
+
+ /* setup buffer */
+ bzero(&msg, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iovlen;
+
+ if (sendmcast) {
+ if (sync_debug)
+ syslog_r(LOG_DEBUG, &sdata,
+ "sending multicast sync message\n");
+ msg.msg_name = &sync_out;
+ msg.msg_namelen = sizeof(sync_out);
+ sendmsg(syncfd, &msg, 0);
+ }
+
+ LIST_FOREACH(shost, &sync_hosts, h_entry) {
+ if (sync_debug)
+ syslog_r(LOG_DEBUG, &sdata,
+ "sending sync message to %s (%s)\n",
+ shost->h_name, inet_ntoa(shost->sh_addr.sin_addr));
+ msg.msg_name = &shost->sh_addr;
+ msg.msg_namelen = sizeof(shost->sh_addr);
+ sendmsg(syncfd, &msg, 0);
+ }
+}
+
+void
+sync_lease(struct lease *lease)
+{
+ struct iovec iov[3];
+ struct dhcp_synchdr hdr;
+ struct dhcp_synctlv_lease ld;
+ struct dhcp_synctlv_hdr end;
+ int i = 0;
+ HMAC_CTX ctx;
+ u_int hmac_len;
+
+ bzero(&hdr, sizeof(hdr));
+ bzero(&ld, sizeof(ld));
+
+ HMAC_CTX_init(&ctx);
+ HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1());
+
+ /* Add DHCP sync packet header */
+ hdr.sh_version = DHCP_SYNC_VERSION;
+ hdr.sh_af = AF_INET;
+ hdr.sh_counter = sync_counter++;
+ hdr.sh_length = htons(sizeof(hdr) + sizeof(ld) + sizeof(end));
+ iov[i].iov_base = &hdr;
+ iov[i].iov_len = sizeof(hdr);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ /* Add single DHCP sync address entry */
+ ld.type = htons(DHCP_SYNC_LEASE);
+ ld.length = htons(sizeof(ld));
+ ld.timestamp = htonl(lease->timestamp);
+ ld.starts = htonl(lease->starts);
+ ld.ends = htonl(lease->ends);
+ memcpy(&ld.ip_addr, &lease->ip_addr, sizeof(ld.ip_addr));
+ memcpy(&ld.hardware_addr, &lease->hardware_addr,
+ sizeof(ld.hardware_addr));
+ syslog_r(LOG_DEBUG, &sdata,
+ "sending DHCP_SYNC_LEASE for hw %s -> ip %s, start %d, end %d",
+ print_hw_addr(ld.hardware_addr.htype, ld.hardware_addr.hlen,
+ ld.hardware_addr.haddr),
+ piaddr(lease->ip_addr),
+ ntohl(ld.starts),
+ ntohl(ld.ends));
+ iov[i].iov_base = &ld;
+ iov[i].iov_len = sizeof(ld);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ /* Add end marker */
+ end.st_type = htons(DHCP_SYNC_END);
+ end.st_length = htons(sizeof(end));
+ iov[i].iov_base = &end;
+ iov[i].iov_len = sizeof(end);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ HMAC_Final(&ctx, hdr.sh_hmac, &hmac_len);
+
+ /* Send message to the target hosts */
+ sync_send(iov, i);
+ HMAC_CTX_cleanup(&ctx);
+}
diff --git a/usr.sbin/dhcpd/sync.h b/usr.sbin/dhcpd/sync.h
new file mode 100644
index 00000000000..af564712919
--- /dev/null
+++ b/usr.sbin/dhcpd/sync.h
@@ -0,0 +1,71 @@
+/* $OpenBSD: sync.h,v 1.1 2008/05/07 12:19:20 beck Exp $ */
+
+/*
+ * Copyright (c) 2008, Bob Beck <beck@openbsd.org>
+ * Copyright (c) 2006, 2007 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.
+ */
+
+#ifndef _DHCPD_SYNC
+#define _DHCPD_SYNC
+
+/*
+ * dhcpd(8) synchronisation protocol.
+ *
+ * This protocol has been designed for realtime synchronisation between
+ * multiple machines running dhcpd(8), running the same config.
+ * It is a simple Type-Length-Value based protocol, it allows easy
+ * extension with future subtypes and bulk transfers by sending multiple
+ * entries at once. The unencrypted messages will be authenticated using
+ * HMAC-SHA1.
+ *
+ */
+
+#define DHCP_SYNC_VERSION 1
+#define DHCP_SYNC_MCASTADDR "224.0.1.240" /* XXX choose valid address */
+#define DHCP_SYNC_MCASTTTL IP_DEFAULT_MULTICAST_TTL
+#define DHCP_SYNC_HMAC_LEN 20 /* SHA1 */
+#define DHCP_SYNC_MAXSIZE 1408
+#define DHCP_SYNC_KEY "/var/db/dhcpd.key"
+
+struct dhcp_synchdr {
+ u_int8_t sh_version;
+ u_int8_t sh_af;
+ u_int16_t sh_length;
+ u_int32_t sh_counter;
+ u_int8_t sh_hmac[DHCP_SYNC_HMAC_LEN];
+ u_int16_t sh_pad[2];
+} __packed;
+
+struct dhcp_synctlv_hdr {
+ u_int16_t st_type;
+ u_int16_t st_length;
+} __packed;
+
+struct dhcp_synctlv_lease {
+ u_int16_t type;
+ u_int16_t length;
+ u_int32_t starts, ends, timestamp;
+ struct iaddr ip_addr;
+ struct hardware hardware_addr;
+} __packed;
+
+#define DHCP_SYNC_END 0x0000
+#define DHCP_SYNC_LEASE 0x0001
+
+extern int sync_init(const char *, const char *, u_short);
+extern int sync_addhost(const char *, u_short);
+extern void sync_recv(void);
+extern void sync_lease(struct lease *);
+#endif /* _DHCPD_SYNC */