summary refs log tree commit diff
path: root/src/dhcp-manager
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
committerMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
commit2c032d8f1c6292c1338a615e6ec40252889ba85c (patch)
tree1f77182220b2b0264288ba4a476ab47e5bc48716 /src/dhcp-manager
parent33491bc4279481db8ae47213e34a6d695a0e8830 (diff)
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'src/dhcp-manager')
-rw-r--r--src/dhcp-manager/Makefile.am6
-rw-r--r--src/dhcp-manager/Makefile.in12
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c1524
-rw-r--r--src/dhcp-manager/nm-dhcp-client.h162
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.c152
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.h15
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c228
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.h26
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.c131
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.h22
-rw-r--r--src/dhcp-manager/nm-dhcp-helper.c327
-rw-r--r--src/dhcp-manager/nm-dhcp-listener.c290
-rw-r--r--src/dhcp-manager/nm-dhcp-listener.h39
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c607
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.h68
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.c849
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.h45
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.c732
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.h41
-rw-r--r--src/dhcp-manager/systemd-dhcp/nm-sd-adapt.c209
-rw-r--r--src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h109
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h76
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h87
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c241
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-option.c257
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c204
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h141
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-internal.h81
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-lease-internal.h64
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-network.c198
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-option.c323
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-protocol.h141
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.c484
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.h78
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-client.c1726
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c865
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-client.c1242
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-lease.c223
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/async.h27
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/fileio.c817
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/fileio.h45
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.c294
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h49
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/list.h138
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/macro.h413
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/refcnt.h34
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/siphash24.c137
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/siphash24.h6
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/socket-util.h118
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/sparse-endian.h88
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/strv.c616
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/strv.h137
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/time-util.c1003
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/time-util.h111
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/unaligned.h66
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/utf8.c314
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/utf8.h42
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/util.c7350
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/shared/util.h1038
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/systemd/_sd-common.h78
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-client.h75
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h54
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-client.h66
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-lease.h42
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/systemd/sd-event.h135
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/systemd/sd-id128.h113
-rw-r--r--src/dhcp-manager/tests/Makefile.am28
-rw-r--r--src/dhcp-manager/tests/Makefile.in77
-rw-r--r--src/dhcp-manager/tests/test-dhcp-dhclient.c158
-rw-r--r--src/dhcp-manager/tests/test-dhcp-utils.c723
70 files changed, 24258 insertions, 2159 deletions
diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am
index 63eac9d5..27945f55 100644
--- a/src/dhcp-manager/Makefile.am
+++ b/src/dhcp-manager/Makefile.am
@@ -3,15 +3,15 @@ libexec_PROGRAMS = nm-dhcp-helper
 nm_dhcp_helper_SOURCES = nm-dhcp-helper.c
 
 nm_dhcp_helper_CPPFLAGS = \
-	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
 	-DG_LOG_DOMAIN=\""nm-dhcp-helper"\" \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	-DNMRUNDIR=\"$(nmrundir)\"
 
-nm_dhcp_helper_LDADD = $(DBUS_LIBS)
+nm_dhcp_helper_LDADD = $(GLIB_LIBS)
 
 
-# FIXME: remove when dbus-glib >= 0.100 is required
+# FIXME: remove when dbus-glib >= 0.100 or GDBus is required
 dhcp_helper_conf = nm-dhcp-helper.conf
 
 if !HAVE_DBUS_GLIB_100
diff --git a/src/dhcp-manager/Makefile.in b/src/dhcp-manager/Makefile.in
index 5495b12f..4dea1085 100644
--- a/src/dhcp-manager/Makefile.in
+++ b/src/dhcp-manager/Makefile.in
@@ -212,6 +212,8 @@ AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
@@ -335,6 +337,7 @@ NEWT_CFLAGS = @NEWT_CFLAGS@
 NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -354,6 +357,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -380,7 +384,7 @@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
+UDEV_DIR = @UDEV_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
@@ -461,14 +465,14 @@ with_resolvconf = @with_resolvconf@
 with_valgrind = @with_valgrind@
 nm_dhcp_helper_SOURCES = nm-dhcp-helper.c
 nm_dhcp_helper_CPPFLAGS = \
-	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
 	-DG_LOG_DOMAIN=\""nm-dhcp-helper"\" \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	-DNMRUNDIR=\"$(nmrundir)\"
 
-nm_dhcp_helper_LDADD = $(DBUS_LIBS)
+nm_dhcp_helper_LDADD = $(GLIB_LIBS)
 
-# FIXME: remove when dbus-glib >= 0.100 is required
+# FIXME: remove when dbus-glib >= 0.100 or GDBus is required
 dhcp_helper_conf = nm-dhcp-helper.conf
 @HAVE_DBUS_GLIB_100_FALSE@dbusservicedir = $(DBUS_SYS_DIR)
 @HAVE_DBUS_GLIB_100_FALSE@dbusservice_DATA = $(dhcp_helper_conf)
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index 03c11c74..47fa5317 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -17,7 +17,8 @@
  *
  */
 
-#include <config.h>
+#include "config.h"
+
 #include <glib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -33,35 +34,35 @@
 #include "nm-logging.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-dhcp-client.h"
+#include "nm-dhcp-utils.h"
+#include "nm-platform.h"
 
 typedef struct {
 	char *       iface;
+	int          ifindex;
 	GByteArray * hwaddr;
 	gboolean     ipv6;
 	char *       uuid;
-	guint        priority;
+	guint32      priority;
 	guint32      timeout;
 	GByteArray * duid;
+	GBytes *     client_id;
+	char *       hostname;
 
-	guchar       state;
-	GPid         pid;
-	gboolean     dead;
+	NMDhcpState  state;
+	pid_t        pid;
 	guint        timeout_id;
 	guint        watch_id;
-	guint32      remove_id;
-	GHashTable * options;
 	gboolean     info_only;
 
-} NMDHCPClientPrivate;
+} NMDhcpClientPrivate;
 
-#define NM_DHCP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_CLIENT, NMDHCPClientPrivate))
+#define NM_DHCP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_CLIENT, NMDhcpClientPrivate))
 
-G_DEFINE_TYPE_EXTENDED (NMDHCPClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
+G_DEFINE_TYPE_EXTENDED (NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
 
 enum {
 	SIGNAL_STATE_CHANGED,
-	SIGNAL_TIMEOUT,
-	SIGNAL_REMOVE,
 	LAST_SIGNAL
 };
 
@@ -70,6 +71,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 enum {
 	PROP_0,
 	PROP_IFACE,
+	PROP_IFINDEX,
 	PROP_HWADDR,
 	PROP_IPV6,
 	PROP_UUID,
@@ -80,8 +82,8 @@ enum {
 
 /********************************************/
 
-GPid
-nm_dhcp_client_get_pid (NMDHCPClient *self)
+pid_t
+nm_dhcp_client_get_pid (NMDhcpClient *self)
 {
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);
 
@@ -89,15 +91,23 @@ nm_dhcp_client_get_pid (NMDHCPClient *self)
 }
 
 const char *
-nm_dhcp_client_get_iface (NMDHCPClient *self)
+nm_dhcp_client_get_iface (NMDhcpClient *self)
 {
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
 
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface;
 }
 
+int
+nm_dhcp_client_get_ifindex (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->ifindex;
+}
+
 gboolean
-nm_dhcp_client_get_ipv6 (NMDHCPClient *self)
+nm_dhcp_client_get_ipv6 (NMDhcpClient *self)
 {
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
 
@@ -105,19 +115,120 @@ nm_dhcp_client_get_ipv6 (NMDHCPClient *self)
 }
 
 const char *
-nm_dhcp_client_get_uuid (NMDHCPClient *self)
+nm_dhcp_client_get_uuid (NMDhcpClient *self)
 {
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
 
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid;
 }
 
+const GByteArray *
+nm_dhcp_client_get_duid (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->duid;
+}
+
+const GByteArray *
+nm_dhcp_client_get_hw_addr (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hwaddr;
+}
+
+guint32
+nm_dhcp_client_get_priority (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), G_MAXUINT32);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->priority;
+}
+
+GBytes *
+nm_dhcp_client_get_client_id (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->client_id;
+}
+
+void
+nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id)
+{
+	NMDhcpClientPrivate *priv;
+
+	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	if (priv->client_id && client_id && g_bytes_equal (priv->client_id, client_id))
+		return;
+	g_clear_pointer (&priv->client_id, g_bytes_unref);
+	priv->client_id = client_id ? g_bytes_ref (client_id) : NULL;
+}
+
+const char *
+nm_dhcp_client_get_hostname (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
+}
+
+/********************************************/
+
+static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
+	[NM_DHCP_STATE_UNKNOWN]  = "unknown",
+	[NM_DHCP_STATE_BOUND]    = "bound",
+	[NM_DHCP_STATE_TIMEOUT]  = "timeout",
+	[NM_DHCP_STATE_EXPIRE]   = "expire",
+	[NM_DHCP_STATE_DONE]     = "done",
+	[NM_DHCP_STATE_FAIL]     = "fail",
+};
+
+static const char *
+state_to_string (NMDhcpState state)
+{
+	if ((gsize) state < G_N_ELEMENTS (state_table))
+		return state_table[state];
+	return NULL;
+}
+
+static NMDhcpState
+reason_to_state (const char *iface, const char *reason)
+{
+	if (g_ascii_strcasecmp (reason, "bound") == 0 ||
+	    g_ascii_strcasecmp (reason, "bound6") == 0 ||
+	    g_ascii_strcasecmp (reason, "renew") == 0 ||
+	    g_ascii_strcasecmp (reason, "renew6") == 0 ||
+	    g_ascii_strcasecmp (reason, "reboot") == 0 ||
+	    g_ascii_strcasecmp (reason, "rebind") == 0 ||
+	    g_ascii_strcasecmp (reason, "rebind6") == 0)
+		return NM_DHCP_STATE_BOUND;
+	else if (g_ascii_strcasecmp (reason, "timeout") == 0)
+		return NM_DHCP_STATE_TIMEOUT;
+	else if (g_ascii_strcasecmp (reason, "nak") == 0 ||
+	         g_ascii_strcasecmp (reason, "expire") == 0 ||
+	         g_ascii_strcasecmp (reason, "expire6") == 0)
+		return NM_DHCP_STATE_EXPIRE;
+	else if (g_ascii_strcasecmp (reason, "end") == 0)
+		return NM_DHCP_STATE_DONE;
+	else if (g_ascii_strcasecmp (reason, "fail") == 0 ||
+	         g_ascii_strcasecmp (reason, "abend") == 0)
+		return NM_DHCP_STATE_FAIL;
+
+	nm_log_dbg (LOGD_DHCP, "(%s): unmapped DHCP state '%s'", iface, reason);
+	return NM_DHCP_STATE_UNKNOWN;
+}
+
 /********************************************/
 
 static void
-timeout_cleanup (NMDHCPClient *self)
+timeout_cleanup (NMDhcpClient *self)
 {
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 
 	if (priv->timeout_id) {
 		g_source_remove (priv->timeout_id);
@@ -126,9 +237,9 @@ timeout_cleanup (NMDHCPClient *self)
 }
 
 static void
-watch_cleanup (NMDHCPClient *self)
+watch_cleanup (NMDhcpClient *self)
 {
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 
 	if (priv->watch_id) {
 		g_source_remove (priv->watch_id);
@@ -137,171 +248,158 @@ watch_cleanup (NMDHCPClient *self)
 }
 
 void
-nm_dhcp_client_stop_pid (GPid pid, const char *iface)
+nm_dhcp_client_stop_pid (pid_t pid, const char *iface)
 {
-	int i = 5;  /* roughly 0.5 seconds */
-
-	g_return_if_fail (pid > 0);
-
-	/* Tell it to quit; maybe it wants to send out a RELEASE message */
-	kill (pid, SIGTERM);
-
-	while (i-- > 0) {
-		gint child_status;
-		int ret;
+	char *name = iface ? g_strdup_printf ("dhcp-client-%s", iface) : NULL;
 
-		ret = waitpid (pid, &child_status, WNOHANG);
-		if (ret > 0)
-			break;
+	g_return_if_fail (pid > 1);
 
-		if (ret == -1) {
-			/* Child already exited */
-			if (errno == ECHILD) {
-				/* Was it really our child and it exited? */
-				if (kill (pid, 0) < 0 && errno == ESRCH)
-					break;
-			} else {
-				/* Took too long; shoot it in the head */
-				i = 0;
-				break;
-			}
-		}
-		g_usleep (G_USEC_PER_SEC / 10);
-	}
-
-	if (i <= 0) {
-		if (iface) {
-			nm_log_warn (LOGD_DHCP, "(%s): DHCP client pid %d didn't exit, will kill it.",
-			             iface, pid);
-		}
-		kill (pid, SIGKILL);
-
-		nm_log_dbg (LOGD_DHCP, "waiting for DHCP client pid %d to exit", pid);
-		waitpid (pid, NULL, 0);
-		nm_log_dbg (LOGD_DHCP, "DHCP client pid %d cleaned up", pid);
-	}
+	nm_utils_kill_child_sync (pid, SIGTERM, LOGD_DHCP, name ? name : "dhcp-client", NULL,
+	                          1000 / 2, 1000 / 20);
+	g_free (name);
 }
 
 static void
-stop (NMDHCPClient *self, gboolean release, const GByteArray *duid)
+stop (NMDhcpClient *self, gboolean release, const GByteArray *duid)
 {
-	NMDHCPClientPrivate *priv;
+	NMDhcpClientPrivate *priv;
 
 	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
 
 	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-	g_return_if_fail (priv->pid > 0);
-
-	/* Clean up the watch handler since we're explicitly killing the daemon */
-	watch_cleanup (self);
-
-	nm_dhcp_client_stop_pid (priv->pid, priv->iface);
 
+	if (priv->pid > 0) {
+		/* Clean up the watch handler since we're explicitly killing the daemon */
+		watch_cleanup (self);
+		nm_dhcp_client_stop_pid (priv->pid, priv->iface);
+	}
+	priv->pid = -1;
 	priv->info_only = FALSE;
 }
 
-static gboolean
-daemon_timeout (gpointer user_data)
+void
+nm_dhcp_client_set_state (NMDhcpClient *self,
+                          NMDhcpState new_state,
+                          GObject *ip_config,
+                          GHashTable *options)
 {
-	NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 
-	if (priv->ipv6) {
-		nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 request timed out.", priv->iface);
+	if (new_state >= NM_DHCP_STATE_BOUND)
+		timeout_cleanup (self);
+	if (new_state >= NM_DHCP_STATE_TIMEOUT)
+		watch_cleanup (self);
+
+	if (new_state == NM_DHCP_STATE_BOUND) {
+		g_assert (   (priv->ipv6 && NM_IS_IP6_CONFIG (ip_config))
+		          || (!priv->ipv6 && NM_IS_IP4_CONFIG (ip_config)));
+		g_assert (options);
+		g_assert_cmpint (g_hash_table_size (options), >, 0);
 	} else {
-		nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 request timed out.", priv->iface);
+		g_assert (ip_config == NULL);
+		g_assert (options == NULL);
 	}
-	g_signal_emit (G_OBJECT (self), signals[SIGNAL_TIMEOUT], 0);
-	return FALSE;
-}
 
-static gboolean
-signal_remove (gpointer user_data)
-{
-	NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
+	/* The client may send same-state transitions for RENEW/REBIND events and
+	 * the lease may have changed, so handle same-state transitions for the
+	 * BOUND state.  Ignore same-state transitions for other events since
+	 * the lease won't have changed and the state was already handled.
+	 */
+	if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND))
+		return;
 
-	NM_DHCP_CLIENT_GET_PRIVATE (self)->remove_id = 0;
-	g_signal_emit (G_OBJECT (self), signals[SIGNAL_REMOVE], 0);
-	return FALSE;
+	nm_log_info (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4,
+	             "(%s): DHCPv%c state changed %s -> %s",
+	             priv->iface,
+	             priv->ipv6 ? '6' : '4',
+	             state_to_string (priv->state),
+	             state_to_string (new_state));
+
+	priv->state = new_state;
+	g_signal_emit (G_OBJECT (self),
+	               signals[SIGNAL_STATE_CHANGED], 0,
+	               new_state,
+	               ip_config,
+	               options);
 }
 
-static void
-dhcp_client_set_state (NMDHCPClient *self,
-                       NMDHCPState state,
-                       gboolean emit_state,
-                       gboolean remove_now)
+static gboolean
+daemon_timeout (gpointer user_data)
 {
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	NMDhcpClient *self = NM_DHCP_CLIENT (user_data);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 
-	priv->state = state;
-
-	if (emit_state)
-		g_signal_emit (G_OBJECT (self), signals[SIGNAL_STATE_CHANGED], 0, priv->state);
-
-	if (state == DHC_END || state == DHC_ABEND) {
-		/* Start the remove signal timer */
-		if (remove_now) {
-			g_signal_emit (G_OBJECT (self), signals[SIGNAL_REMOVE], 0);
-		} else {
-			if (!priv->remove_id)
-				priv->remove_id = g_timeout_add_seconds (5, signal_remove, self);
-		}
-	}
+	priv->timeout_id = 0;
+	nm_log_warn (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4,
+	             "(%s): DHCPv%c request timed out.",
+	             priv->iface,
+	             priv->ipv6 ? '6' : '4');
+	nm_dhcp_client_set_state (self, NM_DHCP_STATE_TIMEOUT, NULL, NULL);
+	return G_SOURCE_REMOVE;
 }
 
 static void
 daemon_watch_cb (GPid pid, gint status, gpointer user_data)
 {
-	NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-	NMDHCPState new_state;
-
-	if (priv->ipv6) {
-		nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 client pid %d exited with status %d",
-		             priv->iface, pid,
-		             WIFEXITED (status) ? WEXITSTATUS (status) : -1);
-	} else {
-		nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 client pid %d exited with status %d",
-		             priv->iface, pid,
-		             WIFEXITED (status) ? WEXITSTATUS (status) : -1);
-	}
-
-	if (!WIFEXITED (status)) {
-		new_state = DHC_ABEND;
+	NMDhcpClient *self = NM_DHCP_CLIENT (user_data);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	NMDhcpState new_state;
+	guint64 log_domain;
+	guint ip_ver;
+
+	log_domain = priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4;
+	ip_ver = priv->ipv6 ? 6 : 4;
+
+	if (WIFEXITED (status))
+		nm_log_info (log_domain, "(%s): DHCPv%d client pid %d exited with status %d",
+		             priv->iface, ip_ver, pid, WEXITSTATUS (status));
+	else if (WIFSIGNALED (status))
+		nm_log_info (log_domain, "(%s): DHCPv%d client pid %d killed by signal %d",
+		             priv->iface, ip_ver, pid, WTERMSIG (status));
+	else if (WIFSTOPPED(status))
+		nm_log_info (log_domain, "(%s): DHCPv%d client pid %d stopped by signal %d",
+		             priv->iface, ip_ver, pid, WSTOPSIG (status));
+	else if (WIFCONTINUED (status))
+		nm_log_info (log_domain, "(%s): DHCPv%d client pid %d resumed (by SIGCONT)",
+		             priv->iface, ip_ver, pid);
+	else
 		nm_log_warn (LOGD_DHCP, "DHCP client died abnormally");
-	} else
-		new_state = DHC_END;
 
-	watch_cleanup (self);
-	timeout_cleanup (self);
-	priv->dead = TRUE;
+	if (!WIFEXITED (status))
+		new_state = NM_DHCP_STATE_FAIL;
+	else
+		new_state = NM_DHCP_STATE_DONE;
+
+	priv->pid = -1;
 
-	dhcp_client_set_state (self, new_state, TRUE, FALSE);
+	nm_dhcp_client_set_state (self, new_state, NULL, NULL);
 }
 
-static void
-start_monitor (NMDHCPClient *self)
+void
+nm_dhcp_client_watch_child (NMDhcpClient *self, pid_t pid)
 {
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 
-	g_return_if_fail (priv->pid > 0);
+	g_return_if_fail (priv->pid == -1);
+	priv->pid = pid;
 
 	/* Set up a timeout on the transaction to kill it after the timeout */
+	g_assert (priv->timeout_id == 0);
 	priv->timeout_id = g_timeout_add_seconds (priv->timeout,
 	                                          daemon_timeout,
 	                                          self);
-	priv->watch_id = g_child_watch_add (priv->pid,
-	                                    (GChildWatchFunc) daemon_watch_cb,
-	                                    self);
+	g_assert (priv->watch_id == 0);
+	priv->watch_id = g_child_watch_add (pid, daemon_watch_cb, self);
 }
 
 gboolean
-nm_dhcp_client_start_ip4 (NMDHCPClient *self,
+nm_dhcp_client_start_ip4 (NMDhcpClient *self,
                           const char *dhcp_client_id,
-                          GByteArray *dhcp_anycast_addr,
-                          const char *hostname)
+                          const char *dhcp_anycast_addr,
+                          const char *hostname,
+                          const char *last_ip4_address)
 {
-	NMDHCPClientPrivate *priv;
+	NMDhcpClientPrivate *priv;
 
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
 
@@ -313,11 +411,12 @@ nm_dhcp_client_start_ip4 (NMDHCPClient *self,
 	nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)",
 	             priv->iface, priv->timeout);
 
-	priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_client_id, dhcp_anycast_addr, hostname);
-	if (priv->pid)
-		start_monitor (self);
+	nm_dhcp_client_set_client_id (self, dhcp_client_id ? nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id) : NULL);
 
-	return priv->pid ? TRUE : FALSE;
+	g_clear_pointer (&priv->hostname, g_free);
+	priv->hostname = g_strdup (hostname);
+
+	return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address);
 }
 
 /* uuid_parse does not work for machine-id, so we use our own converter */
@@ -406,38 +505,21 @@ generate_duid_from_machine_id (void)
 	return duid;
 }
 
-static char *
-escape_duid (const GByteArray *duid)
-{
-	guint32 i = 0;
-	GString *s;
-
-	g_return_val_if_fail (duid != NULL, NULL);
-
-	s = g_string_sized_new (40);
-	while (i < duid->len) {
-		if (s->len)
-			g_string_append_c (s, ':');
-		g_string_append_printf (s, "%02x", duid->data[i++]);
-	}
-	return g_string_free (s, FALSE);
-}
-
 static GByteArray *
-get_duid (NMDHCPClient *self)
+get_duid (NMDhcpClient *self)
 {
 	static GByteArray *duid = NULL;
 	GByteArray *copy = NULL;
-	char *escaped;
+	char *str;
 
 	if (G_UNLIKELY (duid == NULL)) {
 		duid = generate_duid_from_machine_id ();
 		g_assert (duid);
 
 		if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
-			escaped = escape_duid (duid);
-			nm_log_dbg (LOGD_DHCP6, "Generated DUID %s", escaped);
-			g_free (escaped);
+			str = nm_dhcp_utils_duid_to_string (duid);
+			nm_log_dbg (LOGD_DHCP6, "Generated DUID %s", str);
+			g_free (str);
 		}
 	}
 
@@ -450,13 +532,14 @@ get_duid (NMDHCPClient *self)
 }
 
 gboolean
-nm_dhcp_client_start_ip6 (NMDHCPClient *self,
-                          GByteArray *dhcp_anycast_addr,
+nm_dhcp_client_start_ip6 (NMDhcpClient *self,
+                          const char *dhcp_anycast_addr,
                           const char *hostname,
-                          gboolean info_only)
+                          gboolean info_only,
+                          NMSettingIP6ConfigPrivacy privacy)
 {
-	NMDHCPClientPrivate *priv;
-	char *escaped;
+	NMDhcpClientPrivate *priv;
+	char *str;
 
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
 
@@ -472,25 +555,24 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self,
 		priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self);
 
 	if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP)) {
-		escaped = escape_duid (priv->duid);
-		nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, escaped);
-		g_free (escaped);
+		str = nm_dhcp_utils_duid_to_string (priv->duid);
+		nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, str);
+		g_free (str);
 	}
 
+	g_clear_pointer (&priv->hostname, g_free);
+	priv->hostname = g_strdup (hostname);
+
 	priv->info_only = info_only;
 
 	nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
 	             priv->iface, priv->timeout);
 
-	priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
-	                                                        dhcp_anycast_addr,
-	                                                        hostname,
-	                                                        info_only,
-	                                                        priv->duid);
-	if (priv->pid > 0)
-		start_monitor (self);
-
-	return priv->pid ? TRUE : FALSE;
+	return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
+	                                                   dhcp_anycast_addr,
+	                                                   info_only,
+	                                                   privacy,
+	                                                   priv->duid);
 }
 
 void
@@ -506,11 +588,14 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
 	errno = 0;
 	tmp = strtol (pid_contents, NULL, 10);
 	if ((errno == 0) && (tmp > 1)) {
+		guint64 start_time;
 		const char *exe;
 
 		/* Ensure the process is a DHCP client */
+		start_time = nm_utils_get_start_time_for_pid (tmp);
 		proc_path = g_strdup_printf ("/proc/%ld/cmdline", tmp);
-		if (g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) {
+		if (   start_time
+		    && g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) {
 			exe = strrchr (proc_contents, '/');
 			if (exe)
 				exe++;
@@ -518,7 +603,8 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
 				exe = proc_contents;
 
 			if (!strcmp (exe, binary_name))
-				nm_dhcp_client_stop_pid ((GPid) tmp, NULL);
+				nm_utils_kill_process_sync (tmp, start_time, SIGTERM, LOGD_DHCP,
+				                            "dhcp-client", 1000 / 2, 1000 / 20);
 		}
 	}
 
@@ -531,103 +617,30 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
 }
 
 void
-nm_dhcp_client_stop (NMDHCPClient *self, gboolean release)
+nm_dhcp_client_stop (NMDhcpClient *self, gboolean release)
 {
-	NMDHCPClientPrivate *priv;
+	NMDhcpClientPrivate *priv;
+	pid_t old_pid = 0;
 
 	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
 
 	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 
 	/* Kill the DHCP client */
-	if (!priv->dead) {
-		NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid);
-		priv->dead = TRUE;
-
+	old_pid = priv->pid;
+	NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid);
+	if (old_pid > 0) {
 		nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d",
-		             priv->iface, priv->pid);
-	}
-
-	/* And clean stuff up */
-
-	priv->pid = -1;
-	dhcp_client_set_state (self, DHC_END, FALSE, TRUE);
-
-	g_hash_table_remove_all (priv->options);
+		             priv->iface, old_pid);
+	} else
+		nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction", priv->iface);
+	g_assert (priv->pid == -1);
 
-	timeout_cleanup (self);
-	watch_cleanup (self);
+	nm_dhcp_client_set_state (self, NM_DHCP_STATE_DONE, NULL, NULL);
 }
 
 /********************************************/
 
-static gboolean
-state_is_bound (guint32 state)
-{
-	if (   (state == DHC_BOUND4)
-	    || (state == DHC_BOUND6)
-	    || (state == DHC_RENEW4)
-	    || (state == DHC_RENEW6)
-	    || (state == DHC_REBOOT)
-	    || (state == DHC_REBIND4)
-	    || (state == DHC_REBIND6)
-	    || (state == DHC_IPV4LL))
-		return TRUE;
-
-	return FALSE;
-}
-
-static const char *state_table[] = {
-	[DHC_NBI]             = "nbi",
-	[DHC_PREINIT]         = "preinit",
-	[DHC_PREINIT6]        = "preinit6",
-	[DHC_BOUND4]          = "bound",
-	[DHC_BOUND6]          = "bound6",
-	[DHC_IPV4LL]          = "ipv4ll",
-	[DHC_RENEW4]          = "renew",
-	[DHC_RENEW6]          = "renew6",
-	[DHC_REBOOT]          = "reboot",
-	[DHC_REBIND4]         = "rebind",
-	[DHC_REBIND6]         = "rebind6",
-	[DHC_DEPREF6]         = "depref6",
-	[DHC_STOP]            = "stop",
-	[DHC_STOP6]           = "stop6",
-	[DHC_MEDIUM]          = "medium",
-	[DHC_TIMEOUT]         = "timeout",
-	[DHC_FAIL]            = "fail",
-	[DHC_EXPIRE]          = "expire",
-	[DHC_EXPIRE6]         = "expire6",
-	[DHC_RELEASE]         = "release",
-	[DHC_RELEASE6]        = "release6",
-	[DHC_START]           = "start",
-	[DHC_ABEND]           = "abend",
-	[DHC_END]             = "end",
-};
-
-static const char *
-state_to_string (NMDHCPState state)
-{
-	if (state >= 0 && state < G_N_ELEMENTS (state_table))
-		return state_table[state];
-	return NULL;
-}
-
-static NMDHCPState
-string_to_state (const char *name)
-{
-	int i;
-
-	if (name) {
-		for (i = 0; i < G_N_ELEMENTS (state_table); i++) {
-			const char *n = state_table[i];
-
-			if (n && !strcasecmp (name, n))
-				return i;
-		}
-	}
-	return 255;
-}
-
 static char *
 garray_to_string (GArray *array, const char *key)
 {
@@ -661,848 +674,133 @@ garray_to_string (GArray *array, const char *key)
 	return converted;
 }
 
+#define OLD_TAG "old_"
+#define NEW_TAG "new_"
+
 static void
-copy_option (gpointer key,
-             gpointer value,
+copy_option (const char * key,
+             GValue *value,
              gpointer user_data)
 {
 	GHashTable *hash = user_data;
-	const char *str_key = (const char *) key;
 	char *str_value = NULL;
+	const char **p;
+	static const char *ignored_keys[] = {
+		"interface",
+		"pid",
+		"reason",
+		"dhcp_message_type",
+		NULL
+	};
 
-	if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
-		nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
-		             "DBUS_TYPE_G_UCHAR_ARRAY",
-		             str_key);
+	if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY)) {
+		nm_log_warn (LOGD_DHCP, "key %s value type was not DBUS_TYPE_G_UCHAR_ARRAY", key);
 		return;
 	}
 
-	str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key);
-	if (str_value)
-		g_hash_table_insert (hash, g_strdup (str_key), str_value);
-}
-
-void
-nm_dhcp_client_new_options (NMDHCPClient *self,
-                            GHashTable *options,
-                            const char *reason)
-{
-	NMDHCPClientPrivate *priv;
-	guint32 old_state;
-	guint32 new_state;
-
-	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
-	g_return_if_fail (options != NULL);
-	g_return_if_fail (reason != NULL);
+	if (g_str_has_prefix (key, OLD_TAG))
+		return;
 
-	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-	old_state = priv->state;
-	new_state = string_to_state (reason);
-
-	/* Clear old and save new DHCP options */
-	g_hash_table_remove_all (priv->options);
-	g_hash_table_foreach (options, copy_option, priv->options);
-
-	if (old_state == new_state) {
-		/* dhclient will stay in the same state (or, really, provide the same
-		 * reason) for operations like RENEW and REBIND.  We need to ensure
-		 * that triggers various DHCP lease change code, so we need to pass
-		 * along same-state transitions for these states.
-		 */
-		if (   new_state != DHC_BOUND4
-		    && new_state != DHC_RENEW4
-		    && new_state != DHC_REBIND4
-		    && new_state != DHC_BOUND6
-		    && new_state != DHC_RENEW6
-		    && new_state != DHC_REBIND6)
+	/* Filter out stuff that's not actually new DHCP options */
+	for (p = ignored_keys; *p; p++) {
+		if (!strcmp (*p, key))
 			return;
 	}
 
-	/* Handle changed device state */
-	if (state_is_bound (new_state)) {
-		/* Cancel the timeout if the DHCP client is now bound */
-		timeout_cleanup (self);
-	}
-
-	if (priv->ipv6) {
-		nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 state changed %s -> %s",
-		            priv->iface,
-		            state_to_string (old_state),
-		            state_to_string (new_state));
-	} else {
-		nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 state changed %s -> %s",
-		            priv->iface,
-		            state_to_string (old_state),
-		            state_to_string (new_state));
-	}
+	if (g_str_has_prefix (key, NEW_TAG))
+		key += STRLEN (NEW_TAG);
+	if (!key[0])
+		return;
 
-	dhcp_client_set_state (self, new_state, TRUE, FALSE);
+	str_value = garray_to_string ((GArray *) g_value_get_boxed (value), key);
+	if (str_value)
+		g_hash_table_insert (hash, g_strdup (key), str_value);
 }
 
-#define NEW_TAG "new_"
-#define OLD_TAG "old_"
-
 gboolean
-nm_dhcp_client_foreach_option (NMDHCPClient *self,
-                               GHFunc func,
-                               gpointer user_data)
-{
-	NMDHCPClientPrivate *priv;
-	GHashTableIter iter;
-	gpointer iterkey, itervalue;
+nm_dhcp_client_handle_event (gpointer unused,
+                             const char *iface,
+                             gint pid,
+                             GHashTable *options,
+                             const char *reason,
+                             NMDhcpClient *self)
+{
+	NMDhcpClientPrivate *priv;
+	guint32 old_state;
+	guint32 new_state;
+	GHashTable *str_options = NULL;
+	GObject *ip_config = NULL;
 
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
-	g_return_val_if_fail (func != NULL, FALSE);
-
-	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-
-	if (!state_is_bound (priv->state)) {
-		if (priv->ipv6) {
-			nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface);
-		} else {
-			nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface);
-		}
-	}
-
-	g_hash_table_iter_init (&iter, priv->options);
-	while (g_hash_table_iter_next (&iter, &iterkey, &itervalue)) {
-		const char *key = iterkey, *value = itervalue;
-		const char **p;
-		static const char *filter_options[] = {
-			"interface", "pid", "reason", "dhcp_message_type", NULL
-		};
-		gboolean ignore = FALSE;
-
-		/* Filter out stuff that's not actually new DHCP options */
-		for (p = filter_options; *p; p++) {
-			if (!strcmp (*p, key) || !strncmp (key, OLD_TAG, strlen (OLD_TAG))) {
-				ignore = TRUE;
-				break;
-			}
-		}
-
-		if (!ignore) {
-			const char *tmp_key = key;
-
-			/* Remove the "new_" prefix that dhclient passes back */
-			if (!strncmp (key, NEW_TAG, strlen (NEW_TAG)))
-				tmp_key = key + strlen (NEW_TAG);
-
-			func ((gpointer) tmp_key, (gpointer) value, user_data);
-		}
-	}
-	return TRUE;
-}
-
-/********************************************/
-
-static gboolean
-ip4_process_dhcpcd_rfc3442_routes (NMDHCPClient *self,
-                                   const char *str,
-                                   NMIP4Config *ip4_config,
-                                   guint32 *gwaddr)
-{
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-	char **routes, **r;
-	gboolean have_routes = FALSE;
-
-	routes = g_strsplit (str, " ", 0);
-	if (g_strv_length (routes) == 0)
-		goto out;
-
-	if ((g_strv_length (routes) % 2) != 0) {
-		nm_log_warn (LOGD_DHCP4, "  classless static routes provided, but invalid");
-		goto out;
-	}
-
-	for (r = routes; *r; r += 2) {
-		char *slash;
-		NMPlatformIP4Route route;
-		int rt_cidr = 32;
-		guint32 rt_addr, rt_route;
-
-		slash = strchr(*r, '/');
-		if (slash) {
-			*slash = '\0';
-			errno = 0;
-			rt_cidr = strtol (slash + 1, NULL, 10);
-			if ((errno == EINVAL) || (errno == ERANGE)) {
-				nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
-				continue;
-			}
-		}
-		if (inet_pton (AF_INET, *r, &rt_addr) <= 0) {
-			nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r);
-			continue;
-		}
-		if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) {
-			nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
-			continue;
-		}
-
-		have_routes = TRUE;
-		if (rt_cidr == 0 && rt_addr == 0) {
-			/* FIXME: how to handle multiple routers? */
-			*gwaddr = rt_route;
-		} else {
-			nm_log_info (LOGD_DHCP4, "  classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
-			memset (&route, 0, sizeof (route));
-			route.network = rt_addr;
-			route.plen = rt_cidr;
-			route.gateway = rt_route;
-			route.source = NM_PLATFORM_SOURCE_DHCP;
-			route.metric = priv->priority;
-			nm_ip4_config_add_route (ip4_config, &route);
-		}
-	}
-
-out:
-	g_strfreev (routes);
-	return have_routes;
-}
-
-static const char **
-process_dhclient_rfc3442_route (const char **octets, NMPlatformIP4Route *route, gboolean *success)
-{
-	const char **o = octets;
-	int addr_len = 0, i = 0;
-	long int tmp;
-	char *next_hop;
-	guint32 tmp_addr;
-
-	*success = FALSE;
-
-	if (!*o)
-		return o; /* no prefix */
-
-	tmp = strtol (*o, NULL, 10);
-	if (tmp < 0 || tmp > 32)  /* 32 == max IP4 prefix length */
-		return o;
-
-	memset (route, 0, sizeof (*route));
-	route->plen = tmp;
-	o++;
-
-	if (tmp > 0)
-		addr_len = ((tmp - 1) / 8) + 1;
-
-	/* ensure there's at least the address + next hop left */
-	if (g_strv_length ((char **) o) < addr_len + 4)
-		goto error;
-
-	if (tmp) {
-		const char *addr[4] = { "0", "0", "0", "0" };
-		char *str_addr;
-
-		for (i = 0; i < addr_len; i++)
-			addr[i] = *o++;
-
-		str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL);
-		if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) {
-			g_free (str_addr);
-			goto error;
-		}
-		tmp_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp);
-		route->network = tmp_addr;
-	}
-
-	/* Handle next hop */
-	next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL);
-	if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) {
-		g_free (next_hop);
-		goto error;
-	}
-	route->gateway = tmp_addr;
-	g_free (next_hop);
-
-	*success = TRUE;
-	return o + 4; /* advance to past the next hop */
-
-error:
-	return o;
-}
-
-static gboolean
-ip4_process_dhclient_rfc3442_routes (NMDHCPClient *self,
-                                     const char *str,
-                                     NMIP4Config *ip4_config,
-                                     guint32 *gwaddr)
-{
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-	char **octets, **o;
-	gboolean have_routes = FALSE;
-	NMPlatformIP4Route route;
-	gboolean success;
-
-	o = octets = g_strsplit_set (str, " .", 0);
-	if (g_strv_length (octets) < 5) {
-		nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
-		goto out;
-	}
-
-	while (*o) {
-		memset (&route, 0, sizeof (route));
-		o = (char **) process_dhclient_rfc3442_route ((const char **) o, &route, &success);
-		if (!success) {
-			nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes");
-			break;
-		}
-
-		have_routes = TRUE;
-		if (!route.plen) {
-			/* gateway passed as classless static route */
-			*gwaddr = route.gateway;
-		} else {
-			char addr[INET_ADDRSTRLEN];
-
-			/* normal route */
-			route.source = NM_PLATFORM_SOURCE_DHCP;
-			route.metric = priv->priority;
-			nm_ip4_config_add_route (ip4_config, &route);
-
-			nm_log_info (LOGD_DHCP4, "  classless static route %s/%d gw %s",
-			             nm_utils_inet4_ntop (route.network, addr), route.plen,
-			             nm_utils_inet4_ntop (route.gateway, NULL));
-		}
-	}
-
-out:
-	g_strfreev (octets);
-	return have_routes;
-}
-
-static gboolean
-ip4_process_classless_routes (NMDHCPClient *self,
-                              GHashTable *options,
-                              NMIP4Config *ip4_config,
-                              guint32 *gwaddr)
-{
-	const char *str, *p;
-
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (pid > 0, FALSE);
 	g_return_val_if_fail (options != NULL, FALSE);
-	g_return_val_if_fail (ip4_config != NULL, FALSE);
-
-	*gwaddr = 0;
-
-	/* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a
-	 * slightly different format:
-	 *
-	 * option classless-static-routes = array of (destination-descriptor ip-address);
-	 *
-	 * which results in:
-	 *
-	 * 0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6
-	 *
-	 * dhcpcd supports classless static routes natively and uses this same
-	 * option identifier with the following format:
-	 *
-	 * 192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41
-	 */
-	str = g_hash_table_lookup (options, "new_classless_static_routes");
-
-	/* dhclient doesn't have actual support for rfc3442 classless static routes
-	 * upstream.  Thus, people resort to defining the option in dhclient.conf
-	 * and using arbitrary formats like so:
-	 *
-	 * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
-	 *
-	 * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html
-	 */
-	if (!str)
-		str = g_hash_table_lookup (options, "new_rfc3442_classless_static_routes");
-
-	/* Microsoft version; same as rfc3442 but with a different option # (249) */
-	if (!str)
-		str = g_hash_table_lookup (options, "new_ms_classless_static_routes");
-
-	if (!str || !strlen (str))
-		return FALSE;
-
-	p = str;
-	while (*p) {
-		if (!g_ascii_isdigit (*p) && (*p != ' ') && (*p != '.') && (*p != '/')) {
-			nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
-			return FALSE;
-		}
-		p++;
-	};
-
-	if (strchr (str, '/')) {
-		/* dhcpcd format */
-		return ip4_process_dhcpcd_rfc3442_routes (self, str, ip4_config, gwaddr);
-	}
-
-	return ip4_process_dhclient_rfc3442_routes (self, str, ip4_config, gwaddr);
-}
-
-static void
-process_classful_routes (NMDHCPClient *self, GHashTable *options, NMIP4Config *ip4_config)
-{
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-	const char *str;
-	char **searches, **s;
-
-	str = g_hash_table_lookup (options, "new_static_routes");
-	if (!str)
-		return;
-
-	searches = g_strsplit (str, " ", 0);
-	if ((g_strv_length (searches) % 2)) {
-		nm_log_info (LOGD_DHCP, "  static routes provided, but invalid");
-		goto out;
-	}
-
-	for (s = searches; *s; s += 2) {
-		NMPlatformIP4Route route;
-		guint32 rt_addr, rt_route;
-
-		if (inet_pton (AF_INET, *s, &rt_addr) <= 0) {
-			nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route address: '%s'", *s);
-			continue;
-		}
-		if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) {
-			nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route gateway: '%s'", *(s + 1));
-			continue;
-		}
-
-		// FIXME: ensure the IP address and route are sane
-
-		memset (&route, 0, sizeof (route));
-		route.network = rt_addr;
-		/* RFC 2132, updated by RFC 3442:
-		   The Static Routes option (option 33) does not provide a subnet mask
-		   for each route - it is assumed that the subnet mask is implicit in
-		   whatever network number is specified in each route entry */
-		route.plen = nm_utils_ip4_get_default_prefix (rt_addr);
-		if (rt_addr & ~nm_utils_ip4_prefix_to_netmask (route.plen)) {
-			/* RFC 943: target not "this network"; using host routing */
-			route.plen = 32;
-		}
-		route.gateway = rt_route;
-		route.source = NM_PLATFORM_SOURCE_DHCP;
-		route.metric = priv->priority;
-
-		nm_ip4_config_add_route (ip4_config, &route);
-		nm_log_info (LOGD_DHCP, "  static route %s",
-		                        nm_platform_ip4_route_to_string (&route));
-	}
-
-out:
-	g_strfreev (searches);
-}
-
-static void
-process_domain_search (const char *str, GFunc add_func, gpointer user_data)
-{
-	char **searches, **s;
-	char *unescaped, *p;
-	int i;
-
-	g_return_if_fail (str != NULL);
-	g_return_if_fail (add_func != NULL);
-
-	p = unescaped = g_strdup (str);
-	do {
-		p = strstr (p, "\\032");
-		if (!p)
-			break;
-
-		/* Clear the escaped space with real spaces */
-		for (i = 0; i < 4; i++)
-			*p++ = ' ';
-	} while (*p++);
-
-	if (strchr (unescaped, '\\')) {
-		nm_log_warn (LOGD_DHCP, "  invalid domain search: '%s'", unescaped);
-		goto out;
-	}
-
-	searches = g_strsplit (unescaped, " ", 0);
-	for (s = searches; *s; s++) {
-		if (strlen (*s)) {
-			nm_log_info (LOGD_DHCP, "  domain search '%s'", *s);
-			add_func (*s, user_data);
-		}
-	}
-	g_strfreev (searches);
-
-out:
-	g_free (unescaped);
-}
-
-static void
-ip4_add_domain_search (gpointer data, gpointer user_data)
-{
-	nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data);
-}
-
-/* Given a table of DHCP options from the client, convert into an IP4Config */
-static NMIP4Config *
-ip4_options_to_config (NMDHCPClient *self)
-{
-	NMDHCPClientPrivate *priv;
-	NMIP4Config *ip4_config = NULL;
-	guint32 tmp_addr;
-	NMPlatformIP4Address address;
-	char *str = NULL;
-	guint32 gwaddr = 0, plen = 0;
-
-	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+	g_return_val_if_fail (reason != NULL, FALSE);
 
 	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-	g_return_val_if_fail (priv->options != NULL, NULL);
-
-	ip4_config = nm_ip4_config_new ();
-	memset (&address, 0, sizeof (address));
-	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
 
-	str = g_hash_table_lookup (priv->options, "new_ip_address");
-	if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
-		address.address = tmp_addr;
-		nm_log_info (LOGD_DHCP4, "  address %s", str);
-	} else
-		goto error;
-
-	str = g_hash_table_lookup (priv->options, "new_subnet_mask");
-	if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
-		plen = nm_utils_ip4_netmask_to_prefix (tmp_addr);
-		nm_log_info (LOGD_DHCP4, "  plen %d (%s)", plen, str);
-	} else {
-		/* Get default netmask for the IP according to appropriate class. */
-		plen = nm_utils_ip4_get_default_prefix (address.address);
-		nm_log_info (LOGD_DHCP4, "  plen %d (default)", plen);
-	}
-	address.plen = plen;
-
-	/* Routes: if the server returns classless static routes, we MUST ignore
-	 * the 'static_routes' option.
-	 */
-	if (!ip4_process_classless_routes (self, priv->options, ip4_config, &gwaddr))
-		process_classful_routes (self, priv->options, ip4_config);
-
-	if (gwaddr) {
-		nm_log_info (LOGD_DHCP4, "  gateway %s", nm_utils_inet4_ntop (gwaddr, NULL));
-		nm_ip4_config_set_gateway (ip4_config, gwaddr);
-	} else {
-		/* If the gateway wasn't provided as a classless static route with a
-		 * subnet length of 0, try to find it using the old-style 'routers' option.
-		 */
-		str = g_hash_table_lookup (priv->options, "new_routers");
-		if (str) {
-			char **routers = g_strsplit (str, " ", 0);
-			char **s;
-
-			for (s = routers; *s; s++) {
-				/* FIXME: how to handle multiple routers? */
-				if (inet_pton (AF_INET, *s, &gwaddr) > 0) {
-					nm_ip4_config_set_gateway (ip4_config, gwaddr);
-					nm_log_info (LOGD_DHCP4, "  gateway %s", *s);
-					break;
-				} else
-					nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s);
-			}
-			g_strfreev (routers);
-		}
-	}
+	if (g_strcmp0 (priv->iface, iface) != 0)
+		return FALSE;
+	if (priv->pid != pid)
+		return FALSE;
 
-	/*
-	 * RFC 2132, section 9.7
-	 *   DHCP clients use the contents of the 'server identifier' field
-	 *   as the destination address for any DHCP messages unicast to
-	 *   the DHCP server.
-	 *
-	 * Some ISP's provide leases from central servers that are on
-	 * different subnets that the address offered.  If the host
-	 * does not configure the interface as the default route, the
-	 * dhcp server may not be reachable via unicast, and a host
-	 * specific route is needed.
-	 **/
-	str = g_hash_table_lookup (priv->options, "new_dhcp_server_identifier");
-	if (str) {
-		if (inet_pton (AF_INET, str, &tmp_addr) > 0) {
-			NMPlatformIP4Route route;
-			guint32 mask = nm_utils_ip4_prefix_to_netmask (address.plen);
-
-			nm_log_info (LOGD_DHCP4, "  server identifier %s", str);
-			if ((tmp_addr & mask) != (address.address & mask)) {
-				/* DHCP server not on assigned subnet, route needed */
-				memset (&route, 0, sizeof (route));
-				route.network = tmp_addr;
-				route.plen = 32;
-				/* this will be a device route if gwaddr is 0 */
-				route.gateway = gwaddr;
-				route.source = NM_PLATFORM_SOURCE_DHCP;
-				route.metric = priv->priority;
-				nm_ip4_config_add_route (ip4_config, &route);
-				nm_log_dbg (LOGD_IP, "adding route for server identifier: %s",
-				                      nm_platform_ip4_route_to_string (&route));
+	old_state = priv->state;
+	new_state = reason_to_state (priv->iface, reason);
+	nm_log_dbg (LOGD_DHCP, "(%s): DHCP reason '%s' -> state '%s'",
+	            iface, reason, state_to_string (new_state));
+
+	if (new_state == NM_DHCP_STATE_BOUND) {
+		/* Copy options */
+		str_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+		g_hash_table_foreach (options, (GHFunc) copy_option, str_options);
+
+		/* Create the IP config */
+		g_warn_if_fail (g_hash_table_size (str_options));
+		if (g_hash_table_size (str_options)) {
+			if (priv->ipv6) {
+				ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (priv->iface,
+				                                                               str_options,
+				                                                               priv->priority,
+				                                                               priv->info_only);
+			} else {
+				ip_config = (GObject *) nm_dhcp_utils_ip4_config_from_options (priv->iface,
+				                                                               str_options,
+				                                                               priv->priority);
 			}
+			g_warn_if_fail (ip_config != NULL);
 		}
-		else
-			nm_log_warn (LOGD_DHCP4, "ignoring invalid server identifier '%s'", str);
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_dhcp_lease_time");
-	if (str) {
-		address.lifetime = address.preferred = strtoul (str, NULL, 10);
-		nm_log_info (LOGD_DHCP4, "  lease time %d", address.lifetime);
-	}
-
-	address.source = NM_PLATFORM_SOURCE_DHCP;
-	nm_ip4_config_add_address (ip4_config, &address);
-
-	str = g_hash_table_lookup (priv->options, "new_host_name");
-	if (str)
-		nm_log_info (LOGD_DHCP4, "  hostname '%s'", str);
-
-	str = g_hash_table_lookup (priv->options, "new_domain_name_servers");
-	if (str) {
-		char **searches = g_strsplit (str, " ", 0);
-		char **s;
-
-		for (s = searches; *s; s++) {
-			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
-				nm_ip4_config_add_nameserver (ip4_config, tmp_addr);
-				nm_log_info (LOGD_DHCP4, "  nameserver '%s'", *s);
-			} else
-				nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s);
-		}
-		g_strfreev (searches);
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_domain_name");
-	if (str) {
-		char **domains = g_strsplit (str, " ", 0);
-		char **s;
-
-		for (s = domains; *s; s++) {
-			nm_log_info (LOGD_DHCP4, "  domain name '%s'", *s);
-			nm_ip4_config_add_domain (ip4_config, *s);
-		}
-		g_strfreev (domains);
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_domain_search");
-	if (str)
-		process_domain_search (str, ip4_add_domain_search, ip4_config);
-
-	str = g_hash_table_lookup (priv->options, "new_netbios_name_servers");
-	if (str) {
-		char **searches = g_strsplit (str, " ", 0);
-		char **s;
-
-		for (s = searches; *s; s++) {
-			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
-				nm_ip4_config_add_wins (ip4_config, tmp_addr);
-				nm_log_info (LOGD_DHCP4, "  wins '%s'", *s);
-			} else
-				nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s);
-		}
-		g_strfreev (searches);
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_interface_mtu");
-	if (str) {
-		int int_mtu;
-
-		errno = 0;
-		int_mtu = strtol (str, NULL, 10);
-		if ((errno == EINVAL) || (errno == ERANGE))
-			goto error;
-
-		if (int_mtu > 576)
-			nm_ip4_config_set_mtu (ip4_config, int_mtu);
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_nis_domain");
-	if (str) {
-		nm_log_info (LOGD_DHCP4, "  NIS domain '%s'", str);
-		nm_ip4_config_set_nis_domain (ip4_config, str);
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_nis_servers");
-	if (str) {
-		char **searches = g_strsplit (str, " ", 0);
-		char **s;
-
-		for (s = searches; *s; s++) {
-			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
-				nm_ip4_config_add_nis_server (ip4_config, tmp_addr);
-				nm_log_info (LOGD_DHCP4, "  nis '%s'", *s);
-			} else
-				nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s);
-		}
-		g_strfreev (searches);
-	}
-
-	return ip4_config;
-
-error:
-	g_object_unref (ip4_config);
-	return NULL;
-}
-
-NMIP4Config *
-nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test)
-{
-	NMDHCPClientPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
-
-	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-
-	if (test && !state_is_bound (priv->state)) {
-		nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface);
-		return NULL;
-	}
-
-	if (!g_hash_table_size (priv->options)) {
-		/* We never got a response from the DHCP client */
-		return NULL;
-	}
-
-	return ip4_options_to_config (self);
-}
-
-/********************************************/
-
-static void
-ip6_add_domain_search (gpointer data, gpointer user_data)
-{
-	nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data);
-}
-
-/* Given a table of DHCP options from the client, convert into an IP6Config */
-static NMIP6Config *
-ip6_options_to_config (NMDHCPClient *self)
-{
-	NMDHCPClientPrivate *priv;
-	NMIP6Config *ip6_config = NULL;
-	struct in6_addr tmp_addr;
-	NMPlatformIP6Address address;
-	char *str = NULL;
-	GHashTableIter iter;
-	gpointer key, value;
-
-	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
-
-	memset (&address, 0, sizeof (address));
-	address.plen = 128;
-	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
-
-	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-	g_return_val_if_fail (priv->options != NULL, NULL);
-
-	g_hash_table_iter_init (&iter, priv->options);
-	while (g_hash_table_iter_next (&iter, &key, &value)) {
-		nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'",
-		            priv->iface, (const char *) key, (const char *) value);
-	}
-
-	ip6_config = nm_ip6_config_new ();
-
-	str = g_hash_table_lookup (priv->options, "new_max_life");
-	if (str) {
-		address.lifetime = strtoul (str, NULL, 10);
-		nm_log_info (LOGD_DHCP6, "  valid_lft %d", address.lifetime);
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_preferred_life");
-	if (str) {
-		address.preferred = strtoul (str, NULL, 10);
-		nm_log_info (LOGD_DHCP6, "  preferred_lft %d", address.preferred);
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_ip6_address");
-	if (str) {
-		if (!inet_pton (AF_INET6, str, &tmp_addr)) {
-			nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'",
-			             priv->iface, str);
-			goto error;
-		}
-
-		address.address = tmp_addr;
-		address.source = NM_PLATFORM_SOURCE_DHCP;
-		nm_ip6_config_add_address (ip6_config, &address);
-		nm_log_info (LOGD_DHCP6, "  address %s", str);
-	} else if (priv->info_only == FALSE) {
-		/* No address in Managed mode is a hard error */
-		goto error;
-	}
-
-	str = g_hash_table_lookup (priv->options, "new_host_name");
-	if (str)
-		nm_log_info (LOGD_DHCP6, "  hostname '%s'", str);
-
-	str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers");
-	if (str) {
-		char **searches = g_strsplit (str, " ", 0);
-		char **s;
-
-		for (s = searches; *s; s++) {
-			if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) {
-				nm_ip6_config_add_nameserver (ip6_config, &tmp_addr);
-				nm_log_info (LOGD_DHCP6, "  nameserver '%s'", *s);
-			} else
-				nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s);
-		}
-		g_strfreev (searches);
 	}
 
-	str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search");
-	if (str)
-		process_domain_search (str, ip6_add_domain_search, ip6_config);
+	nm_dhcp_client_set_state (self, new_state, ip_config, str_options);
 
-	return ip6_config;
+	if (str_options)
+		g_hash_table_destroy (str_options);
+	g_clear_object (&ip_config);
 
-error:
-	g_object_unref (ip6_config);
-	return NULL;
-}
-
-NMIP6Config *
-nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test)
-{
-	NMDHCPClientPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
-
-	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-
-	if (test && !state_is_bound (priv->state)) {
-		nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface);
-		return NULL;
-	}
-
-	if (!g_hash_table_size (priv->options)) {
-		/* We never got a response from the DHCP client */
-		return NULL;
-	}
-
-	return ip6_options_to_config (self);
+	return TRUE;
 }
 
 /********************************************/
 
 static void
-nm_dhcp_client_init (NMDHCPClient *self)
+nm_dhcp_client_init (NMDhcpClient *self)
 {
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
-
-	priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-	priv->pid = -1;
+	NM_DHCP_CLIENT_GET_PRIVATE (self)->pid = -1;
 }
 
 static void
 get_property (GObject *object, guint prop_id,
 			  GValue *value, GParamSpec *pspec)
 {
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_IFACE:
 		g_value_set_string (value, priv->iface);
 		break;
+	case PROP_IFINDEX:
+		g_value_set_int (value, priv->ifindex);
+		break;
 	case PROP_HWADDR:
 		g_value_set_boxed (value, priv->hwaddr);
 		break;
@@ -1528,13 +826,18 @@ static void
 set_property (GObject *object, guint prop_id,
 			  const GValue *value, GParamSpec *pspec)
 {
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object);
  
 	switch (prop_id) {
 	case PROP_IFACE:
 		/* construct-only */
 		priv->iface = g_strdup (g_value_get_string (value));
 		break;
+	case PROP_IFINDEX:
+		/* construct-only */
+		priv->ifindex = g_value_get_int (value);
+		g_warn_if_fail (priv->ifindex > 0);
+		break;
 	case PROP_HWADDR:
 		/* construct only */
 		priv->hwaddr = g_value_dup_boxed (value);
@@ -1563,23 +866,17 @@ set_property (GObject *object, guint prop_id,
 static void
 dispose (GObject *object)
 {
-	NMDHCPClient *self = NM_DHCP_CLIENT (object);
-	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	NMDhcpClient *self = NM_DHCP_CLIENT (object);
+	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 
 	/* Stopping the client is left up to the controlling device
 	 * explicitly since we may want to quit NetworkManager but not terminate
 	 * the DHCP client.
 	 */
 
-	if (priv->remove_id) {
-		g_source_remove (priv->remove_id);
-		priv->remove_id = 0;
-	}
+	watch_cleanup (self);
+	timeout_cleanup (self);
 
-	if (priv->options) {
-		g_hash_table_destroy (priv->options);
-		priv->options = NULL;
-	}
 	g_clear_pointer (&priv->iface, g_free);
 
 	if (priv->hwaddr) {
@@ -1596,11 +893,11 @@ dispose (GObject *object)
 }
 
 static void
-nm_dhcp_client_class_init (NMDHCPClientClass *client_class)
+nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (client_class);
 
-	g_type_class_add_private (client_class, sizeof (NMDHCPClientPrivate));
+	g_type_class_add_private (client_class, sizeof (NMDhcpClientPrivate));
 
 	/* virtual methods */
 	object_class->dispose = dispose;
@@ -1612,78 +909,59 @@ nm_dhcp_client_class_init (NMDHCPClientClass *client_class)
 
 	g_object_class_install_property
 		(object_class, PROP_IFACE,
-		 g_param_spec_string (NM_DHCP_CLIENT_INTERFACE,
-		                      "iface",
-		                      "Interface",
+		 g_param_spec_string (NM_DHCP_CLIENT_INTERFACE, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_IFINDEX,
+		 g_param_spec_int (NM_DHCP_CLIENT_IFINDEX, "", "",
+		                   -1, G_MAXINT, -1,
+		                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
 	g_object_class_install_property
 		(object_class, PROP_HWADDR,
-		 g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR,
-		                     "hwaddr",
-		                     "hardware address",
+		 g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR, "", "",
 		                     G_TYPE_BYTE_ARRAY,
-		                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IPV6,
-		 g_param_spec_boolean (NM_DHCP_CLIENT_IPV6,
-		                       "ipv6",
-		                       "IPv6",
+		 g_param_spec_boolean (NM_DHCP_CLIENT_IPV6, "", "",
 		                       FALSE,
-		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_UUID,
-		 g_param_spec_string (NM_DHCP_CLIENT_UUID,
-		                      "uuid",
-		                      "UUID",
+		 g_param_spec_string (NM_DHCP_CLIENT_UUID, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_PRIORITY,
-		 g_param_spec_uint (NM_DHCP_CLIENT_PRIORITY,
-		                    "priority",
-		                    "Priority",
-		                    0, G_MAXUINT, 0,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_uint (NM_DHCP_CLIENT_PRIORITY, "", "",
+		                    0, G_MAXUINT32, 0,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_TIMEOUT,
-		 g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT,
-		                    "timeout",
-		                    "Timeout",
+		 g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT, "", "",
 		                    0, G_MAXUINT, 45,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	/* signals */
 	signals[SIGNAL_STATE_CHANGED] =
 		g_signal_new (NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
 					  G_OBJECT_CLASS_TYPE (object_class),
 					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDHCPClientClass, state_changed),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__UINT,
-					  G_TYPE_NONE, 1, G_TYPE_UINT);
-
-	signals[SIGNAL_TIMEOUT] =
-		g_signal_new (NM_DHCP_CLIENT_SIGNAL_TIMEOUT,
-					  G_OBJECT_CLASS_TYPE (object_class),
-					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDHCPClientClass, timeout),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__VOID,
-					  G_TYPE_NONE, 0);
-
-	signals[SIGNAL_REMOVE] =
-		g_signal_new (NM_DHCP_CLIENT_SIGNAL_REMOVE,
-					  G_OBJECT_CLASS_TYPE (object_class),
-					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDHCPClientClass, remove),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__VOID,
-					  G_TYPE_NONE, 0);
+					  G_STRUCT_OFFSET (NMDhcpClientClass, state_changed),
+					  NULL, NULL, NULL,
+					  G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE);
 }
 
diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h
index ad18926a..971c9dc0 100644
--- a/src/dhcp-manager/nm-dhcp-client.h
+++ b/src/dhcp-manager/nm-dhcp-client.h
@@ -16,8 +16,8 @@
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
  */
 
-#ifndef NM_DHCP_CLIENT_H
-#define NM_DHCP_CLIENT_H
+#ifndef __NETWORKMANAGER_DHCP_CLIENT_H__
+#define __NETWORKMANAGER_DHCP_CLIENT_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -28,130 +28,142 @@
 #include <nm-ip6-config.h>
 
 #define NM_TYPE_DHCP_CLIENT            (nm_dhcp_client_get_type ())
-#define NM_DHCP_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClient))
-#define NM_DHCP_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass))
+#define NM_DHCP_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient))
+#define NM_DHCP_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass))
 #define NM_IS_DHCP_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_CLIENT))
 #define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_CLIENT))
-#define NM_DHCP_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass))
+#define NM_DHCP_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass))
 
 #define NM_DHCP_CLIENT_INTERFACE "iface"
+#define NM_DHCP_CLIENT_IFINDEX   "ifindex"
 #define NM_DHCP_CLIENT_HWADDR    "hwaddr"
 #define NM_DHCP_CLIENT_IPV6      "ipv6"
 #define NM_DHCP_CLIENT_UUID      "uuid"
 #define NM_DHCP_CLIENT_PRIORITY  "priority"
 #define NM_DHCP_CLIENT_TIMEOUT   "timeout"
 
-#define NM_DHCP_CLIENT_SIGNAL_TIMEOUT       "timeout"
 #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed"
-#define NM_DHCP_CLIENT_SIGNAL_REMOVE        "remove"
 
 typedef enum {
-	DHC_NBI = 0,     /* no broadcast interfaces found */
-	DHC_PREINIT,     /* configuration started */
-	DHC_PREINIT6,    /* configuration started */
-	DHC_BOUND4,      /* IPv4 lease obtained */
-	DHC_BOUND6,      /* IPv6 lease obtained */
-	DHC_IPV4LL,      /* IPv4LL address obtained */
-	DHC_RENEW4,      /* IPv4 lease renewed */
-	DHC_RENEW6,      /* IPv6 lease renewed */
-	DHC_REBOOT,      /* have valid lease, but now obtained a different one */
-	DHC_REBIND4,     /* IPv4 new/different lease */
-	DHC_REBIND6,     /* IPv6 new/different lease */
-	DHC_DEPREF6,     /* IPv6 lease depreferred */
-	DHC_STOP,        /* remove old lease */
-	DHC_STOP6,       /* remove old lease */
-	DHC_MEDIUM,      /* media selection begun */
-	DHC_TIMEOUT,     /* timed out contacting DHCP server */
-	DHC_FAIL,        /* all attempts to contact server timed out, sleeping */
-	DHC_EXPIRE,      /* lease has expired, renewing */
-	DHC_EXPIRE6,     /* lease has expired, renewing */
-	DHC_RELEASE,     /* releasing lease */
-	DHC_RELEASE6,    /* releasing lease */
-	DHC_START,       /* sent when dhclient started OK */
-	DHC_ABEND,       /* dhclient exited abnormally */
-	DHC_END,         /* dhclient exited normally */
-	DHC_END_OPTIONS, /* last option in subscription sent */
-} NMDHCPState;
+	NM_DHCP_STATE_UNKNOWN = 0,
+	NM_DHCP_STATE_BOUND,        /* new lease or lease changed */
+	NM_DHCP_STATE_TIMEOUT,      /* timed out contacting server */
+	NM_DHCP_STATE_DONE,         /* client quit or stopped */
+	NM_DHCP_STATE_EXPIRE,       /* lease expired or NAKed */
+	NM_DHCP_STATE_FAIL,         /* failed for some reason */
+	__NM_DHCP_STATE_MAX,
+	NM_DHCP_STATE_MAX = __NM_DHCP_STATE_MAX - 1,
+} NMDhcpState;
 
 typedef struct {
 	GObject parent;
-} NMDHCPClient;
+} NMDhcpClient;
 
 typedef struct {
 	GObjectClass parent;
 
 	/* Methods */
 
-	GPid (*ip4_start)     (NMDHCPClient *self,
-	                       const char *dhcp_client_id,
-	                       GByteArray *anycast_addr,
-	                       const char *hostname);
+	gboolean (*ip4_start)     (NMDhcpClient *self,
+	                           const char *anycast_addr,
+	                           const char *last_ip4_address);
 
-	GPid (*ip6_start)     (NMDHCPClient *self,
-	                       GByteArray *anycast_addr,
-	                       const char *hostname,
-	                       gboolean info_only,
-	                       const GByteArray *duid);
+	gboolean (*ip6_start)     (NMDhcpClient *self,
+	                           const char *anycast_addr,
+	                           gboolean info_only,
+	                           NMSettingIP6ConfigPrivacy privacy,
+	                           const GByteArray *duid);
 
-	void (*stop)          (NMDHCPClient *self,
-	                       gboolean release,
-	                       const GByteArray *duid);
+	void (*stop)              (NMDhcpClient *self,
+	                           gboolean release,
+	                           const GByteArray *duid);
 
 	/**
 	 * get_duid:
-	 * @self: the #NMDHCPClient
+	 * @self: the #NMDhcpClient
 	 *
 	 * Attempts to find an existing DHCPv6 DUID for this client in the DHCP
 	 * client's persistent configuration.  Returned DUID should be the binary
 	 * representation of the DUID.  If no DUID is found, %NULL should be
 	 * returned.
 	 */
-	GByteArray * (*get_duid) (NMDHCPClient *self);
+	GByteArray * (*get_duid) (NMDhcpClient *self);
 
 	/* Signals */
-	void (*state_changed) (NMDHCPClient *self, NMDHCPState state);
-	void (*timeout)       (NMDHCPClient *self);
-	void (*remove)        (NMDHCPClient *self);
-} NMDHCPClientClass;
+	void (*state_changed) (NMDhcpClient *self,
+	                       NMDhcpState state,
+	                       GObject *ip_config,
+	                       GHashTable *options);
+} NMDhcpClientClass;
 
 GType nm_dhcp_client_get_type (void);
 
-GPid nm_dhcp_client_get_pid (NMDHCPClient *self);
+typedef const char *(*NMDhcpClientGetPathFunc) (void);
 
-const char *nm_dhcp_client_get_iface (NMDHCPClient *self);
+typedef GSList *    (*NMDhcpClientGetLeaseConfigsFunc) (const char *iface,
+                                                        const char *uuid,
+                                                        gboolean ipv6,
+                                                        guint32 default_route_metric);
 
-gboolean nm_dhcp_client_get_ipv6 (NMDHCPClient *self);
+void _nm_dhcp_client_register (GType gtype,
+                               const char *name,
+                               NMDhcpClientGetPathFunc get_path_func,
+                               NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func);
 
-const char *nm_dhcp_client_get_uuid (NMDHCPClient *self);
+pid_t nm_dhcp_client_get_pid (NMDhcpClient *self);
 
-gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self,
+const char *nm_dhcp_client_get_iface (NMDhcpClient *self);
+
+int         nm_dhcp_client_get_ifindex (NMDhcpClient *self);
+
+gboolean nm_dhcp_client_get_ipv6 (NMDhcpClient *self);
+
+const char *nm_dhcp_client_get_uuid (NMDhcpClient *self);
+
+const GByteArray *nm_dhcp_client_get_duid (NMDhcpClient *self);
+
+const GByteArray *nm_dhcp_client_get_hw_addr (NMDhcpClient *self);
+
+guint32 nm_dhcp_client_get_priority (NMDhcpClient *self);
+
+GBytes *nm_dhcp_client_get_client_id (NMDhcpClient *self);
+
+const char *nm_dhcp_client_get_hostname (NMDhcpClient *self);
+
+gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self,
                                    const char *dhcp_client_id,
-                                   GByteArray *dhcp_anycast_addr,
-                                   const char *hostname);
+                                   const char *dhcp_anycast_addr,
+                                   const char *hostname,
+                                   const char *last_ip4_address);
 
-gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self,
-                                   GByteArray *dhcp_anycast_addr,
+gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
+                                   const char *dhcp_anycast_addr,
                                    const char *hostname,
-                                   gboolean info_only);
+                                   gboolean info_only,
+                                   NMSettingIP6ConfigPrivacy privacy);
 
-void nm_dhcp_client_stop (NMDHCPClient *self, gboolean release);
+void nm_dhcp_client_stop (NMDhcpClient *self, gboolean release);
 
-void nm_dhcp_client_new_options (NMDHCPClient *self,
-                                 GHashTable *options,
-                                 const char *reason);
+/* Backend helpers for subclasses */
+void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
 
-gboolean nm_dhcp_client_foreach_option (NMDHCPClient *self,
-                                        GHFunc func,
-                                        gpointer user_data);
+void nm_dhcp_client_stop_pid (pid_t pid, const char *iface);
 
-NMIP4Config *nm_dhcp_client_get_ip4_config   (NMDHCPClient *self, gboolean test);
+void nm_dhcp_client_watch_child (NMDhcpClient *self, pid_t pid);
 
-NMIP6Config *nm_dhcp_client_get_ip6_config   (NMDHCPClient *self, gboolean test);
+void nm_dhcp_client_set_state (NMDhcpClient *self,
+                               NMDhcpState new_state,
+                               GObject *ip_config,   /* NMIP4Config or NMIP6Config */
+                               GHashTable *options); /* str:str hash */
 
-/* Backend helpers */
-void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
+gboolean nm_dhcp_client_handle_event (gpointer unused,
+                                      const char *iface,
+                                      gint pid,
+                                      GHashTable *options,
+                                      const char *reason,
+                                      NMDhcpClient *self);
 
-void nm_dhcp_client_stop_pid (GPid pid, const char *iface);
+void nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id);
 
-#endif /* NM_DHCP_CLIENT_H */
+#endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */
 
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
index 8527e6c0..46e8e0b8 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
@@ -17,21 +17,24 @@
  * Copyright (C) 2011 Red Hat, Inc.
  */
 
-#include <config.h>
+#include "config.h"
 
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <string.h>
 #include <ctype.h>
+#include <arpa/inet.h>
 
 #include "nm-dhcp-dhclient-utils.h"
+#include "nm-dhcp-utils.h"
 #include "nm-ip4-config.h"
 #include "nm-utils.h"
+#include "nm-platform.h"
 #include "NetworkManagerUtils.h"
+#include "gsystem-local-alloc.h"
+#include "nm-utils-internal.h"
 
 #define CLIENTID_TAG            "send dhcp-client-identifier"
-#define CLIENTID_FORMAT         CLIENTID_TAG " \"%s\"; # added by NetworkManager"
-#define CLIENTID_FORMAT_OCTETS  CLIENTID_TAG " %s; # added by NetworkManager"
 
 #define HOSTNAME4_TAG    "send host-name"
 #define HOSTNAME4_FORMAT HOSTNAME4_TAG " \"%s\"; # added by NetworkManager"
@@ -71,32 +74,39 @@ add_hostname (GString *str, const char *format, const char *hostname)
 }
 
 static void
-add_ip4_config (GString *str, const char *dhcp_client_id, const char *hostname)
+add_ip4_config (GString *str, GBytes *client_id, const char *hostname)
 {
-	if (dhcp_client_id) {
-		gboolean is_octets = TRUE;
-		int i = 0;
+	if (client_id) {
+		const char *p;
+		gsize l;
+		guint i;
 
-		while (dhcp_client_id[i]) {
-			if ((i % 3) != 2 && !g_ascii_isxdigit (dhcp_client_id[i])) {
-				is_octets = FALSE;
-				break;
-			}
-			if ((i % 3) == 2 && dhcp_client_id[i] != ':') {
-				is_octets = FALSE;
+		p = g_bytes_get_data (client_id, &l);
+		g_assert (p);
+
+		/* Allow type 0 (non-hardware address) to be represented as a string
+		 * as long as all the characters are printable.
+		 */
+		for (i = 1; (p[0] == 0) && i < l; i++) {
+			if (!g_ascii_isprint (p[i]))
 				break;
-			}
-			i++;
 		}
 
-		/* If the client ID is just hex digits and : then don't use quotes,
-		 * because dhclient expects either a quoted ASCII string, or a byte
-		 * array formated as hex octets separated by :
-		 */
-		if (is_octets)
-			g_string_append_printf (str, CLIENTID_FORMAT_OCTETS "\n", dhcp_client_id);
-		else
-			g_string_append_printf (str, CLIENTID_FORMAT "\n", dhcp_client_id);
+		g_string_append (str, CLIENTID_TAG " ");
+		if (i < l) {
+			/* Unprintable; convert to a hex string */
+			for (i = 0; i < l; i++) {
+				if (i > 0)
+					g_string_append_c (str, ':');
+				g_string_append_printf (str, "%02x", (guint8) p[i]);
+			}
+		} else {
+			/* Printable; just add to the line minus the 'type' */
+			g_string_append_c (str, '"');
+			g_string_append_len (str, p + 1, l - 1);
+			g_string_append_c (str, '"');
+		}
+		g_string_append (str, "; # added by NetworkManager\n");
 	}
 
 	add_hostname (str, HOSTNAME4_FORMAT "\n", hostname);
@@ -124,19 +134,74 @@ add_ip6_config (GString *str, const char *hostname)
 	                 "send fqdn.server-update on;\n");
 }
 
+static GBytes *
+read_client_id (const char *str)
+{
+	gs_free char *s = NULL;
+	char *p;
+
+	g_assert (!strncmp (str, CLIENTID_TAG, STRLEN (CLIENTID_TAG)));
+
+	str += STRLEN (CLIENTID_TAG);
+	while (g_ascii_isspace (*str))
+		str++;
+
+	if (*str == '"') {
+		s = g_strdup (str + 1);
+		p = strrchr (s, '"');
+		if (p)
+			*p = '\0';
+		else
+			return NULL;
+	} else
+		s = g_strdup (str);
+
+	g_strchomp (s);
+	if (s[strlen (s) - 1] == ';')
+		s[strlen (s) - 1] = '\0';
+
+	return nm_dhcp_utils_client_id_string_to_bytes (s);
+}
+
+GBytes *
+nm_dhcp_dhclient_get_client_id_from_config_file (const char *path)
+{
+	gs_free char *contents = NULL;
+	gs_strfreev char **lines = NULL;
+	char **line;
+
+	g_return_val_if_fail (path != NULL, NULL);
+
+	if (!g_file_test (path, G_FILE_TEST_EXISTS))
+		return NULL;
+
+	if (!g_file_get_contents (path, &contents, NULL, NULL))
+		return NULL;
+
+	lines = g_strsplit_set (contents, "\n\r", 0);
+	for (line = lines; lines && *line; line++) {
+		if (!strncmp (*line, CLIENTID_TAG, STRLEN (CLIENTID_TAG)))
+			return read_client_id (*line);
+	}
+	return NULL;
+}
+
 char *
 nm_dhcp_dhclient_create_config (const char *interface,
                                 gboolean is_ip6,
-                                const char *dhcp_client_id,
-                                GByteArray *anycast_addr,
+                                GBytes *client_id,
+                                const char *anycast_addr,
                                 const char *hostname,
                                 const char *orig_path,
-                                const char *orig_contents)
+                                const char *orig_contents,
+                                GBytes **out_new_client_id)
 {
 	GString *new_contents;
 	GPtrArray *alsoreq;
 	int i;
 
+	g_return_val_if_fail (!anycast_addr || nm_utils_hwaddr_valid (anycast_addr, ETH_ALEN), NULL);
+
 	new_contents = g_string_new (_("# Created by NetworkManager\n"));
 	alsoreq = g_ptr_array_sized_new (5);
 
@@ -153,11 +218,15 @@ nm_dhcp_dhclient_create_config (const char *interface,
 			if (!strlen (g_strstrip (p)))
 				continue;
 
-			/* Override config file "dhcp-client-id" and use one from the
-			 * connection.
-			 */
-			if (dhcp_client_id && !strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG)))
-				continue;
+			if (!strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG))) {
+				/* Override config file "dhcp-client-id" and use one from the connection */
+				if (client_id)
+					continue;
+
+				/* Otherwise capture and return the existing client id */
+				if (out_new_client_id)
+					*out_new_client_id = read_client_id (p);
+			}
 
 			/* Override config file hostname and use one from the connection */
 			if (hostname) {
@@ -225,9 +294,8 @@ nm_dhcp_dhclient_create_config (const char *interface,
 		add_also_request (alsoreq, "dhcp6.name-servers");
 		add_also_request (alsoreq, "dhcp6.domain-search");
 		add_also_request (alsoreq, "dhcp6.client-id");
-		add_also_request (alsoreq, "dhcp6.server-id");
 	} else {
-		add_ip4_config (new_contents, dhcp_client_id, hostname);
+		add_ip4_config (new_contents, client_id, hostname);
 		add_also_request (alsoreq, "rfc3442-classless-static-routes");
 		add_also_request (alsoreq, "ms-classless-static-routes");
 		add_also_request (alsoreq, "static-routes");
@@ -246,22 +314,14 @@ nm_dhcp_dhclient_create_config (const char *interface,
 
 	g_string_append_c (new_contents, '\n');
 
-	if (anycast_addr && anycast_addr->len == 6) {
-		const guint8 *p_anycast_addr = anycast_addr->data;
-
+	if (anycast_addr) {
 		g_string_append_printf (new_contents, "interface \"%s\" {\n"
 		                        " initial-interval 1; \n"
-		                        " anycast-mac ethernet %02x:%02x:%02x:%02x:%02x:%02x;\n"
+		                        " anycast-mac ethernet %s;\n"
 		                        "}\n",
-		                        interface,
-		                        p_anycast_addr[0], p_anycast_addr[1],
-		                        p_anycast_addr[2], p_anycast_addr[3],
-		                        p_anycast_addr[4], p_anycast_addr[5]);
+		                        interface, anycast_addr);
 	}
 
-	/* Finally, assert that anycast_addr was unset or a 48 bit mac address. */
-	g_return_val_if_fail (!anycast_addr || anycast_addr->len == 6, g_string_free (new_contents, FALSE));
-
 	return g_string_free (new_contents, FALSE);
 }
 
@@ -629,7 +689,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
 
 		address.timestamp = now_monotonic_ts;
 		address.lifetime = address.preferred = expiry;
-		address.source = NM_PLATFORM_SOURCE_DHCP;
+		address.source = NM_IP_CONFIG_SOURCE_DHCP;
 
 		ip4 = nm_ip4_config_new ();
 		nm_ip4_config_add_address (ip4, &address);
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
index e8d91b31..a1828add 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
@@ -16,8 +16,8 @@
  * Copyright (C) 2010 Red Hat, Inc.
  */
 
-#ifndef NM_DHCP_DHCLIENT_UTILS_H
-#define NM_DHCP_DHCLIENT_UTILS_H
+#ifndef __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__
+#define __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -27,11 +27,12 @@
 
 char *nm_dhcp_dhclient_create_config (const char *interface,
                                       gboolean is_ip6,
-                                      const char *dhcp_client_id,
-                                      GByteArray *anycast_addr,
+                                      GBytes *client_id,
+                                      const char *anycast_addr,
                                       const char *hostname,
                                       const char *orig_path,
-                                      const char *orig_contents);
+                                      const char *orig_contents,
+                                      GBytes **out_new_client_id);
 
 char *nm_dhcp_dhclient_escape_duid (const GByteArray *duid);
 
@@ -48,5 +49,7 @@ GSList *nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
                                                 gboolean ipv6,
                                                 GDateTime *now);
 
-#endif /* NM_DHCP_DHCLIENT_UTILS_H */
+GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path);
+
+#endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */
 
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index 4232ee0c..504e8d5d 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -18,6 +18,8 @@
  * Copyright (C) 2005 - 2012 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #define _XOPEN_SOURCE
 #include <time.h>
 #undef _XOPEN_SOURCE
@@ -34,49 +36,35 @@
 #include <arpa/inet.h>
 #include <ctype.h>
 
-#include <config.h>
-
 #include "nm-dhcp-dhclient.h"
 #include "nm-utils.h"
 #include "nm-logging.h"
 #include "nm-dhcp-dhclient-utils.h"
 #include "nm-dhcp-manager.h"
 #include "nm-posix-signals.h"
+#include "NetworkManagerUtils.h"
+#include "nm-dhcp-listener.h"
+#include "gsystem-local-alloc.h"
 
-G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
+G_DEFINE_TYPE (NMDhcpDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
 
-#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientPrivate))
+#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientPrivate))
 
 typedef struct {
-	const char *path;
 	char *conf_file;
 	const char *def_leasefile;
 	char *lease_file;
 	char *pid_file;
-} NMDHCPDhclientPrivate;
+} NMDhcpDhclientPrivate;
 
-const char *
-nm_dhcp_dhclient_get_path (const char *try_first)
+static const char *
+nm_dhcp_dhclient_get_path (void)
 {
-	static const char *dhclient_paths[] = {
-		"/sbin/dhclient",
-		"/usr/sbin/dhclient",
-		"/usr/pkg/sbin/dhclient",
-		"/usr/local/sbin/dhclient",
-		NULL
-	};
-	const char **path = dhclient_paths;
-
-	if (strlen (try_first) && g_file_test (try_first, G_FILE_TEST_EXISTS))
-		return try_first;
-
-	while (*path != NULL) {
-		if (g_file_test (*path, G_FILE_TEST_EXISTS))
-			break;
-		path++;
-	}
+	const char *path = NULL;
 
-	return *path;
+	if (WITH_DHCLIENT)
+		path = nm_utils_find_helper ("dhclient", DHCLIENT_PATH, NULL);
+	return path;
 }
 
 /**
@@ -136,10 +124,11 @@ get_dhclient_leasefile (const char *iface,
 	return NULL;
 }
 
-GSList *
+static GSList *
 nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
                                        const char *uuid,
-                                       gboolean ipv6)
+                                       gboolean ipv6,
+                                       guint32 default_route_metric)
 {
 	char *contents = NULL;
 	char *leasefile;
@@ -165,10 +154,11 @@ static gboolean
 merge_dhclient_config (const char *iface,
                        const char *conf_file,
                        gboolean is_ip6,
-                       const char *dhcp_client_id,
-                       GByteArray *anycast_addr,
+                       GBytes *client_id,
+                       const char *anycast_addr,
                        const char *hostname,
                        const char *orig_path,
+                       GBytes **out_new_client_id,
                        GError **error)
 {
 	char *orig = NULL, *new;
@@ -187,7 +177,7 @@ merge_dhclient_config (const char *iface,
 		}
 	}
 
-	new = nm_dhcp_dhclient_create_config (iface, is_ip6, dhcp_client_id, anycast_addr, hostname, orig_path, orig);
+	new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, orig_path, orig, out_new_client_id);
 	g_assert (new);
 	success = g_file_set_contents (conf_file, new, -1, error);
 	g_free (new);
@@ -271,9 +261,10 @@ static char *
 create_dhclient_config (const char *iface,
                         gboolean is_ip6,
                         const char *uuid,
-                        const char *dhcp_client_id,
-                        GByteArray *dhcp_anycast_addr,
-                        const char *hostname)
+                        GBytes *client_id,
+                        const char *dhcp_anycast_addr,
+                        const char *hostname,
+                        GBytes **out_new_client_id)
 {
 	char *orig = NULL, *new = NULL;
 	GError *error = NULL;
@@ -298,7 +289,7 @@ create_dhclient_config (const char *iface,
 	}
 
 	error = NULL;
-	success = merge_dhclient_config (iface, new, is_ip6, dhcp_client_id, dhcp_anycast_addr, hostname, orig, &error);
+	success = merge_dhclient_config (iface, new, is_ip6, client_id, dhcp_anycast_addr, hostname, orig, out_new_client_id, &error);
 	if (!success) {
 		nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient%s configuration: %s",
 		             iface, is_ip6 ? "6" : "", error->message);
@@ -324,23 +315,24 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED)
 	nm_unblock_posix_signals (NULL);
 }
 
-static GPid
-dhclient_start (NMDHCPClient *client,
+static gboolean
+dhclient_start (NMDhcpClient *client,
                 const char *mode_opt,
                 const GByteArray *duid,
-                gboolean release)
+                gboolean release,
+                pid_t *out_pid)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
 	GPtrArray *argv = NULL;
-	GPid pid = -1;
+	pid_t pid;
 	GError *error = NULL;
-	const char *iface, *uuid, *system_bus_address;
+	const char *iface, *uuid, *system_bus_address, *dhclient_path = NULL;
 	char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL;
 	gboolean ipv6, success;
 	guint log_domain;
 	char *escaped, *preferred_leasefile_path = NULL;
 
-	g_return_val_if_fail (priv->pid_file == NULL, -1);
+	g_return_val_if_fail (priv->pid_file == NULL, FALSE);
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
@@ -348,9 +340,10 @@ dhclient_start (NMDHCPClient *client,
 
 	log_domain = ipv6 ? LOGD_DHCP6 : LOGD_DHCP4;
 
-	if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
-		nm_log_warn (log_domain, "%s does not exist.", priv->path);
-		return -1;
+	dhclient_path = nm_dhcp_dhclient_get_path ();
+	if (!dhclient_path) {
+		nm_log_warn (log_domain, "dhclient could not be found");
+		return FALSE;
 	}
 
 	pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid",
@@ -358,7 +351,7 @@ dhclient_start (NMDHCPClient *client,
 		                        iface);
 
 	/* Kill any existing dhclient from the pidfile */
-	binary_name = g_path_get_basename (priv->path);
+	binary_name = g_path_get_basename (dhclient_path);
 	nm_dhcp_client_stop_existing (pid_file, binary_name);
 	g_free (binary_name);
 
@@ -404,15 +397,21 @@ dhclient_start (NMDHCPClient *client,
 			             iface, priv->lease_file,
 			             error ? error->code : -1,
 			             error && error->message ? error->message : "(unknown)");
-			return -1;
+			g_free (pid_file);
+			return FALSE;
 		}
 	}
 
 	argv = g_ptr_array_new ();
-	g_ptr_array_add (argv, (gpointer) priv->path);
+	g_ptr_array_add (argv, (gpointer) dhclient_path);
 
 	g_ptr_array_add (argv, (gpointer) "-d");
 
+	/* Be quiet. dhclient logs to syslog anyway. And we duplicate the syslog
+	 * to stderr in case of NM running with --debug.
+	 */
+	g_ptr_array_add (argv, (gpointer) "-q");
+
 	if (release)
 		g_ptr_array_add (argv, (gpointer) "-r");
 
@@ -456,68 +455,80 @@ dhclient_start (NMDHCPClient *client,
 	nm_log_dbg (log_domain, "running: %s", cmd_str);
 	g_free (cmd_str);
 
-	if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                    &dhclient_child_setup, NULL, &pid, &error)) {
-		nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message);
-		g_error_free (error);
-		pid = -1;
-	} else {
+	if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
+	                   G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+	                   &dhclient_child_setup, NULL, &pid, &error)) {
+		g_assert (pid > 0);
 		nm_log_info (log_domain, "dhclient started with pid %d", pid);
+		if (release == FALSE)
+			nm_dhcp_client_watch_child (client, pid);
 		priv->pid_file = pid_file;
+	} else {
+		nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message);
+		g_error_free (error);
+		g_free (pid_file);
 	}
 
+	if (out_pid)
+		*out_pid = pid;
+
 	g_ptr_array_free (argv, TRUE);
 	g_free (system_bus_address_env);
-	return pid;
+	return pid > 0 ? TRUE : FALSE;
 }
 
-static GPid
-ip4_start (NMDHCPClient *client,
-           const char *dhcp_client_id,
-           GByteArray *dhcp_anycast_addr,
-           const char *hostname)
+static gboolean
+ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
-	const char *iface, *uuid;
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	GBytes *client_id;
+	gs_unref_bytes GBytes *new_client_id = NULL;
+	const char *iface, *uuid, *hostname;
+	gboolean success = FALSE;
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
+	client_id = nm_dhcp_client_get_client_id (client);
+	hostname = nm_dhcp_client_get_hostname (client);
 
-	priv->conf_file = create_dhclient_config (iface, FALSE, uuid, dhcp_client_id, dhcp_anycast_addr, hostname);
-	if (!priv->conf_file) {
+	priv->conf_file = create_dhclient_config (iface, FALSE, uuid, client_id, dhcp_anycast_addr, hostname, &new_client_id);
+	if (priv->conf_file) {
+		if (new_client_id)
+			nm_dhcp_client_set_client_id (client, new_client_id);
+		success = dhclient_start (client, NULL, NULL, FALSE, NULL);
+	} else
 		nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
-		return -1;
-	}
 
-	return dhclient_start (client, NULL, NULL, FALSE);
+	return success;
 }
 
-static GPid
-ip6_start (NMDHCPClient *client,
-           GByteArray *dhcp_anycast_addr,
-           const char *hostname,
+static gboolean
+ip6_start (NMDhcpClient *client,
+           const char *dhcp_anycast_addr,
            gboolean info_only,
+           NMSettingIP6ConfigPrivacy privacy,
            const GByteArray *duid)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
-	const char *iface, *uuid;
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	const char *iface, *uuid, *hostname;
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
+	hostname = nm_dhcp_client_get_hostname (client);
 
-	priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname);
+	priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, NULL);
 	if (!priv->conf_file) {
 		nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration file.", iface);
-		return -1;
+		return FALSE;
 	}
 
-	return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE);
+	return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE, NULL);
 }
 
 static void
-stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
+stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
 
 	/* Chain up to parent */
 	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid);
@@ -533,20 +544,37 @@ stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
 	}
 
 	if (release) {
-		GPid rpid;
+		pid_t rpid = -1;
 
-		rpid = dhclient_start (client, NULL, duid, TRUE);
-		if (rpid > 0) {
+		if (dhclient_start (client, NULL, duid, TRUE, &rpid)) {
 			/* Wait a few seconds for the release to happen */
 			nm_dhcp_client_stop_pid (rpid, nm_dhcp_client_get_iface (client));
 		}
 	}
 }
 
+static void
+state_changed (NMDhcpClient *client,
+	           NMDhcpState state,
+	           GObject *ip_config,
+	           GHashTable *options)
+{
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	gs_unref_bytes GBytes *client_id = NULL;
+
+	if (nm_dhcp_client_get_client_id (client))
+		return;
+	if (state != NM_DHCP_STATE_BOUND)
+		return;
+
+	client_id = nm_dhcp_dhclient_get_client_id_from_config_file (priv->conf_file);
+	nm_dhcp_client_set_client_id (client, client_id);
+}
+
 static GByteArray *
-get_duid (NMDHCPClient *client)
+get_duid (NMDhcpClient *client)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
 	GByteArray *duid = NULL;
 	char *leasefile;
 	GError *error = NULL;
@@ -595,13 +623,11 @@ static const char *def_leasefiles[] = {
 };
 
 static void
-nm_dhcp_dhclient_init (NMDHCPDhclient *self)
+nm_dhcp_dhclient_init (NMDhcpDhclient *self)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 	const char **iter = &def_leasefiles[0];
 
-	priv->path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
-
 	while (iter && *iter) {
 		if (g_file_test (*iter, G_FILE_TEST_EXISTS)) {
 			priv->def_leasefile = *iter;
@@ -613,12 +639,21 @@ nm_dhcp_dhclient_init (NMDHCPDhclient *self)
 	/* Fallback option */
 	if (!priv->def_leasefile)
 		priv->def_leasefile = SYSCONFDIR "/dhclient6.leases";
+
+	g_signal_connect (nm_dhcp_listener_get (),
+	                  NM_DHCP_LISTENER_EVENT,
+	                  G_CALLBACK (nm_dhcp_client_handle_event),
+	                  self);
 }
 
 static void
 dispose (GObject *object)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object);
+
+	g_signal_handlers_disconnect_by_func (nm_dhcp_listener_get (),
+	                                      G_CALLBACK (nm_dhcp_client_handle_event),
+	                                      NM_DHCP_DHCLIENT (object));
 
 	g_free (priv->pid_file);
 	g_free (priv->conf_file);
@@ -628,12 +663,12 @@ dispose (GObject *object)
 }
 
 static void
-nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class)
+nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class)
 {
-	NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhclient_class);
+	NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhclient_class);
 	GObjectClass *object_class = G_OBJECT_CLASS (dhclient_class);
 
-	g_type_class_add_private (dhclient_class, sizeof (NMDHCPDhclientPrivate));
+	g_type_class_add_private (dhclient_class, sizeof (NMDhcpDhclientPrivate));
 
 	/* virtual methods */
 	object_class->dispose = dispose;
@@ -642,5 +677,16 @@ nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class)
 	client_class->ip6_start = ip6_start;
 	client_class->stop = stop;
 	client_class->get_duid = get_duid;
+	client_class->state_changed = state_changed;
+}
+
+static void __attribute__((constructor))
+register_dhcp_dhclient (void)
+{
+	g_type_init ();
+	_nm_dhcp_client_register (NM_TYPE_DHCP_DHCLIENT,
+	                          "dhclient",
+	                          nm_dhcp_dhclient_get_path,
+	                          nm_dhcp_dhclient_get_lease_ip_configs);
 }
 
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h
index 20219a63..5abcc08e 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.h
+++ b/src/dhcp-manager/nm-dhcp-dhclient.h
@@ -16,8 +16,8 @@
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
  */
 
-#ifndef NM_DHCP_DHCLIENT_H
-#define NM_DHCP_DHCLIENT_H
+#ifndef __NETWORKMANAGER_DHCP_DHCLIENT_H__
+#define __NETWORKMANAGER_DHCP_DHCLIENT_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -25,27 +25,21 @@
 #include "nm-dhcp-client.h"
 
 #define NM_TYPE_DHCP_DHCLIENT            (nm_dhcp_dhclient_get_type ())
-#define NM_DHCP_DHCLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclient))
-#define NM_DHCP_DHCLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass))
+#define NM_DHCP_DHCLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclient))
+#define NM_DHCP_DHCLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass))
 #define NM_IS_DHCP_DHCLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT))
 #define NM_IS_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_DHCLIENT))
-#define NM_DHCP_DHCLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass))
+#define NM_DHCP_DHCLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass))
 
 typedef struct {
-	NMDHCPClient parent;
-} NMDHCPDhclient;
+	NMDhcpClient parent;
+} NMDhcpDhclient;
 
 typedef struct {
-	NMDHCPClientClass parent;
-} NMDHCPDhclientClass;
+	NMDhcpClientClass parent;
+} NMDhcpDhclientClass;
 
 GType nm_dhcp_dhclient_get_type (void);
 
-GSList *nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
-                                               const char *uuid,
-                                               gboolean ipv6);
-
-const char *nm_dhcp_dhclient_get_path (const char *try_first);
-
-#endif /* NM_DHCP_DHCLIENT_H */
+#endif /* __NETWORKMANAGER_DHCP_DHCLIENT_H__ */
 
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c
index acfbee26..e5a40c2f 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.c
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c
@@ -21,10 +21,10 @@
  */
 
 
-#include <config.h>
+#include "config.h"
+
 #include <glib.h>
 #include <glib/gi18n.h>
-#include <dbus/dbus.h>
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -38,38 +38,25 @@
 #include "nm-utils.h"
 #include "nm-logging.h"
 #include "nm-posix-signals.h"
+#include "NetworkManagerUtils.h"
+#include "nm-dhcp-listener.h"
 
-G_DEFINE_TYPE (NMDHCPDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_CLIENT)
+G_DEFINE_TYPE (NMDhcpDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_CLIENT)
 
-#define NM_DHCP_DHCPCD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdPrivate))
+#define NM_DHCP_DHCPCD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcdPrivate))
 
 typedef struct {
-	const char *path;
 	char *pid_file;
-} NMDHCPDhcpcdPrivate;
+} NMDhcpDhcpcdPrivate;
 
-const char *
-nm_dhcp_dhcpcd_get_path (const char *try_first)
+static const char *
+nm_dhcp_dhcpcd_get_path (void)
 {
-	static const char *dhcpcd_paths[] = {
-		"/sbin/dhcpcd",
-		"/usr/sbin/dhcpcd",
-		"/usr/pkg/sbin/dhcpcd",
-		"/usr/local/sbin/dhcpcd",
-		NULL
-	};
-	const char **path = dhcpcd_paths;
-
-	if (strlen (try_first) && g_file_test (try_first, G_FILE_TEST_EXISTS))
-		return try_first;
-
-	while (*path != NULL) {
-		if (g_file_test (*path, G_FILE_TEST_EXISTS))
-			break;
-		path++;
-	}
+	const char *path = NULL;
 
-	return *path;
+	if (WITH_DHCPCD)
+		path = nm_utils_find_helper ("dhcpcd", DHCPCD_PATH, NULL);
+	return path;
 }
 
 static void
@@ -86,20 +73,17 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)
 	nm_unblock_posix_signals (NULL);
 }
 
-static GPid
-ip4_start (NMDHCPClient *client,
-           const char *dhcp_client_id,
-           GByteArray *dhcp_anycast_addr,
-           const char *hostname)
+static gboolean
+ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
 {
-	NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
+	NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
 	GPtrArray *argv = NULL;
-	GPid pid = -1;
+	pid_t pid = -1;
 	GError *error = NULL;
 	char *pid_contents = NULL, *binary_name, *cmd_str;
-	const char *iface;
+	const char *iface, *dhcpcd_path, *hostname;
 
-	g_return_val_if_fail (priv->pid_file == NULL, -1);
+	g_return_val_if_fail (priv->pid_file == NULL, FALSE);
 
 	iface = nm_dhcp_client_get_iface (client);
 
@@ -108,18 +92,19 @@ ip4_start (NMDHCPClient *client,
 	 */
 	priv->pid_file = g_strdup_printf (RUNDIR "/dhcpcd-%s.pid", iface);
 
-	if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
-		nm_log_warn (LOGD_DHCP4, "%s does not exist.", priv->path);
-		return -1;
+	dhcpcd_path = nm_dhcp_dhcpcd_get_path ();
+	if (!dhcpcd_path) {
+		nm_log_warn (LOGD_DHCP4, "dhcpcd could not be found");
+		return FALSE;
 	}
 
 	/* Kill any existing dhcpcd from the pidfile */
-	binary_name = g_path_get_basename (priv->path);
+	binary_name = g_path_get_basename (dhcpcd_path);
 	nm_dhcp_client_stop_existing (priv->pid_file, binary_name);
 	g_free (binary_name);
 
 	argv = g_ptr_array_new ();
-	g_ptr_array_add (argv, (gpointer) priv->path);
+	g_ptr_array_add (argv, (gpointer) dhcpcd_path);
 
 	g_ptr_array_add (argv, (gpointer) "-B");	/* Don't background on lease (disable fork()) */
 
@@ -127,6 +112,9 @@ ip4_start (NMDHCPClient *client,
 
 	g_ptr_array_add (argv, (gpointer) "-L");	/* Disable built-in IPv4LL since we use avahi-autoipd */
 
+	/* --noarp. Don't request or claim the address by ARP; this also disables IPv4LL. */
+	g_ptr_array_add (argv, (gpointer) "-A");
+
 	g_ptr_array_add (argv, (gpointer) "-G");	/* Let NM handle routing */
 
 	g_ptr_array_add (argv, (gpointer) "-c");	/* Set script file */
@@ -140,7 +128,8 @@ ip4_start (NMDHCPClient *client,
 	g_ptr_array_add (argv, (gpointer) "-4");
 #endif
 
-	if (hostname && strlen (hostname)) {
+	hostname = nm_dhcp_client_get_hostname (client);
+	if (hostname) {
 		g_ptr_array_add (argv, (gpointer) "-h");	/* Send hostname to DHCP server */
 		g_ptr_array_add (argv, (gpointer) hostname );
 	}
@@ -152,34 +141,37 @@ ip4_start (NMDHCPClient *client,
 	nm_log_dbg (LOGD_DHCP4, "running: %s", cmd_str);
 	g_free (cmd_str);
 
-	if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                    &dhcpcd_child_setup, NULL, &pid, &error)) {
+	if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
+	                   G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+	                   &dhcpcd_child_setup, NULL, &pid, &error)) {
+		g_assert (pid > 0);
+		nm_log_info (LOGD_DHCP4, "dhcpcd started with pid %d", pid);
+		nm_dhcp_client_watch_child (client, pid);
+	} else {
 		nm_log_warn (LOGD_DHCP4, "dhcpcd failed to start.  error: '%s'", error->message);
 		g_error_free (error);
-		pid = -1;
-	} else
-		nm_log_info (LOGD_DHCP4, "dhcpcd started with pid %d", pid);
+	}
 
 	g_free (pid_contents);
 	g_ptr_array_free (argv, TRUE);
-	return pid;
+	return pid > 0 ? TRUE : FALSE;
 }
 
-static GPid
-ip6_start (NMDHCPClient *client,
-           GByteArray *dhcp_anycast_addr,
-           const char *hostname,
+static gboolean
+ip6_start (NMDhcpClient *client,
+           const char *dhcp_anycast_addr,
            gboolean info_only,
+           NMSettingIP6ConfigPrivacy privacy,
            const GByteArray *duid)
 {
 	nm_log_warn (LOGD_DHCP6, "the dhcpcd backend does not support IPv6.");
-	return -1;
+	return FALSE;
 }
 
 static void
-stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
+stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
-	NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
+	NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
 
 	/* Chain up to parent */
 	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release, duid);
@@ -195,17 +187,22 @@ stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
 /***************************************************/
 
 static void
-nm_dhcp_dhcpcd_init (NMDHCPDhcpcd *self)
+nm_dhcp_dhcpcd_init (NMDhcpDhcpcd *self)
 {
-	NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self);
-
-	priv->path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH);
+	g_signal_connect (nm_dhcp_listener_get (),
+	                  NM_DHCP_LISTENER_EVENT,
+	                  G_CALLBACK (nm_dhcp_client_handle_event),
+	                  self);
 }
 
 static void
 dispose (GObject *object)
 {
-	NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (object);
+	NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (object);
+
+	g_signal_handlers_disconnect_by_func (nm_dhcp_listener_get (),
+	                                      G_CALLBACK (nm_dhcp_client_handle_event),
+	                                      NM_DHCP_DHCPCD (object));
 
 	g_free (priv->pid_file);
 
@@ -213,12 +210,12 @@ dispose (GObject *object)
 }
 
 static void
-nm_dhcp_dhcpcd_class_init (NMDHCPDhcpcdClass *dhcpcd_class)
+nm_dhcp_dhcpcd_class_init (NMDhcpDhcpcdClass *dhcpcd_class)
 {
-	NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhcpcd_class);
+	NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhcpcd_class);
 	GObjectClass *object_class = G_OBJECT_CLASS (dhcpcd_class);
 
-	g_type_class_add_private (dhcpcd_class, sizeof (NMDHCPDhcpcdPrivate));
+	g_type_class_add_private (dhcpcd_class, sizeof (NMDhcpDhcpcdPrivate));
 
 	/* virtual methods */
 	object_class->dispose = dispose;
@@ -228,3 +225,13 @@ nm_dhcp_dhcpcd_class_init (NMDHCPDhcpcdClass *dhcpcd_class)
 	client_class->stop = stop;
 }
 
+static void __attribute__((constructor))
+register_dhcp_dhclient (void)
+{
+	g_type_init ();
+	_nm_dhcp_client_register (NM_TYPE_DHCP_DHCPCD,
+	                          "dhcpcd",
+	                          nm_dhcp_dhcpcd_get_path,
+	                          NULL);
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h
index fb6b0628..deed70e0 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.h
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h
@@ -16,8 +16,8 @@
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
  */
 
-#ifndef NM_DHCP_DHCPCD_H
-#define NM_DHCP_DHCPCD_H
+#ifndef __NETWORKMANAGER_DHCP_DHCPCD_H__
+#define __NETWORKMANAGER_DHCP_DHCPCD_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -25,23 +25,21 @@
 #include "nm-dhcp-client.h"
 
 #define NM_TYPE_DHCP_DHCPCD            (nm_dhcp_dhcpcd_get_type ())
-#define NM_DHCP_DHCPCD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcd))
-#define NM_DHCP_DHCPCD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass))
+#define NM_DHCP_DHCPCD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcd))
+#define NM_DHCP_DHCPCD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcdClass))
 #define NM_IS_DHCP_DHCPCD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCPCD))
 #define NM_IS_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_DHCPCD))
-#define NM_DHCP_DHCPCD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass))
+#define NM_DHCP_DHCPCD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcdClass))
 
 typedef struct {
-	NMDHCPClient parent;
-} NMDHCPDhcpcd;
+	NMDhcpClient parent;
+} NMDhcpDhcpcd;
 
 typedef struct {
-	NMDHCPClientClass parent;
-} NMDHCPDhcpcdClass;
+	NMDhcpClientClass parent;
+} NMDhcpDhcpcdClass;
 
 GType nm_dhcp_dhcpcd_get_type (void);
 
-const char *nm_dhcp_dhcpcd_get_path (const char *try_first);
-
-#endif /* NM_DHCP_DHCPCD_H */
+#endif /* __NETWORKMANAGER_DHCP_DHCPCD_H__ */
 
diff --git a/src/dhcp-manager/nm-dhcp-helper.c b/src/dhcp-manager/nm-dhcp-helper.c
index 4ddfafe1..7855f68c 100644
--- a/src/dhcp-manager/nm-dhcp-helper.c
+++ b/src/dhcp-manager/nm-dhcp-helper.c
@@ -18,184 +18,37 @@
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
 
-/* for environ */
-#define _GNU_SOURCE
+#include "config.h"
 
-#include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <signal.h>
 
-#include <config.h>
-
-#include <dbus/dbus.h>
+#include <gio/gio.h>
 
 #define NM_DHCP_CLIENT_DBUS_IFACE   "org.freedesktop.nm_dhcp_client"
 
-/**
- * _dbus_dict_open_write:
- * @iter: A valid dbus message iterator
- * @iter_dict: on return, a dict iterator to pass to further dict functions
- *
- * Start a dict in a dbus message.  Should be paired with a call to
- * _dbus_dict_close_write().
- *
- * Returns: %TRUE on success, %FALSE on failure
- */
-static dbus_bool_t
-_dbus_dict_open_write (DBusMessageIter *iter, DBusMessageIter *iter_dict)
-{
-	if (!iter || !iter_dict)
-		return FALSE;
-
-	return dbus_message_iter_open_container (iter,
-	                                         DBUS_TYPE_ARRAY,
-	                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-	                                         DBUS_TYPE_STRING_AS_STRING
-	                                         DBUS_TYPE_VARIANT_AS_STRING
-	                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
-	                                         iter_dict);
-}
-
-/**
- * _dbus_dict_close_write:
- * @iter: valid dbus message iterator, same as passed to _dbus_dict_open_write()
- * @iter_dict: a dbus dict iterator returned from _dbus_dict_open_write()
- *
- * End a dict element in a dbus message.  Should be paired with a call to
- * _dbus_dict_open_write().
- *
- * Returns: %TRUE on success, %FALSE on failure
- */
-static dbus_bool_t
-_dbus_dict_close_write (DBusMessageIter *iter, DBusMessageIter *iter_dict)
-{
-	if (!iter || !iter_dict)
-		return FALSE;
-
-	return dbus_message_iter_close_container (iter, iter_dict);
-}
-
-static dbus_bool_t
-_dbus_add_dict_entry_start (DBusMessageIter *iter_dict,
-                            DBusMessageIter *iter_dict_entry,
-                            const char *key,
-                            const int value_type)
-{
-	if (!dbus_message_iter_open_container (iter_dict, DBUS_TYPE_DICT_ENTRY, NULL, iter_dict_entry))
-		return FALSE;
-
-	if (!dbus_message_iter_append_basic (iter_dict_entry, DBUS_TYPE_STRING, &key))
-		return FALSE;
-
-	return TRUE;
-}
-
-
-static dbus_bool_t
-_dbus_add_dict_entry_end (DBusMessageIter *iter_dict,
-                          DBusMessageIter *iter_dict_entry,
-                          DBusMessageIter *iter_dict_val)
-{
-	if (!dbus_message_iter_close_container (iter_dict_entry, iter_dict_val))
-		return FALSE;
-	if (!dbus_message_iter_close_container (iter_dict, iter_dict_entry))
-		return FALSE;
-
-	return TRUE;
-}
-
-static dbus_bool_t
-_dbus_add_dict_entry_byte_array (DBusMessageIter *iter_dict,
-                                 const char *key,
-                                 const char *value,
-                                 const dbus_uint32_t value_len)
-{
-	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
-	dbus_uint32_t i;
-
-	if (!_dbus_add_dict_entry_start (iter_dict, &iter_dict_entry, key, DBUS_TYPE_ARRAY))
-		return FALSE;
-
-	if (!dbus_message_iter_open_container (&iter_dict_entry,
-	                                       DBUS_TYPE_VARIANT,
-	                                       DBUS_TYPE_ARRAY_AS_STRING
-	                                       DBUS_TYPE_BYTE_AS_STRING,
-	                                       &iter_dict_val))
-		return FALSE;
-
-	if (!dbus_message_iter_open_container (&iter_dict_val,
-	                                       DBUS_TYPE_ARRAY,
-	                                       DBUS_TYPE_BYTE_AS_STRING,
-	                                       &iter_array))
-		return FALSE;
-
-	for (i = 0; i < value_len; i++) {
-		if (!dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_BYTE, &(value[i])))
-			return FALSE;
-	}
-
-	if (!dbus_message_iter_close_container (&iter_dict_val, &iter_array))
-		return FALSE;
-
-	if (!_dbus_add_dict_entry_end (iter_dict, &iter_dict_entry, &iter_dict_val))
-		return FALSE;
-
-	return TRUE;
-}
-
-/**
- * _dbus_dict_append_byte_array:
- * @iter_dict: A valid %DBusMessageIter returned from _dbus_dict_open_write()
- * @key: The key of the dict item
- * @value: The byte array
- * @value_len: The length of the byte array, in bytes
- *
- * Add a byte array entry to the dict.
- *
- * Returns: %TRUE on success, %FALSE on failure
- *
- */
-static dbus_bool_t
-_dbus_dict_append_byte_array (DBusMessageIter *iter_dict,
-                              const char *key,
-                              const char *value,
-                              const dbus_uint32_t value_len)
-{
-	if (!key)
-		return FALSE;
-	if (!value && (value_len != 0))
-		return FALSE;
-	return _dbus_add_dict_entry_byte_array (iter_dict, key, value, value_len);
-}
-
-
 static const char * ignore[] = {"PATH", "SHLVL", "_", "PWD", "dhc_dbus", NULL};
 
-static dbus_bool_t
-build_message (DBusMessage * message)
+static GVariant *
+build_signal_parameters (void)
 {
 	char **item;
-	dbus_bool_t success = FALSE;
-	DBusMessageIter iter, iter_dict;
+	GVariantBuilder builder;
 
-	dbus_message_iter_init_append (message, &iter);
-	if (!_dbus_dict_open_write (&iter, &iter_dict))
-		goto out;
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
 
 	/* List environment and format for dbus dict */
 	for (item = environ; *item; item++) {
 		char *name, *val, **p;
 
 		/* Split on the = */
-		name = strdup (*item);
+		name = g_strdup (*item);
 		val = strchr (name, '=');
-		if (!val)
+		if (!val || val == name)
 			goto next;
 		*val++ = '\0';
-		if (!strlen (val))
-			val = NULL;
 
 		/* Ignore non-DCHP-related environment variables */
 		for (p = (char **) ignore; *p; p++) {
@@ -206,72 +59,82 @@ build_message (DBusMessage * message)
 		/* Value passed as a byte array rather than a string, because there are
 		 * no character encoding guarantees with DHCP, and D-Bus requires
 		 * strings to be UTF-8.
+		 *
+		 * Note that we can't use g_variant_new_bytestring() here, because that
+		 * includes the trailing '\0'. (??!?)
 		 */
-		if (!_dbus_dict_append_byte_array (&iter_dict,
-		                                      name,
-		                                      val ? val : "\0",
-		                                      val ? strlen (val) : 1)) {
-			fprintf (stderr, "Error: failed to add item '%s' to signal\n", name);
-		}
+		g_variant_builder_add (&builder, "{sv}",
+		                       name,
+		                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+		                                                  val, strlen (val), 1));
 
 	next:
-		free (name);
+		g_free (name);
 	}
 
-	if (!_dbus_dict_close_write (&iter, &iter_dict))
-		goto out;
-
-	success = TRUE;
-
-out:
-	return success;
+	return g_variant_new ("(a{sv})", &builder);
 }
 
 #if !HAVE_DBUS_GLIB_100
-static DBusConnection *
-shared_connection_init (void)
+/* It doesn't matter that nm-dhcp-helper doesn't use dbus-glib itself; the
+ * workaround code is for if the daemon is built with old dbus-glib.
+ */
+
+static gboolean ever_acquired = FALSE;
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar     *name,
+                  gpointer         user_data)
 {
-	DBusConnection * connection;
-	DBusError error;
-	int ret;
+	GMainLoop *loop = user_data;
 
-	dbus_connection_set_change_sigpipe (TRUE);
+	ever_acquired = TRUE;
+	g_main_loop_quit (loop);
+}
 
-	dbus_error_init (&error);
-	connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
-	if (dbus_error_is_set (&error)) {
-		fprintf (stderr, "Error: could not get the system bus.  Make sure "
-		            "the message bus daemon is running!  Message: (%s) %s\n",
-		            error.name,
-		            error.message);
-		goto error;
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar     *name,
+              gpointer         user_data)
+{
+	if (ever_acquired) {
+		g_print ("Lost D-Bus name: exiting\n");
+		exit (0);
+	} else {
+		g_printerr ("Error: Could not acquire the NM DHCP client service.\n");
+		exit (1);
 	}
+}
 
-	dbus_error_init (&error);
-	ret = dbus_bus_request_name (connection, "org.freedesktop.nm_dhcp_client", 0, &error);
-	if (dbus_error_is_set (&error)) {
-		fprintf (stderr, "Error: Could not acquire the NM DHCP client service. "
-		            "Message: (%s) %s\n",
-		            error.name,
-		            error.message);
-		goto error;
-	}
+static GDBusConnection *
+shared_connection_init (void)
+{
+	GDBusConnection *connection;
+	GError *error = NULL;
+	GMainLoop *loop;
 
-	if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
-		fprintf (stderr, "Error: Could not acquire the NM DHCP client service "
-		         "as it is already taken.  Return: %d\n",
-		         ret);
-		goto error;
+	connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+	if (!connection) {
+		g_dbus_error_strip_remote_error (error);
+		g_printerr ("Error: could not get the system bus.  Make sure "
+		            "the message bus daemon is running!  Message: %s\n",
+		            error->message);
+		g_error_free (error);
+		return NULL;
 	}
 
-	return connection;
+	loop = g_main_loop_new (NULL, FALSE);
+	g_bus_own_name_on_connection (connection,
+	                              "org.freedesktop.nm_dhcp_client",
+	                              0,
+	                              on_name_acquired,
+	                              on_name_lost,
+	                              loop, NULL);
+	g_main_loop_run (loop);
+	g_main_loop_unref (loop);
 
-error:
-	if (dbus_error_is_set (&error))
-		dbus_error_free (&error);
-	if (connection)
-		dbus_connection_unref (connection);
-	return NULL;
+	return connection;
 }
 #endif
 
@@ -284,7 +147,7 @@ fatal_error (void)
 	if (pid_str)
 		pid = strtol (pid_str, NULL, 10);
 	if (pid) {
-		fprintf (stderr, "Fatal error occured, killing dhclient instance with pid %d.\n", pid);
+		g_printerr ("Fatal error occured, killing dhclient instance with pid %d.\n", pid);
 		kill (pid, SIGTERM);
 	}
 
@@ -294,52 +157,46 @@ fatal_error (void)
 int
 main (int argc, char *argv[])
 {
-	DBusConnection *connection;
-	DBusMessage *message;
-	dbus_bool_t result;
-	DBusError error;
-
-	dbus_connection_set_change_sigpipe (TRUE);
+	GDBusConnection *connection;
+	GError *error = NULL;
 
-	dbus_error_init (&error);
-	connection = dbus_connection_open_private ("unix:path=" NMRUNDIR "/private-dhcp", &error);
+	connection = g_dbus_connection_new_for_address_sync ("unix:path=" NMRUNDIR "/private-dhcp",
+	                                                     G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+	                                                     NULL, NULL, &error);
 	if (!connection) {
 #if !HAVE_DBUS_GLIB_100
 		connection = shared_connection_init ();
 #endif
 		if (!connection) {
-			fprintf (stderr, "Error: could not connect to NetworkManager DBus socket: (%s) %s\n",
-				     error.name, error.message);
-			dbus_error_free (&error);
+			g_dbus_error_strip_remote_error (error);
+			g_printerr ("Error: could not connect to NetworkManager D-Bus socket: %s\n",
+			            error->message);
+			g_error_free (error);
 			fatal_error ();
 		}
 	}
-	dbus_connection_set_exit_on_disconnect (connection, FALSE);
 
-	message = dbus_message_new_signal ("/", NM_DHCP_CLIENT_DBUS_IFACE, "Event");
-	if (message == NULL) {
-		fprintf (stderr, "Error: Not enough memory to send DHCP Event signal.\n");
+	if (!g_dbus_connection_emit_signal (connection,
+	                                    NULL,
+	                                    "/",
+	                                    NM_DHCP_CLIENT_DBUS_IFACE,
+	                                    "Event",
+	                                    build_signal_parameters (),
+	                                    &error)) {
+		g_dbus_error_strip_remote_error (error);
+		g_printerr ("Error: Could not send DHCP Event signal: %s\n", error->message);
+		g_error_free (error);
 		fatal_error ();
 	}
 
-	/* Dump environment variables into the message */
-	result = build_message (message);
-	if (result == FALSE) {
-		fprintf (stderr, "Error: Not enough memory to send DHCP Event signal.\n");
+	if (!g_dbus_connection_flush_sync (connection, NULL, &error)) {
+		g_dbus_error_strip_remote_error (error);
+		g_printerr ("Error: Could not flush D-Bus connection: %s\n", error->message);
+		g_error_free (error);
 		fatal_error ();
 	}
 
-	/* queue the message */
-	result = dbus_connection_send (connection, message, NULL);
-	if (!result) {
-		fprintf (stderr, "Error: Could not send send DHCP Event signal.\n");
-		fatal_error ();
-	}
-	dbus_message_unref (message);
-
-	/* Send out the message */
-	dbus_connection_flush (connection);
-
+	g_object_unref (connection);
 	return 0;
 }
 
diff --git a/src/dhcp-manager/nm-dhcp-listener.c b/src/dhcp-manager/nm-dhcp-listener.c
new file mode 100644
index 00000000..b995c348
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-listener.c
@@ -0,0 +1,290 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2014 Red Hat, Inc.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "nm-dhcp-listener.h"
+#include "nm-logging.h"
+#include "nm-dbus-manager.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+#include "NetworkManagerUtils.h"
+
+#define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client"
+#define PRIV_SOCK_PATH            NMRUNDIR "/private-dhcp"
+#define PRIV_SOCK_TAG             "dhcp"
+
+typedef struct {
+	NMDBusManager *     dbus_mgr;
+	guint               new_conn_id;
+	guint               dis_conn_id;
+	GHashTable *        proxies;
+	DBusGProxy *        proxy;
+} NMDhcpListenerPrivate;
+
+#define NM_DHCP_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_LISTENER, NMDhcpListenerPrivate))
+
+G_DEFINE_TYPE (NMDhcpListener, nm_dhcp_listener, G_TYPE_OBJECT)
+
+enum {
+	EVENT,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/***************************************************/
+
+static char *
+garray_to_string (GArray *array, const char *key)
+{
+	GString *str;
+	int i;
+	unsigned char c;
+	char *converted = NULL;
+
+	g_return_val_if_fail (array != NULL, NULL);
+
+	/* Since the DHCP options come through environment variables, they should
+	 * already be UTF-8 safe, but just make sure.
+	 */
+	str = g_string_sized_new (array->len);
+	for (i = 0; i < array->len; i++) {
+		c = array->data[i];
+
+		/* Convert NULLs to spaces and non-ASCII characters to ? */
+		if (c == '\0')
+			c = ' ';
+		else if (c > 127)
+			c = '?';
+		str = g_string_append_c (str, c);
+	}
+	str = g_string_append_c (str, '\0');
+
+	converted = str->str;
+	if (!g_utf8_validate (converted, -1, NULL))
+		nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
+	g_string_free (str, FALSE);
+	return converted;
+}
+
+static char *
+get_option (GHashTable *hash, const char *key)
+{
+	GValue *value;
+
+	value = g_hash_table_lookup (hash, key);
+	if (value == NULL)
+		return NULL;
+
+	if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
+		nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
+		             "DBUS_TYPE_G_UCHAR_ARRAY",
+		             (char *) key);
+		return NULL;
+	}
+
+	return garray_to_string ((GArray *) g_value_get_boxed (value), key);
+}
+
+static void
+handle_event (DBusGProxy *proxy,
+              GHashTable *options,
+              gpointer user_data)
+{
+	NMDhcpListener *self = NM_DHCP_LISTENER (user_data);
+	char *iface = NULL;
+	char *pid_str = NULL;
+	char *reason = NULL;
+	gint pid;
+	gboolean handled = FALSE;
+
+	iface = get_option (options, "interface");
+	if (iface == NULL) {
+		nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
+		goto out;
+	}
+
+	pid_str = get_option (options, "pid");
+	pid = nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1);
+	if (pid == -1) {
+		nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
+		goto out;
+	}
+
+	reason = get_option (options, "reason");
+	if (reason == NULL) {
+		nm_log_warn (LOGD_DHCP, "(pid %d) DHCP event didn't have a reason", pid);
+		goto out;
+	}
+
+	g_signal_emit (self, signals[EVENT], 0, iface, pid, options, reason, &handled);
+	if (!handled) {
+		if (g_ascii_strcasecmp (reason, "RELEASE") == 0) {
+			/* Ignore event when the dhcp client gets killed and we receive its last message */
+			nm_log_dbg (LOGD_DHCP, "(pid %d) unhandled RELEASE DHCP event for interface %s", pid, iface);
+		} else
+			nm_log_warn (LOGD_DHCP, "(pid %d) unhandled DHCP event for interface %s", pid, iface);
+	}
+
+out:
+	g_free (iface);
+	g_free (pid_str);
+	g_free (reason);
+}
+
+#if HAVE_DBUS_GLIB_100
+static void
+new_connection_cb (NMDBusManager *mgr,
+                   DBusGConnection *connection,
+                   NMDhcpListener *self)
+{
+	DBusGProxy *proxy;
+
+	/* Create a new proxy for the client */
+	proxy = dbus_g_proxy_new_for_peer (connection, "/", NM_DHCP_CLIENT_DBUS_IFACE);
+	dbus_g_proxy_add_signal (proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (proxy, "Event", G_CALLBACK (handle_event), self, NULL);
+
+	g_hash_table_insert (NM_DHCP_LISTENER_GET_PRIVATE (self)->proxies, connection, proxy);
+}
+
+static void
+dis_connection_cb (NMDBusManager *mgr,
+                   DBusGConnection *connection,
+                   NMDhcpListener *self)
+{
+	NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (self);
+	DBusGProxy *proxy;
+
+	proxy = g_hash_table_lookup (priv->proxies, connection);
+	if (proxy) {
+		dbus_g_proxy_disconnect_signal (proxy, "Event", G_CALLBACK (handle_event), self);
+		g_hash_table_remove (priv->proxies, connection);
+	}
+}
+#endif
+
+/***************************************************/
+
+NMDhcpListener *
+nm_dhcp_listener_get (void)
+{
+	static NMDhcpListener *singleton = NULL;
+
+	if (G_UNLIKELY (singleton == NULL))
+		singleton = g_object_new (NM_TYPE_DHCP_LISTENER, NULL);
+	g_assert (singleton);
+	return singleton;
+}
+
+static void
+nm_dhcp_listener_init (NMDhcpListener *self)
+{
+	NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (self);
+#if !HAVE_DBUS_GLIB_100
+	DBusGConnection *g_connection;
+#endif
+
+	/* Maps DBusGConnection :: DBusGProxy */
+	priv->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+
+#if HAVE_DBUS_GLIB_100
+	/* Register the socket our DHCP clients will return lease info on */
+	nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
+	priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
+	                                      NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
+	                                      G_CALLBACK (new_connection_cb),
+	                                      self);
+	priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
+	                                      NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
+	                                      G_CALLBACK (dis_connection_cb),
+	                                      self);
+#else
+	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	priv->proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                         "org.freedesktop.nm_dhcp_client",
+	                                         "/",
+	                                         NM_DHCP_CLIENT_DBUS_IFACE);
+	g_assert (priv->proxy);
+	dbus_g_proxy_add_signal (priv->proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "Event", G_CALLBACK (handle_event), self, NULL);
+#endif
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (object);
+
+	if (priv->new_conn_id) {
+		g_signal_handler_disconnect (priv->dbus_mgr, priv->new_conn_id);
+		priv->new_conn_id = 0;
+	}
+	if (priv->dis_conn_id) {
+		g_signal_handler_disconnect (priv->dbus_mgr, priv->dis_conn_id);
+		priv->dis_conn_id = 0;
+	}
+	priv->dbus_mgr = NULL;
+
+	if (priv->proxies) {
+		g_hash_table_destroy (priv->proxies);
+		priv->proxies = NULL;
+	}
+	g_clear_object (&priv->proxy);
+
+	G_OBJECT_CLASS (nm_dhcp_listener_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_listener_class_init (NMDhcpListenerClass *listener_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (listener_class);
+
+	g_type_class_add_private (listener_class, sizeof (NMDhcpListenerPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+
+	/* signals */
+	signals[EVENT] =
+		g_signal_new (NM_DHCP_LISTENER_EVENT,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST, 0,
+		              g_signal_accumulator_true_handled,
+		              NULL, NULL,
+		              G_TYPE_BOOLEAN,     /* listeners return TRUE if handled */
+		              4,
+		              G_TYPE_STRING,      /* iface */
+		              G_TYPE_INT,         /* pid */
+		              G_TYPE_HASH_TABLE,  /* options */
+		              G_TYPE_STRING);     /* reason */
+}
diff --git a/src/dhcp-manager/nm-dhcp-listener.h b/src/dhcp-manager/nm-dhcp-listener.h
new file mode 100644
index 00000000..15ec0531
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-listener.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DHCP_LISTENER_H__
+#define __NETWORKMANAGER_DHCP_LISTENER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define NM_TYPE_DHCP_LISTENER           (nm_dhcp_listener_get_type ())
+#define NM_DHCP_LISTENER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListener))
+#define NM_IS_DHCP_LISTENER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_LISTENER))
+#define NM_DHCP_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListenerClass))
+
+#define NM_DHCP_LISTENER_EVENT "event"
+
+typedef GObject NMDhcpListener;
+typedef GObjectClass NMDhcpListenerClass;
+
+GType nm_dhcp_listener_get_type (void);
+
+NMDhcpListener *nm_dhcp_listener_get (void);
+
+#endif /* __NETWORKMANAGER_DHCP_LISTENER_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index ab6966cb..f936f45c 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -21,9 +21,9 @@
  */
 
 #include "config.h"
+
 #include <glib.h>
 #include <glib/gi18n.h>
-#include <dbus/dbus.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <signal.h>
@@ -37,31 +37,15 @@
 #include "nm-dhcp-manager.h"
 #include "nm-dhcp-dhclient.h"
 #include "nm-dhcp-dhcpcd.h"
+#include "nm-dhcp-systemd.h"
 #include "nm-logging.h"
-#include "nm-dbus-manager.h"
 #include "nm-config.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-glib-compat.h"
 #include "NetworkManagerUtils.h"
 
-GQuark
-nm_dhcp_manager_error_quark (void)
-{
-    static GQuark ret = 0;
-
-    if (ret == 0)
-        ret = g_quark_from_static_string ("nm_dhcp_manager_error");
-
-    return ret;
-}
-
-#define NM_DHCP_CLIENT_DBUS_IFACE   "org.freedesktop.nm_dhcp_client"
-
 #define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
 
-#define PRIV_SOCK_PATH NMRUNDIR "/private-dhcp"
-#define PRIV_SOCK_TAG  "dhcp"
-
 /* default to installed helper, but can be modified for testing */
 const char *nm_dhcp_helper_path = LIBEXECDIR "/nm-dhcp-helper";
 
@@ -69,336 +53,186 @@ typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gbo
 
 typedef struct {
 	GType               client_type;
-	GetLeaseConfigFunc  get_lease_ip_configs_func;
-
-	NMDBusManager *     dbus_mgr;
-	guint               new_conn_id;
-	guint               dis_conn_id;
-	GHashTable *        proxies;
-
 	GHashTable *        clients;
-	DBusGProxy *        proxy;
 	char *              default_hostname;
-} NMDHCPManagerPrivate;
+} NMDhcpManagerPrivate;
+
+#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDhcpManagerPrivate))
 
+G_DEFINE_TYPE (NMDhcpManager, nm_dhcp_manager, G_TYPE_OBJECT)
 
-#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDHCPManagerPrivate))
+/***************************************************/
+
+typedef struct {
+	GType gtype;
+	const char *name;
+	NMDhcpClientGetPathFunc get_path_func;
+	NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func;
+} ClientDesc;
 
-G_DEFINE_TYPE (NMDHCPManager, nm_dhcp_manager, G_TYPE_OBJECT)
+static GSList *client_descs = NULL;
 
-static char *
-garray_to_string (GArray *array, const char *key)
+void
+_nm_dhcp_client_register (GType gtype,
+                          const char *name,
+                          NMDhcpClientGetPathFunc get_path_func,
+                          NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func)
 {
-	GString *str;
-	int i;
-	unsigned char c;
-	char *converted = NULL;
+	ClientDesc *desc;
+	GSList *iter;
 
-	g_return_val_if_fail (array != NULL, NULL);
+	g_return_if_fail (gtype != G_TYPE_INVALID);
+	g_return_if_fail (name != NULL);
 
-	/* Since the DHCP options come through environment variables, they should
-	 * already be UTF-8 safe, but just make sure.
-	 */
-	str = g_string_sized_new (array->len);
-	for (i = 0; i < array->len; i++) {
-		c = array->data[i];
-
-		/* Convert NULLs to spaces and non-ASCII characters to ? */
-		if (c == '\0')
-			c = ' ';
-		else if (c > 127)
-			c = '?';
-		str = g_string_append_c (str, c);
+	for (iter = client_descs; iter; iter = iter->next) {
+		desc = iter->data;
+		g_return_if_fail (desc->gtype != gtype);
+		g_return_if_fail (strcmp (desc->name, name) != 0);
 	}
-	str = g_string_append_c (str, '\0');
 
-	converted = str->str;
-	if (!g_utf8_validate (converted, -1, NULL))
-		nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
-	g_string_free (str, FALSE);
-	return converted;
+	desc = g_slice_new0 (ClientDesc);
+	desc->gtype = gtype;
+	desc->name = name;
+	desc->get_path_func = get_path_func;
+	desc->get_lease_configs_func = get_lease_configs_func;
+	client_descs = g_slist_prepend (client_descs, desc);
 }
 
-static NMDHCPClient *
-get_client_for_pid (NMDHCPManager *manager, GPid pid)
+static ClientDesc *
+find_client_desc (const char *name, GType gtype)
 {
-	NMDHCPManagerPrivate *priv;
-	GHashTableIter iter;
-	gpointer value;
+	GSList *iter;
 
-	g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
-
-	priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+	g_return_val_if_fail (name || gtype, NULL);
 
-	g_hash_table_iter_init (&iter, priv->clients);
-	while (g_hash_table_iter_next (&iter, NULL, &value)) {
-		NMDHCPClient *candidate = NM_DHCP_CLIENT (value);
+	for (iter = client_descs; iter; iter = iter->next) {
+		ClientDesc *desc = iter->data;
 
-		if (nm_dhcp_client_get_pid (candidate) == pid)
-			return candidate;
+		if (name && strcmp (desc->name, name) != 0)
+			continue;
+		if (gtype && desc->gtype != gtype)
+			continue;
+		return desc;
 	}
-
 	return NULL;
 }
 
-static NMDHCPClient *
-get_client_for_iface (NMDHCPManager *manager,
-                      const char *iface,
-                      gboolean ip6)
+static GType
+is_client_enabled (const char *name, GError **error)
+{
+	ClientDesc *desc;
+
+	desc = find_client_desc (name, G_TYPE_INVALID);
+	if (desc && (!desc->get_path_func || desc->get_path_func()))
+		return desc->gtype;
+
+	g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+	             _("'%s' support not found or not enabled."),
+	             name);
+	return G_TYPE_INVALID;
+}
+
+/***************************************************/
+
+static NMDhcpClient *
+get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
 {
-	NMDHCPManagerPrivate *priv;
+	NMDhcpManagerPrivate *priv;
 	GHashTableIter iter;
 	gpointer value;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
-	g_return_val_if_fail (iface, NULL);
+	g_return_val_if_fail (ifindex > 0, NULL);
 
 	priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
 
 	g_hash_table_iter_init (&iter, priv->clients);
 	while (g_hash_table_iter_next (&iter, NULL, &value)) {
-		NMDHCPClient *candidate = NM_DHCP_CLIENT (value);
+		NMDhcpClient *candidate = NM_DHCP_CLIENT (value);
 
-		if (   !strcmp (iface, nm_dhcp_client_get_iface (candidate))
-		    && (nm_dhcp_client_get_ipv6 (candidate) == ip6))
+		if (   nm_dhcp_client_get_ifindex (candidate) == ifindex
+		    && nm_dhcp_client_get_ipv6 (candidate) == ip6)
 			return candidate;
 	}
 
 	return NULL;
 }
 
-static char *
-get_option (GHashTable *hash, const char *key)
-{
-	GValue *value;
-
-	value = g_hash_table_lookup (hash, key);
-	if (value == NULL)
-		return NULL;
-
-	if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
-		nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
-		             "DBUS_TYPE_G_UCHAR_ARRAY",
-		             (char *) key);
-		return NULL;
-	}
-
-	return garray_to_string ((GArray *) g_value_get_boxed (value), key);
-}
-
-static void
-nm_dhcp_manager_handle_event (DBusGProxy *proxy,
-                              GHashTable *options,
-                              gpointer user_data)
-{
-	NMDHCPManager *manager = NM_DHCP_MANAGER (user_data);
-	NMDHCPClient *client;
-	char *iface = NULL;
-	char *pid_str = NULL;
-	char *reason = NULL;
-	long pid;
-
-	iface = get_option (options, "interface");
-	if (iface == NULL) {
-		nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
-		goto out;
-	}
-
-	pid_str = get_option (options, "pid");
-	pid = nm_utils_ascii_str_to_int64 (pid_str, 10, 0, LONG_MAX, -1);
-	if (pid == -1 || pid != (GPid)pid) {
-		nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
-		goto out;
-	}
-
-	reason = get_option (options, "reason");
-	client = get_client_for_pid (manager, (GPid) pid);
-	if (client == NULL) {
-		if (reason && g_ascii_strcasecmp (reason, "RELEASE") == 0) {
-			/* This happens regularly, when the dhcp client gets killed and we receive its last message.
-			 * Don't log a warning in this case. */
-			nm_log_dbg (LOGD_DHCP, "(pid %ld) unhandled RELEASE DHCP event for interface %s", pid, iface);
-		} else
-			nm_log_warn (LOGD_DHCP, "(pid %ld) unhandled DHCP event for interface %s", pid, iface);
-		goto out;
-	}
-
-	if (strcmp (iface, nm_dhcp_client_get_iface (client))) {
-		nm_log_warn (LOGD_DHCP, "(pid %ld) received DHCP event from unexpected interface '%s' (expected '%s')",
-		             pid, iface, nm_dhcp_client_get_iface (client));
-		goto out;
-	}
-
-	if (reason == NULL) {
-		nm_log_warn (LOGD_DHCP, "(pid %ld) DHCP event didn't have a reason", pid);
-		goto out;
-	}
-
-	nm_dhcp_client_new_options (client, options, reason);
-
-out:
-	g_free (iface);
-	g_free (pid_str);
-	g_free (reason);
-}
-
-#if HAVE_DBUS_GLIB_100
-static void
-new_connection_cb (NMDBusManager *mgr,
-                   DBusGConnection *connection,
-                   NMDHCPManager *self)
-{
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-	DBusGProxy *proxy;
-
-	/* Create a new proxy for the client */
-	proxy = dbus_g_proxy_new_for_peer (connection, "/", NM_DHCP_CLIENT_DBUS_IFACE);
-	dbus_g_proxy_add_signal (proxy,
-	                         "Event",
-	                         DBUS_TYPE_G_MAP_OF_VARIANT,
-	                         G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (proxy,
-	                             "Event",
-	                             G_CALLBACK (nm_dhcp_manager_handle_event),
-	                             self,
-	                             NULL);
-	g_hash_table_insert (priv->proxies, connection, proxy);
-}
-
-static void
-dis_connection_cb (NMDBusManager *mgr,
-                   DBusGConnection *connection,
-                   NMDHCPManager *self)
-{
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-	DBusGProxy *proxy;
-
-	proxy = g_hash_table_lookup (priv->proxies, connection);
-	if (proxy) {
-		dbus_g_proxy_disconnect_signal (proxy,
-		                                "Event",
-		                                G_CALLBACK (nm_dhcp_manager_handle_event),
-		                                self);
-		g_hash_table_remove (priv->proxies, connection);
-	}
-}
-#endif
-
 static GType
 get_client_type (const char *client, GError **error)
 {
-	const char *dhclient_path = NULL;
-	const char *dhcpcd_path = NULL;
-
-	/* If a client was disabled at build-time, its *_PATH define will be
-	 * an empty string.
-	 */
-	/* coverity[array_null] */
-	if (DHCLIENT_PATH && strlen (DHCLIENT_PATH))
-		dhclient_path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
-	/* coverity[array_null] */
-	if (DHCPCD_PATH && strlen (DHCPCD_PATH))
-		dhcpcd_path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH);
-
-	if (!client) {
-		if (dhclient_path)
-			return NM_TYPE_DHCP_DHCLIENT;
-		else if (dhcpcd_path)
-			return NM_TYPE_DHCP_DHCPCD;
-		else {
-			g_set_error_literal (error,
-			                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
-			                     _("no usable DHCP client could be found."));
-			return G_TYPE_INVALID;
-		}
-	}
-
-	if (!strcmp (client, "dhclient")) {
-		if (!dhclient_path) {
-			g_set_error_literal (error,
-			                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
-			                     _("'dhclient' could be found."));
-			return G_TYPE_INVALID;
+	GType client_gtype;
+
+	if (client)
+		client_gtype = is_client_enabled (client, error);
+	else {
+		/* Fallbacks */
+		client_gtype = is_client_enabled ("dhclient", NULL);
+		if (client_gtype == G_TYPE_INVALID)
+			client_gtype = is_client_enabled ("dhcpcd", NULL);
+		if (client_gtype == G_TYPE_INVALID)
+			client_gtype = is_client_enabled ("internal", NULL);
+		if (client_gtype == G_TYPE_INVALID) {
+			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+				                 _("no usable DHCP client could be found."));
 		}
-		return NM_TYPE_DHCP_DHCLIENT;
 	}
-
-	if (!strcmp (client, "dhcpcd")) {
-		if (!dhcpcd_path) {
-			g_set_error_literal (error,
-			                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
-			                     _("'dhcpcd' could be found."));
-			return G_TYPE_INVALID;
-		}
-		return NM_TYPE_DHCP_DHCPCD;
-	}
-
-	g_set_error (error,
-	             NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
-	             _("unsupported DHCP client '%s'"), client);
-	return G_TYPE_INVALID;
+	return client_gtype;
 }
 
-#define REMOVE_ID_TAG "remove-id"
-#define TIMEOUT_ID_TAG "timeout-id"
+static void client_state_changed (NMDhcpClient *client,
+                                  NMDhcpState state,
+                                  GObject *ip_config,
+                                  GHashTable *options,
+                                  NMDhcpManager *self);
 
 static void
-remove_client (NMDHCPManager *self, NMDHCPClient *client)
+remove_client (NMDhcpManager *self, NMDhcpClient *client)
 {
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-	guint id;
-
-	id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), REMOVE_ID_TAG));
-	if (id)
-		g_signal_handler_disconnect (client, id);
-
-	id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), TIMEOUT_ID_TAG));
-	if (id)
-		g_signal_handler_disconnect (client, id);
+	g_signal_handlers_disconnect_by_func (client, client_state_changed, self);
 
 	/* Stopping the client is left up to the controlling device
 	 * explicitly since we may want to quit NetworkManager but not terminate
 	 * the DHCP client.
 	 */
 
-	g_hash_table_remove (priv->clients, client);
+	g_hash_table_remove (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client);
 }
 
 static void
-add_client (NMDHCPManager *self, NMDHCPClient *client)
+client_state_changed (NMDhcpClient *client,
+                      NMDhcpState state,
+                      GObject *ip_config,
+                      GHashTable *options,
+                      NMDhcpManager *self)
 {
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-	guint id;
-
-	id = g_signal_connect_swapped (client, NM_DHCP_CLIENT_SIGNAL_REMOVE, G_CALLBACK (remove_client), self);
-	g_object_set_data (G_OBJECT (client), REMOVE_ID_TAG, GUINT_TO_POINTER (id));
-
-	id = g_signal_connect_swapped (client, NM_DHCP_CLIENT_SIGNAL_TIMEOUT, G_CALLBACK (remove_client), self);
-	g_object_set_data (G_OBJECT (client), TIMEOUT_ID_TAG, GUINT_TO_POINTER (id));
-
-	g_hash_table_insert (priv->clients, client, g_object_ref (client));
+	if (state >= NM_DHCP_STATE_TIMEOUT)
+		remove_client (self, client);
 }
 
-static NMDHCPClient *
-client_start (NMDHCPManager *self,
+static NMDhcpClient *
+client_start (NMDhcpManager *self,
               const char *iface,
+              int ifindex,
               const GByteArray *hwaddr,
               const char *uuid,
-              guint priority,
+              guint32 priority,
               gboolean ipv6,
               const char *dhcp_client_id,
               guint32 timeout,
-              GByteArray *dhcp_anycast_addr,
+              const char *dhcp_anycast_addr,
               const char *hostname,
-              gboolean info_only)
+              gboolean info_only,
+              NMSettingIP6ConfigPrivacy privacy,
+              const char *last_ip4_address)
 {
-	NMDHCPManagerPrivate *priv;
-	NMDHCPClient *client;
+	NMDhcpManagerPrivate *priv;
+	NMDhcpClient *client;
 	gboolean success = FALSE;
 
 	g_return_val_if_fail (self, NULL);
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
-	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (ifindex > 0, NULL);
 	g_return_val_if_fail (uuid != NULL, NULL);
 
 	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
@@ -407,32 +241,34 @@ client_start (NMDHCPManager *self,
 	g_return_val_if_fail (priv->client_type != 0, NULL);
 
 	/* Kill any old client instance */
-	client = get_client_for_iface (self, iface, ipv6);
+	client = get_client_for_ifindex (self, ifindex, ipv6);
 	if (client) {
-		nm_dhcp_client_stop (client, FALSE);
+		g_object_ref (client);
 		remove_client (self, client);
+		nm_dhcp_client_stop (client, FALSE);
+		g_object_unref (client);
 	}
 
 	/* And make a new one */
 	client = g_object_new (priv->client_type,
 	                       NM_DHCP_CLIENT_INTERFACE, iface,
+	                       NM_DHCP_CLIENT_IFINDEX, ifindex,
 	                       NM_DHCP_CLIENT_HWADDR, hwaddr,
 	                       NM_DHCP_CLIENT_IPV6, ipv6,
 	                       NM_DHCP_CLIENT_UUID, uuid,
 	                       NM_DHCP_CLIENT_PRIORITY, priority,
 	                       NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT,
 	                       NULL);
-	g_return_val_if_fail (client != NULL, NULL);
-	add_client (self, client);
+	g_hash_table_insert (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client, g_object_ref (client));
+	g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self);
 
 	if (ipv6)
-		success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, hostname, info_only);
+		success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, hostname, info_only, privacy);
 	else
-		success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname);
+		success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, last_ip4_address);
 
 	if (!success) {
 		remove_client (self, client);
-		g_object_unref (client);
 		client = NULL;
 	}
 
@@ -440,134 +276,105 @@ client_start (NMDHCPManager *self,
 }
 
 static const char *
-get_send_hostname (NMDHCPManager *self, const char *setting_hostname)
+get_send_hostname (NMDhcpManager *self, const char *setting_hostname)
 {
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 
 	/* Always prefer the explicit dhcp-send-hostname if given */
 	return setting_hostname ? setting_hostname : priv->default_hostname;
 }
 
-/* Caller owns a reference to the NMDHCPClient on return */
-NMDHCPClient *
-nm_dhcp_manager_start_ip4 (NMDHCPManager *self,
+/* Caller owns a reference to the NMDhcpClient on return */
+NMDhcpClient *
+nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
                            const char *iface,
+                           int ifindex,
                            const GByteArray *hwaddr,
                            const char *uuid,
-                           guint priority,
-                           NMSettingIP4Config *s_ip4,
+                           guint32 priority,
+                           gboolean send_hostname,
+                           const char *dhcp_hostname,
+                           const char *dhcp_client_id,
                            guint32 timeout,
-                           GByteArray *dhcp_anycast_addr)
+                           const char *dhcp_anycast_addr,
+                           const char *last_ip_address)
 {
-	const char *hostname = NULL, *method;
-	gboolean send_hostname;
+	const char *hostname = NULL;
 
-	g_return_val_if_fail (self, NULL);
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
 
-	method = nm_setting_ip4_config_get_method (s_ip4);
-	g_return_val_if_fail (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0, NULL);
-
-	send_hostname = nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4);
 	if (send_hostname)
-		hostname = get_send_hostname (self, nm_setting_ip4_config_get_dhcp_hostname (s_ip4));
-
-	return client_start (self, iface, hwaddr, uuid, priority, FALSE,
-	                     nm_setting_ip4_config_get_dhcp_client_id (s_ip4),
-	                     timeout, dhcp_anycast_addr, hostname, FALSE);
+		hostname = get_send_hostname (self, dhcp_hostname);
+	return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE,
+	                     dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
+	                     FALSE, 0, last_ip_address);
 }
 
-/* Caller owns a reference to the NMDHCPClient on return */
-NMDHCPClient *
-nm_dhcp_manager_start_ip6 (NMDHCPManager *self,
+/* Caller owns a reference to the NMDhcpClient on return */
+NMDhcpClient *
+nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
                            const char *iface,
+                           int ifindex,
                            const GByteArray *hwaddr,
                            const char *uuid,
-                           guint priority,
-                           NMSettingIP6Config *s_ip6,
+                           guint32 priority,
+                           gboolean send_hostname,
+                           const char *dhcp_hostname,
                            guint32 timeout,
-                           GByteArray *dhcp_anycast_addr,
-                           gboolean info_only)
+                           const char *dhcp_anycast_addr,
+                           gboolean info_only,
+                           NMSettingIP6ConfigPrivacy privacy)
 {
-	const char *hostname;
+	const char *hostname = NULL;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
 
-	hostname = get_send_hostname (self, nm_setting_ip6_config_get_dhcp_hostname (s_ip6));
-
-	return client_start (self, iface, hwaddr, uuid, priority, TRUE,
-	                     NULL, timeout, dhcp_anycast_addr, hostname, info_only);
+	if (send_hostname)
+		hostname = get_send_hostname (self, dhcp_hostname);
+	return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
+	                     NULL, timeout, dhcp_anycast_addr, hostname, info_only,
+	                     privacy, NULL);
 }
 
 void
-nm_dhcp_manager_set_default_hostname (NMDHCPManager *manager, const char *hostname)
+nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostname)
 {
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
 
 	g_clear_pointer (&priv->default_hostname, g_free);
 
 	/* Never send 'localhost'-type names to the DHCP server */
-	if (g_strcmp0 (hostname, "localhost.localdomain") == 0 ||
-	    g_strcmp0 (hostname, "localhost6.localdomain6") == 0)
+	if (!nm_utils_is_specific_hostname (hostname))
 		return;
 
 	priv->default_hostname = g_strdup (hostname);
 }
 
 GSList *
-nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self,
+nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
                                       const char *iface,
                                       const char *uuid,
-                                      gboolean ipv6)
+                                      gboolean ipv6,
+                                      guint32 default_route_metric)
 {
-	NMDHCPManagerPrivate *priv;
+	ClientDesc *desc;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
 	g_return_val_if_fail (iface != NULL, NULL);
 	g_return_val_if_fail (uuid != NULL, NULL);
 
-	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-
-	if (priv->get_lease_ip_configs_func)
-		return priv->get_lease_ip_configs_func (iface, uuid, ipv6);
+	desc = find_client_desc (NULL, NM_DHCP_MANAGER_GET_PRIVATE (self)->client_type);
+	if (desc && desc->get_lease_configs_func)
+		return desc->get_lease_configs_func (iface, uuid, ipv6, default_route_metric);
 	return NULL;
 }
 
-NMIP4Config *
-nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
-                                            const char *iface,
-                                            GHashTable *options,
-                                            const char *reason)
-{
-	NMDHCPClient *client;
-	NMIP4Config *config;
-	GType client_type;
-	GError *error = NULL;
-
-	client_type = get_client_type (dhcp_client, &error);
-	if (!client_type) {
-		nm_log_err (LOGD_DHCP4, "error: %s", error ? error->message : "(unknown)");
-		g_clear_error (&error);
-		return NULL;
-	}
-
-	client = (NMDHCPClient *) g_object_new (client_type,
-	                                        NM_DHCP_CLIENT_INTERFACE, iface,
-	                                        NULL);
-	g_return_val_if_fail (client != NULL, NULL);
-	nm_dhcp_client_new_options (client, options, reason);
-	config = nm_dhcp_client_get_ip4_config (client, TRUE);
-	g_object_unref (client);
-
-	return config;
-}
-
 /***************************************************/
 
-NMDHCPManager *
+NMDhcpManager *
 nm_dhcp_manager_get (void)
 {
-	static NMDHCPManager *singleton = NULL;
+	static NMDhcpManager *singleton = NULL;
 
 	if (G_UNLIKELY (singleton == NULL))
 		singleton = g_object_new (NM_TYPE_DHCP_MANAGER, NULL);
@@ -576,67 +383,48 @@ nm_dhcp_manager_get (void)
 }
 
 static void
-nm_dhcp_manager_init (NMDHCPManager *self)
+nm_dhcp_manager_init (NMDhcpManager *self)
 {
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+	NMConfig *config = nm_config_get ();
 	const char *client;
 	GError *error = NULL;
-#if !HAVE_DBUS_GLIB_100
-	DBusGConnection *g_connection;
-#endif
+	GSList *iter;
 
-	/* Maps DBusGConnection :: DBusGProxy */
-	priv->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+	for (iter = client_descs; iter; iter = iter->next) {
+		ClientDesc *desc = iter->data;
+
+		nm_log_dbg (LOGD_DHCP, "Registered DHCP client '%s' (%s)",
+		            desc->name, g_type_name (desc->gtype));
+	}
 
 	/* Client-specific setup */
-	client = nm_config_get_dhcp_client (nm_config_get ());
-	priv->client_type = get_client_type (client, &error);
+	client = nm_config_get_dhcp_client (config);
+	if (nm_config_get_configure_and_quit (config)) {
+		if (g_strcmp0 (client, "internal") != 0)
+			nm_log_warn (LOGD_DHCP, "Using internal DHCP client since configure-and-quit is set.");
+		client = "internal";
+	}
 
-	if (priv->client_type == NM_TYPE_DHCP_DHCLIENT)
-		priv->get_lease_ip_configs_func = nm_dhcp_dhclient_get_lease_ip_configs;
-	else if (priv->client_type == G_TYPE_INVALID) {
+	priv->client_type = get_client_type (client, &error);
+	if (priv->client_type == G_TYPE_INVALID) {
 		nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
 		             error->message);
+	} else {
+		nm_log_dbg (LOGD_DHCP, "Using DHCP client '%s'", find_client_desc (NULL, priv->client_type)->name);
+
 	}
 	g_clear_error (&error);
 
 	priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 	                                       NULL,
 	                                       (GDestroyNotify) g_object_unref);
-	g_assert (priv->clients);
-
-	priv->dbus_mgr = nm_dbus_manager_get ();
-
-#if HAVE_DBUS_GLIB_100
-	/* Register the socket our DHCP clients will return lease info on */
-	nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
-	priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
-	                                      NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
-	                                      (GCallback) new_connection_cb,
-	                                      self);
-	priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
-	                                      NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
-	                                      (GCallback) dis_connection_cb,
-	                                      self);
-#else
-	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
-	priv->proxy = dbus_g_proxy_new_for_name (g_connection,
-	                                         "org.freedesktop.nm_dhcp_client",
-	                                         "/",
-	                                         NM_DHCP_CLIENT_DBUS_IFACE);
-	g_assert (priv->proxy);
-	dbus_g_proxy_add_signal (priv->proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "Event",
-	                             G_CALLBACK (nm_dhcp_manager_handle_event),
-	                             self,
-	                             NULL);
-#endif
 }
 
 static void
 dispose (GObject *object)
 {
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
+	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
 	GList *values, *iter;
 
 	if (priv->clients) {
@@ -646,30 +434,13 @@ dispose (GObject *object)
 		g_list_free (values);
 	}
 
-	if (priv->new_conn_id) {
-		g_signal_handler_disconnect (priv->dbus_mgr, priv->new_conn_id);
-		priv->new_conn_id = 0;
-	}
-	if (priv->dis_conn_id) {
-		g_signal_handler_disconnect (priv->dbus_mgr, priv->dis_conn_id);
-		priv->dis_conn_id = 0;
-	}
-	priv->dbus_mgr = NULL;
-
-	if (priv->proxies) {
-		g_hash_table_destroy (priv->proxies);
-		priv->proxies = NULL;
-	}
-	if (priv->proxy)
-		g_object_unref (priv->proxy);
-
 	G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
 }
 
 static void
 finalize (GObject *object)
 {
-	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
+	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
 
 	g_free (priv->default_hostname);
 
@@ -680,11 +451,11 @@ finalize (GObject *object)
 }
 
 static void
-nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class)
+nm_dhcp_manager_class_init (NMDhcpManagerClass *manager_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
 
-	g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate));
+	g_type_class_add_private (manager_class, sizeof (NMDhcpManagerPrivate));
 
 	/* virtual methods */
 	object_class->finalize = finalize;
diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h
index efdd4378..bf6cc778 100644
--- a/src/dhcp-manager/nm-dhcp-manager.h
+++ b/src/dhcp-manager/nm-dhcp-manager.h
@@ -19,81 +19,71 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#ifndef NM_DHCP_MANAGER_H
-#define NM_DHCP_MANAGER_H
+#ifndef __NETWORKMANAGER_DHCP_MANAGER_H__
+#define __NETWORKMANAGER_DHCP_MANAGER_H__
 
 #include <glib.h>
 #include <glib-object.h>
 
-#include <nm-setting-ip4-config.h>
-#include <nm-setting-ip6-config.h>
-
 #include "nm-dhcp-client.h"
 #include "nm-ip4-config.h"
 #include "nm-dhcp4-config.h"
 
-typedef enum {
-	NM_DHCP_MANAGER_ERROR_BAD_CLIENT = 0, /*< nick=BadClient >*/
-	NM_DHCP_MANAGER_ERROR_INTERNAL = 1,   /*< nick=InternalError >*/
-} NMDHCPManagerError;
-
-#define NM_DHCP_MANAGER_ERROR (nm_dhcp_manager_error_quark ())
-
-GQuark nm_dhcp_manager_error_quark    (void);
-
-
 #define NM_TYPE_DHCP_MANAGER            (nm_dhcp_manager_get_type ())
-#define NM_DHCP_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManager))
-#define NM_DHCP_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass))
+#define NM_DHCP_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_MANAGER, NMDhcpManager))
+#define NM_DHCP_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_MANAGER, NMDhcpManagerClass))
 #define NM_IS_DHCP_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_MANAGER))
 #define NM_IS_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_MANAGER))
-#define NM_DHCP_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass))
+#define NM_DHCP_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_MANAGER, NMDhcpManagerClass))
 
 typedef struct {
 	GObject parent;
-} NMDHCPManager;
+} NMDhcpManager;
 
 typedef struct {
 	GObjectClass parent;
-} NMDHCPManagerClass;
+} NMDhcpManagerClass;
 
 GType nm_dhcp_manager_get_type (void);
 
-NMDHCPManager *nm_dhcp_manager_get (void);
+NMDhcpManager *nm_dhcp_manager_get (void);
 
-void           nm_dhcp_manager_set_default_hostname (NMDHCPManager *manager,
+void           nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager,
                                                      const char *hostname);
 
-NMDHCPClient * nm_dhcp_manager_start_ip4     (NMDHCPManager *manager,
+NMDhcpClient * nm_dhcp_manager_start_ip4     (NMDhcpManager *manager,
                                               const char *iface,
+                                              int ifindex,
                                               const GByteArray *hwaddr,
                                               const char *uuid,
-                                              guint priority,
-                                              NMSettingIP4Config *s_ip4,
+                                              guint32 priority,
+                                              gboolean send_hostname,
+                                              const char *dhcp_hostname,
+                                              const char *dhcp_client_id,
                                               guint32 timeout,
-                                              GByteArray *dhcp_anycast_addr);
+                                              const char *dhcp_anycast_addr,
+                                              const char *last_ip_address);
 
-NMDHCPClient * nm_dhcp_manager_start_ip6     (NMDHCPManager *manager,
+NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
                                               const char *iface,
+                                              int ifindex,
                                               const GByteArray *hwaddr,
                                               const char *uuid,
-                                              guint priority,
-                                              NMSettingIP6Config *s_ip6,
+                                              guint32 priority,
+                                              gboolean send_hostname,
+                                              const char *dhcp_hostname,
                                               guint32 timeout,
-                                              GByteArray *dhcp_anycast_addr,
-                                              gboolean info_only);
+                                              const char *dhcp_anycast_addr,
+                                              gboolean info_only,
+                                              NMSettingIP6ConfigPrivacy privacy);
 
-GSList *       nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self,
+GSList *       nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
                                                      const char *iface,
                                                      const char *uuid,
-                                                     gboolean ipv6);
+                                                     gboolean ipv6,
+                                                     guint32 default_route_metric);
 
 /* For testing only */
-NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
-                                                         const char *iface,
-                                                         GHashTable *options,
-                                                         const char *reason);
-
 extern const char* nm_dhcp_helper_path;
 
-#endif /* NM_DHCP_MANAGER_H */
+#endif /* __NETWORKMANAGER_DHCP_MANAGER_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c
new file mode 100644
index 00000000..12dc03cc
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-systemd.c
@@ -0,0 +1,849 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <net/if_arp.h>
+
+#include "nm-dhcp-systemd.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "nm-dhcp-utils.h"
+#include "NetworkManagerUtils.h"
+#include "gsystem-local-alloc.h"
+#include "nm-platform.h"
+
+#include "nm-sd-adapt.h"
+
+#include "sd-dhcp-client.h"
+#include "sd-dhcp6-client.h"
+#include "dhcp-protocol.h"
+#include "dhcp-lease-internal.h"
+#include "dhcp6-protocol.h"
+#include "dhcp6-lease-internal.h"
+
+G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
+
+#define NM_DHCP_SYSTEMD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemdPrivate))
+
+typedef struct {
+	struct sd_dhcp_client *client4;
+	struct sd_dhcp6_client *client6;
+	char *lease_file;
+
+	guint timeout_id;
+	guint request_count;
+
+	gboolean privacy;
+} NMDhcpSystemdPrivate;
+
+/************************************************************/
+
+#define DHCP_OPTION_NIS_DOMAIN         40
+#define DHCP_OPTION_NIS_SERVERS        41
+#define DHCP_OPTION_DOMAIN_SEARCH     119
+#define DHCP_OPTION_RFC3442_ROUTES    121
+#define DHCP_OPTION_MS_ROUTES         249
+#define DHCP_OPTION_WPAD              252
+
+/* Internal values */
+#define DHCP_OPTION_IP_ADDRESS       1024
+#define DHCP_OPTION_EXPIRY           1025
+#define DHCP6_OPTION_IP_ADDRESS      1026
+#define DHCP6_OPTION_PREFIXLEN       1027
+#define DHCP6_OPTION_PREFERRED_LIFE  1028
+#define DHCP6_OPTION_MAX_LIFE        1029
+#define DHCP6_OPTION_STARTS          1030
+#define DHCP6_OPTION_LIFE_STARTS     1031
+#define DHCP6_OPTION_RENEW           1032
+#define DHCP6_OPTION_REBIND          1033
+#define DHCP6_OPTION_IAID            1034
+
+typedef struct {
+	guint num;
+	const char *name;
+	gboolean include;
+} ReqOption;
+
+#define REQPREFIX "requested_"
+
+static const ReqOption dhcp4_requests[] = {
+	{ DHCP_OPTION_SUBNET_MASK,            REQPREFIX "subnet_mask",                     TRUE },
+	{ DHCP_OPTION_TIME_OFFSET,            REQPREFIX "time_offset",                     TRUE },
+	{ DHCP_OPTION_ROUTER,                 REQPREFIX "routers",                         TRUE },
+	{ DHCP_OPTION_DOMAIN_NAME_SERVER,     REQPREFIX "domain_name_servers",             TRUE },
+	{ DHCP_OPTION_HOST_NAME,              REQPREFIX "host_name",                       TRUE },
+	{ DHCP_OPTION_DOMAIN_NAME,            REQPREFIX "domain_name",                     TRUE },
+	{ DHCP_OPTION_INTERFACE_MTU,          REQPREFIX "interface_mtu",                   TRUE },
+	{ DHCP_OPTION_BROADCAST,              REQPREFIX "broadcast_address",               TRUE },
+	{ DHCP_OPTION_STATIC_ROUTE,           REQPREFIX "static_routes",                   TRUE },
+	{ DHCP_OPTION_NIS_DOMAIN,             REQPREFIX "nis_domain",                      TRUE },
+	{ DHCP_OPTION_NIS_SERVERS,            REQPREFIX "nis_servers",                     TRUE },
+	{ DHCP_OPTION_NTP_SERVER,             REQPREFIX "ntp_servers",                     TRUE },
+	{ DHCP_OPTION_SERVER_IDENTIFIER,      REQPREFIX "dhcp_server_identifier",          TRUE },
+	{ DHCP_OPTION_DOMAIN_SEARCH,          REQPREFIX "domain_search",                   TRUE },
+	{ DHCP_OPTION_CLASSLESS_STATIC_ROUTE, REQPREFIX "rfc3442_classless_static_routes", TRUE },
+	{ DHCP_OPTION_MS_ROUTES,              REQPREFIX "ms_classless_static_routes",      TRUE },
+	{ DHCP_OPTION_WPAD,                   REQPREFIX "wpad",                            TRUE },
+
+	/* Internal values */
+	{ DHCP_OPTION_IP_ADDRESS_LEASE_TIME, REQPREFIX "expiry",                          FALSE },
+	{ DHCP_OPTION_CLIENT_IDENTIFIER,     REQPREFIX "dhcp_client_identifier",          FALSE },
+	{ DHCP_OPTION_IP_ADDRESS,            REQPREFIX "ip_address",                      FALSE },
+	{ 0, NULL, FALSE }
+};
+
+static const ReqOption dhcp6_requests[] = {
+	{ DHCP6_OPTION_CLIENTID,       REQPREFIX "dhcp6_client_id",     TRUE },
+
+	/* Don't request server ID by default; some servers don't reply to
+	 * Information Requests that request the Server ID.
+	 */
+	{ DHCP6_OPTION_SERVERID,       REQPREFIX "dhcp6_server_id",     FALSE },
+
+	{ DHCP6_OPTION_DNS_SERVERS,    REQPREFIX "dhcp6_name_servers",  TRUE },
+	{ DHCP6_OPTION_DOMAIN_LIST,    REQPREFIX "dhcp6_domain_search", TRUE },
+	{ DHCP6_OPTION_SNTP_SERVERS,   REQPREFIX "dhcp6_sntp_servers",  TRUE },
+
+	/* Internal values */
+	{ DHCP6_OPTION_IP_ADDRESS,     REQPREFIX "ip6_address",         FALSE },
+	{ DHCP6_OPTION_PREFIXLEN,      REQPREFIX "ip6_prefixlen",       FALSE },
+	{ DHCP6_OPTION_PREFERRED_LIFE, REQPREFIX "preferred_life",      FALSE },
+	{ DHCP6_OPTION_MAX_LIFE,       REQPREFIX "max_life",            FALSE },
+	{ DHCP6_OPTION_STARTS,         REQPREFIX "starts",              FALSE },
+	{ DHCP6_OPTION_LIFE_STARTS,    REQPREFIX "life_starts",         FALSE },
+	{ DHCP6_OPTION_RENEW,          REQPREFIX "renew",               FALSE },
+	{ DHCP6_OPTION_REBIND,         REQPREFIX "rebind",              FALSE },
+	{ DHCP6_OPTION_IAID,           REQPREFIX "iaid",                FALSE },
+	{ 0, NULL, FALSE }
+};
+
+static void
+take_option (GHashTable *options,
+             const ReqOption *requests,
+             guint option,
+             char *value)
+{
+	guint i;
+
+	g_return_if_fail (value != NULL);
+
+	for (i = 0; requests[i].name; i++) {
+		if (requests[i].num == option) {
+			g_hash_table_insert (options,
+			                     (gpointer) (requests[i].name + STRLEN (REQPREFIX)),
+			                     value);
+			break;
+		}
+	}
+	/* Option should always be found */
+	g_assert (requests[i].name);
+}
+
+static void
+add_option (GHashTable *options, const ReqOption *requests, guint option, const char *value)
+{
+	if (options)
+		take_option (options, requests, option, g_strdup (value));
+}
+
+static void
+add_option_u32 (GHashTable *options, const ReqOption *requests, guint option, guint32 value)
+{
+	if (options)
+		take_option (options, requests, option, g_strdup_printf ("%u", value));
+}
+
+static void
+add_option_u64 (GHashTable *options, const ReqOption *requests, guint option, guint64 value)
+{
+	if (options)
+		take_option (options, requests, option, g_strdup_printf ("%" G_GUINT64_FORMAT, value));
+}
+
+static void
+add_requests_to_options (GHashTable *options, const ReqOption *requests)
+{
+	guint i;
+
+	for (i = 0; options && requests[i].name; i++) {
+		if (requests[i].include)
+			g_hash_table_insert (options, (gpointer) requests[i].name, g_strdup ("1"));
+	}
+}
+
+#define LOG_LEASE(domain, ...) \
+G_STMT_START { \
+	if (log_lease) { \
+		nm_log (LOGL_INFO, (domain), __VA_ARGS__); \
+	} \
+} G_STMT_END
+
+static NMIP4Config *
+lease_to_ip4_config (sd_dhcp_lease *lease,
+                     GHashTable *options,
+                     guint32 default_priority,
+                     gboolean log_lease,
+                     GError **error)
+{
+	NMIP4Config *ip4_config = NULL;
+	struct in_addr tmp_addr;
+	const struct in_addr *addr_list;
+	char buf[INET_ADDRSTRLEN];
+	const char *str;
+	guint32 lifetime = 0, i;
+	NMPlatformIP4Address address;
+	GString *l;
+	struct sd_dhcp_route *routes;
+	guint16 mtu;
+	int r, num;
+	guint64 end_time;
+
+	g_return_val_if_fail (lease != NULL, NULL);
+
+	ip4_config = nm_ip4_config_new ();
+
+	/* Address */
+	sd_dhcp_lease_get_address (lease, &tmp_addr);
+	memset (&address, 0, sizeof (address));
+	address.address = tmp_addr.s_addr;
+	str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
+	LOG_LEASE (LOGD_DHCP4, "  address %s", str);
+	add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str);
+
+	/* Prefix/netmask */
+	sd_dhcp_lease_get_netmask (lease, &tmp_addr);
+	address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr);
+	LOG_LEASE (LOGD_DHCP4, "  plen %d", address.plen);
+	add_option (options,
+	            dhcp4_requests,
+	            DHCP_OPTION_SUBNET_MASK,
+	            nm_utils_inet4_ntop (tmp_addr.s_addr, NULL));
+
+	/* Lease time */
+	sd_dhcp_lease_get_lifetime (lease, &lifetime);
+	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
+	address.lifetime = address.preferred = lifetime;
+	end_time = (guint64) time (NULL) + lifetime;
+	LOG_LEASE (LOGD_DHCP4, "  expires in %" G_GUINT32_FORMAT " seconds", lifetime);
+	add_option_u64 (options,
+	                dhcp4_requests,
+	                DHCP_OPTION_IP_ADDRESS_LEASE_TIME,
+	                end_time);
+
+	address.source = NM_IP_CONFIG_SOURCE_DHCP;
+	nm_ip4_config_add_address (ip4_config, &address);
+
+	/* Gateway */
+	r = sd_dhcp_lease_get_router (lease, &tmp_addr);
+	if (r == 0) {
+		nm_ip4_config_set_gateway (ip4_config, tmp_addr.s_addr);
+		str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
+		LOG_LEASE (LOGD_DHCP4, "  gateway %s", str);
+		add_option (options, dhcp4_requests, DHCP_OPTION_ROUTER, str);
+	}
+
+	/* DNS Servers */
+	num = sd_dhcp_lease_get_dns (lease, &addr_list);
+	if (num > 0) {
+		l = g_string_sized_new (30);
+		for (i = 0; i < num; i++) {
+			if (addr_list[i].s_addr) {
+				nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr);
+				str = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL);
+				LOG_LEASE (LOGD_DHCP4, "  nameserver '%s'", str);
+				g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
+			}
+		}
+		if (l->len)
+			add_option (options, dhcp4_requests, DHCP_OPTION_DOMAIN_NAME_SERVER, l->str);
+		g_string_free (l, TRUE);
+	}
+
+	/* Domain Name */
+	r = sd_dhcp_lease_get_domainname (lease, &str);
+	if (r == 0) {
+		/* Multiple domains sometimes stuffed into the option */
+		char **domains = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = domains; *s; s++) {
+			LOG_LEASE (LOGD_DHCP4, "  domain name '%s'", *s);
+			nm_ip4_config_add_domain (ip4_config, *s);
+		}
+		g_strfreev (domains);
+		add_option (options, dhcp4_requests, DHCP_OPTION_DOMAIN_NAME, str);
+	}
+
+	/* Hostname */
+	r = sd_dhcp_lease_get_hostname (lease, &str);
+	if (r == 0) {
+		LOG_LEASE (LOGD_DHCP4, "  hostname '%s'", str);
+		add_option (options, dhcp4_requests, DHCP_OPTION_HOST_NAME, str);
+	}
+
+	/* Routes */
+	num = sd_dhcp_lease_get_routes (lease, &routes);
+	if (num > 0) {
+		l = g_string_sized_new (30);
+		for (i = 0; i < num; i++) {
+			NMPlatformIP4Route route;
+			const char *gw_str;
+
+			memset (&route, 0, sizeof (route));
+			route.network = routes[i].dst_addr.s_addr;
+			route.plen = routes[i].dst_prefixlen;
+			route.gateway = routes[i].gw_addr.s_addr;
+			route.source = NM_IP_CONFIG_SOURCE_DHCP;
+			route.metric = default_priority;
+			nm_ip4_config_add_route (ip4_config, &route);
+
+			str = nm_utils_inet4_ntop (route.network, buf);
+			gw_str = nm_utils_inet4_ntop (route.gateway, NULL);
+			LOG_LEASE (LOGD_DHCP4, "  static route %s/%d gw %s", str, route.plen, gw_str);
+
+			g_string_append_printf (l, "%s%s/%d %s", l->len ? " " : "", str, route.plen, gw_str);
+		}
+		add_option (options, dhcp4_requests, DHCP_OPTION_RFC3442_ROUTES, l->str);
+		g_string_free (l, TRUE);
+	}
+
+	/* MTU */
+	r = sd_dhcp_lease_get_mtu (lease, &mtu);
+	if (r == 0 && mtu) {
+		nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP);
+		add_option_u32 (options, dhcp4_requests, DHCP_OPTION_INTERFACE_MTU, mtu);
+		LOG_LEASE (LOGD_DHCP4, "  mtu %u", mtu);
+	}
+
+	/* NTP servers */
+	num = sd_dhcp_lease_get_ntp (lease, &addr_list);
+	if (num > 0) {
+		l = g_string_sized_new (30);
+		for (i = 0; i < num; i++) {
+			str = nm_utils_inet4_ntop (addr_list[i].s_addr, buf);
+			LOG_LEASE (LOGD_DHCP4, "  ntp server '%s'", str);
+			g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
+		}
+		add_option (options, dhcp4_requests, DHCP_OPTION_NTP_SERVER, l->str);
+		g_string_free (l, TRUE);
+	}
+
+	return ip4_config;
+}
+
+/************************************************************/
+
+static char *
+get_leasefile_path (const char *iface, const char *uuid, gboolean ipv6)
+{
+	return g_strdup_printf (NMSTATEDIR "/internal%s-%s-%s.lease",
+	                        ipv6 ? "6" : "",
+	                        uuid,
+	                        iface);
+}
+
+static GSList *
+nm_dhcp_systemd_get_lease_ip_configs (const char *iface,
+                                      const char *uuid,
+                                      gboolean ipv6,
+                                      guint32 default_route_metric)
+{
+	GSList *leases = NULL;
+	gs_free char *path = NULL;
+	sd_dhcp_lease *lease = NULL;
+	NMIP4Config *ip4_config;
+	int r;
+
+	if (ipv6)
+		return NULL;
+
+	path = get_leasefile_path (iface, uuid, FALSE);
+	r = sd_dhcp_lease_load (&lease, path);
+	if (r == 0 && lease) {
+		ip4_config = lease_to_ip4_config (lease, NULL, default_route_metric, FALSE, NULL);
+		if (ip4_config)
+			leases = g_slist_append (leases, ip4_config);
+	}
+
+	return leases;
+}
+
+/************************************************************/
+
+static void
+_save_client_id (NMDhcpSystemd *self,
+                 uint8_t type,
+                 const uint8_t *client_id,
+                 size_t len)
+{
+	gs_unref_bytes GBytes *b = NULL;
+	gs_free char *buf = NULL;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (client_id != NULL);
+	g_return_if_fail (len > 0);
+
+	if (!nm_dhcp_client_get_client_id (NM_DHCP_CLIENT (self))) {
+		buf = g_malloc (len + 1);
+		buf[0] = type;
+		memcpy (buf + 1, client_id, len);
+		b = g_bytes_new (buf, len + 1);
+		nm_dhcp_client_set_client_id (NM_DHCP_CLIENT (self), b);
+	}
+}
+
+static void
+bound4_handle (NMDhcpSystemd *self)
+{
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
+	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
+	sd_dhcp_lease *lease;
+	NMIP4Config *ip4_config;
+	GHashTable *options;
+	GError *error = NULL;
+	int r;
+
+	nm_log_dbg (LOGD_DHCP4, "(%s): lease available", iface);
+
+	r = sd_dhcp_client_get_lease (priv->client4, &lease);
+	if (r < 0 || !lease) {
+		nm_log_warn (LOGD_DHCP4, "(%s): no lease!", iface);
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
+		return;
+	}
+
+	options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+	ip4_config = lease_to_ip4_config (lease,
+	                                  options,
+	                                  nm_dhcp_client_get_priority (NM_DHCP_CLIENT (self)),
+	                                  TRUE,
+	                                  &error);
+	if (ip4_config) {
+		const uint8_t *client_id = NULL;
+		size_t client_id_len = 0;
+		uint8_t type = 0;
+
+		add_requests_to_options (options, dhcp4_requests);
+		sd_dhcp_lease_save (lease, priv->lease_file);
+
+		sd_dhcp_client_get_client_id(priv->client4, &type, &client_id, &client_id_len);
+		if (client_id)
+			_save_client_id (self, type, client_id, client_id_len);
+
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
+		                          NM_DHCP_STATE_BOUND,
+		                          G_OBJECT (ip4_config),
+		                          options);
+	} else {
+		nm_log_warn (LOGD_DHCP4, "(%s): %s", iface, error->message);
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
+		g_clear_error (&error);
+	}
+
+	sd_dhcp_lease_unref (lease);
+	g_hash_table_destroy (options);
+	g_clear_object (&ip4_config);
+}
+
+static void
+dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data)
+{
+	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data);
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
+	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
+
+	g_assert (priv->client4 == client);
+
+	nm_log_dbg (LOGD_DHCP4, "(%s): DHCPv4 client event %d", iface, event);
+
+	switch (event) {
+	case DHCP_EVENT_EXPIRED:
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_EXPIRE, NULL, NULL);
+		break;
+	case DHCP_EVENT_STOP:
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL);
+		break;
+	case DHCP_EVENT_RENEW:
+	case DHCP_EVENT_IP_CHANGE:
+	case DHCP_EVENT_IP_ACQUIRE:
+		bound4_handle (self);
+		break;
+	default:
+		nm_log_warn (LOGD_DHCP4, "(%s): unhandled DHCP event %d", iface, event);
+		break;
+	}
+}
+
+static guint16
+get_arp_type (const GByteArray *hwaddr)
+{
+	if (hwaddr->len == ETH_ALEN)
+		return ARPHRD_ETHER;
+	else if (hwaddr->len == INFINIBAND_ALEN)
+		return ARPHRD_INFINIBAND;
+	else
+		g_assert_not_reached ();
+}
+
+static gboolean
+ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
+{
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
+	const char *iface = nm_dhcp_client_get_iface (client);
+	const GByteArray *hwaddr;
+	sd_dhcp_lease *lease = NULL;
+	GBytes *override_client_id;
+	const uint8_t *client_id = NULL;
+	size_t client_id_len = 0;
+	struct in_addr last_addr = { 0 };
+	const char *hostname;
+	int r, i;
+
+	g_assert (priv->client4 == NULL);
+	g_assert (priv->client6 == NULL);
+
+	g_free (priv->lease_file);
+	priv->lease_file = get_leasefile_path (iface, nm_dhcp_client_get_uuid (client), FALSE);
+
+	r = sd_dhcp_client_new (&priv->client4);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP4, "(%s): failed to create DHCPv4 client (%d)", iface, r);
+		return FALSE;
+	}
+
+	r = sd_dhcp_client_attach_event (priv->client4, NULL, 0);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP4, "(%s): failed to attach DHCP event (%d)", iface, r);
+		goto error;
+	}
+
+	hwaddr = nm_dhcp_client_get_hw_addr (client);
+	if (hwaddr) {
+		r = sd_dhcp_client_set_mac (priv->client4,
+		                            hwaddr->data,
+		                            hwaddr->len,
+		                            get_arp_type (hwaddr));
+		if (r < 0) {
+			nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP MAC address (%d)", iface, r);
+			goto error;
+		}
+	}
+
+	r = sd_dhcp_client_set_index (priv->client4, nm_dhcp_client_get_ifindex (client));
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP ifindex (%d)", iface, r);
+		goto error;
+	}
+
+	r = sd_dhcp_client_set_callback (priv->client4, dhcp_event_cb, client);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP callback (%d)", iface, r);
+		goto error;
+	}
+
+	r = sd_dhcp_client_set_request_broadcast (priv->client4, true);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP broadcast (%d)", iface, r);
+		goto error;
+	}
+
+	sd_dhcp_lease_load (&lease, priv->lease_file);
+
+	if (last_ip4_address)
+		inet_pton (AF_INET, last_ip4_address, &last_addr);
+	else if (lease)
+		sd_dhcp_lease_get_address (lease, &last_addr);
+
+	if (last_addr.s_addr) {
+		r = sd_dhcp_client_set_request_address (priv->client4, &last_addr);
+		if (r < 0) {
+			nm_log_warn (LOGD_DHCP4, "(%s): failed to set last IPv4 address (%d)", iface, r);
+			goto error;
+		}
+	}
+
+	override_client_id = nm_dhcp_client_get_client_id (client);
+	if (override_client_id) {
+		client_id = g_bytes_get_data (override_client_id, &client_id_len);
+		g_assert (client_id && client_id_len);
+		sd_dhcp_client_set_client_id (priv->client4,
+		                              client_id[0],
+		                              client_id + 1,
+		                              client_id_len - 1);
+	} else if (lease) {
+		r = sd_dhcp_lease_get_client_id (lease, &client_id, &client_id_len);
+		if (r == 0 && client_id_len) {
+			sd_dhcp_client_set_client_id (priv->client4,
+			                              client_id[0],
+			                              client_id + 1,
+			                              client_id_len - 1);
+			_save_client_id (NM_DHCP_SYSTEMD (client),
+			                 client_id[0],
+			                 client_id + 1,
+			                 client_id_len - 1);
+		}
+	}
+
+	if (lease)
+		sd_dhcp_lease_unref (lease);
+
+	/* Add requested options */
+	for (i = 0; dhcp4_requests[i].name; i++) {
+		if (dhcp4_requests[i].include)
+			sd_dhcp_client_set_request_option (priv->client4, dhcp4_requests[i].num);
+	}
+
+	hostname = nm_dhcp_client_get_hostname (client);
+	if (hostname) {
+		r = sd_dhcp_client_set_hostname (priv->client4, hostname);
+		if (r < 0) {
+			nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP hostname (%d)", iface, r);
+			goto error;
+		}
+	}
+
+	r = sd_dhcp_client_start (priv->client4);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP4, "(%s): failed to start DHCP (%d)", iface, r);
+		goto error;
+	}
+
+	return TRUE;
+
+error:
+	sd_dhcp_client_unref (priv->client4);
+	priv->client4 = NULL;
+	return FALSE;
+}
+
+static void
+bound6_handle (NMDhcpSystemd *self)
+{
+	/* not yet supported... */
+	nm_log_warn (LOGD_DHCP6, "(%s): internal DHCP does not yet support DHCPv6",
+	             nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)));
+	nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
+}
+
+static void
+dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
+{
+	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data);
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
+	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
+
+	g_assert (priv->client6 == client);
+
+	nm_log_dbg (LOGD_DHCP6, "(%s): DHCPv6 client event %d", iface, event);
+
+	switch (event) {
+	case DHCP6_EVENT_RETRANS_MAX:
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_TIMEOUT, NULL, NULL);
+		break;
+	case DHCP6_EVENT_RESEND_EXPIRE:
+	case DHCP6_EVENT_STOP:
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL);
+		break;
+	case DHCP6_EVENT_IP_ACQUIRE:
+		bound6_handle (self);
+		break;
+	default:
+		nm_log_warn (LOGD_DHCP6, "(%s): unhandled DHCPv6 event %d", iface, event);
+		break;
+	}
+}
+
+static gboolean
+ip6_start (NMDhcpClient *client,
+           const char *dhcp_anycast_addr,
+           gboolean info_only,
+           NMSettingIP6ConfigPrivacy privacy,
+           const GByteArray *duid)
+{
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
+	const char *iface = nm_dhcp_client_get_iface (client);
+	const GByteArray *hwaddr;
+	int r, i;
+
+	g_assert (priv->client4 == NULL);
+	g_assert (priv->client6 == NULL);
+	g_return_val_if_fail (duid != NULL, FALSE);
+
+	g_free (priv->lease_file);
+	priv->lease_file = get_leasefile_path (iface, nm_dhcp_client_get_uuid (client), TRUE);
+
+	r = sd_dhcp6_client_new (&priv->client6);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to create DHCPv6 client (%d)", iface, r);
+		return FALSE;
+	}
+
+	/* NM stores the entire DUID which includes the uint16 "type", while systemd
+	 * wants the type passed separately from the following data.
+	 */
+	r = sd_dhcp6_client_set_duid (priv->client6,
+	                              ntohs (((const guint16 *) duid->data)[0]),
+	                              duid->data + 2,
+	                              duid->len - 2);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to create DHCPv6 client (%d)", iface, r);
+		return FALSE;
+	}
+
+	r = sd_dhcp6_client_attach_event (priv->client6, NULL, 0);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to attach DHCP event (%d)", iface, r);
+		goto error;
+	}
+
+	hwaddr = nm_dhcp_client_get_hw_addr (client);
+	if (hwaddr) {
+		r = sd_dhcp6_client_set_mac (priv->client6,
+		                             hwaddr->data,
+		                             hwaddr->len,
+		                             get_arp_type (hwaddr));
+		if (r < 0) {
+			nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP MAC address (%d)", iface, r);
+			goto error;
+		}
+	}
+
+	r = sd_dhcp6_client_set_index (priv->client6, nm_dhcp_client_get_ifindex (client));
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP ifindex (%d)", iface, r);
+		goto error;
+	}
+
+	r = sd_dhcp6_client_set_ifname (priv->client6, iface);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP ifname (%d)", iface, r);
+		goto error;
+	}
+
+	r = sd_dhcp6_client_set_callback (priv->client6, dhcp6_event_cb, client);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP callback (%d)", iface, r);
+		goto error;
+	}
+
+	/* Add requested options */
+	for (i = 0; dhcp6_requests[i].name; i++) {
+		if (dhcp6_requests[i].include)
+			sd_dhcp6_client_set_request_option (priv->client6, dhcp6_requests[i].num);
+	}
+
+	r = sd_dhcp6_client_start (priv->client6);
+	if (r < 0) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to start DHCP (%d)", iface, r);
+		goto error;
+	}
+
+	return TRUE;
+
+error:
+	sd_dhcp6_client_unref (priv->client6);
+	priv->client6 = NULL;
+	return FALSE;
+}
+
+static void
+stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
+{
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
+	int r = 0;
+
+	if (priv->client4)
+		r = sd_dhcp_client_stop (priv->client4);
+	else if (priv->client6)
+		r = sd_dhcp6_client_stop (priv->client6);
+
+	if (r) {
+		nm_log_warn (priv->client6 ? LOGD_DHCP6 : LOGD_DHCP4,
+			         "(%s): failed to stop DHCP client (%d)",
+			         nm_dhcp_client_get_iface (client),
+			         r);
+	}
+}
+
+/***************************************************/
+
+static void
+nm_dhcp_systemd_init (NMDhcpSystemd *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (object);
+
+	g_clear_pointer (&priv->lease_file, g_free);
+
+	if (priv->client4) {
+		sd_dhcp_client_stop (priv->client4);
+		sd_dhcp_client_unref (priv->client4);
+		priv->client4 = NULL;
+	}
+
+	if (priv->client6) {
+		sd_dhcp6_client_stop (priv->client6);
+		sd_dhcp6_client_unref (priv->client6);
+		priv->client6 = NULL;
+	}
+
+	G_OBJECT_CLASS (nm_dhcp_systemd_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_systemd_class_init (NMDhcpSystemdClass *sdhcp_class)
+{
+	NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS (sdhcp_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (sdhcp_class);
+
+	g_type_class_add_private (sdhcp_class, sizeof (NMDhcpSystemdPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+
+	client_class->ip4_start = ip4_start;
+	client_class->ip6_start = ip6_start;
+	client_class->stop = stop;
+}
+
+static void __attribute__((constructor))
+register_dhcp_dhclient (void)
+{
+	g_type_init ();
+	_nm_dhcp_client_register (NM_TYPE_DHCP_SYSTEMD,
+	                          "internal",
+	                          NULL,
+	                          nm_dhcp_systemd_get_lease_ip_configs);
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-systemd.h b/src/dhcp-manager/nm-dhcp-systemd.h
new file mode 100644
index 00000000..2a7a463e
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-systemd.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_SYSTEMD_H
+#define NM_DHCP_SYSTEMD_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-dhcp-client.h"
+
+#define NM_TYPE_DHCP_SYSTEMD            (nm_dhcp_systemd_get_type ())
+#define NM_DHCP_SYSTEMD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemd))
+#define NM_DHCP_SYSTEMD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemdClass))
+#define NM_IS_DHCP_SYSTEMD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_SYSTEMD))
+#define NM_IS_DHCP_SYSTEMD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_SYSTEMD))
+#define NM_DHCP_SYSTEMD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemdClass))
+
+typedef struct {
+	NMDhcpClient parent;
+} NMDhcpSystemd;
+
+typedef struct {
+	NMDhcpClientClass parent;
+} NMDhcpSystemdClass;
+
+GType nm_dhcp_systemd_get_type (void);
+
+#endif /* NM_DHCP_SYSTEMD_H */
+
diff --git a/src/dhcp-manager/nm-dhcp-utils.c b/src/dhcp-manager/nm-dhcp-utils.c
new file mode 100644
index 00000000..5aa25ff0
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-utils.c
@@ -0,0 +1,732 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "nm-logging.h"
+#include "nm-dhcp-utils.h"
+#include "nm-utils.h"
+#include "NetworkManagerUtils.h"
+#include "nm-platform.h"
+
+/********************************************/
+
+static gboolean
+ip4_process_dhcpcd_rfc3442_routes (const char *str,
+                                   guint32 priority,
+                                   NMIP4Config *ip4_config,
+                                   guint32 *gwaddr)
+{
+	char **routes, **r;
+	gboolean have_routes = FALSE;
+
+	routes = g_strsplit (str, " ", 0);
+	if (g_strv_length (routes) == 0)
+		goto out;
+
+	if ((g_strv_length (routes) % 2) != 0) {
+		nm_log_warn (LOGD_DHCP4, "  classless static routes provided, but invalid");
+		goto out;
+	}
+
+	for (r = routes; *r; r += 2) {
+		char *slash;
+		NMPlatformIP4Route route;
+		int rt_cidr = 32;
+		guint32 rt_addr, rt_route;
+
+		slash = strchr(*r, '/');
+		if (slash) {
+			*slash = '\0';
+			errno = 0;
+			rt_cidr = strtol (slash + 1, NULL, 10);
+			if ((errno == EINVAL) || (errno == ERANGE)) {
+				nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
+				continue;
+			}
+		}
+		if (inet_pton (AF_INET, *r, &rt_addr) <= 0) {
+			nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r);
+			continue;
+		}
+		if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) {
+			nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
+			continue;
+		}
+
+		have_routes = TRUE;
+		if (rt_cidr == 0 && rt_addr == 0) {
+			/* FIXME: how to handle multiple routers? */
+			*gwaddr = rt_route;
+		} else {
+			nm_log_info (LOGD_DHCP4, "  classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
+			memset (&route, 0, sizeof (route));
+			route.network = rt_addr;
+			route.plen = rt_cidr;
+			route.gateway = rt_route;
+			route.source = NM_IP_CONFIG_SOURCE_DHCP;
+			route.metric = priority;
+			nm_ip4_config_add_route (ip4_config, &route);
+		}
+	}
+
+out:
+	g_strfreev (routes);
+	return have_routes;
+}
+
+static const char **
+process_dhclient_rfc3442_route (const char **octets,
+                                NMPlatformIP4Route *route,
+                                gboolean *success)
+{
+	const char **o = octets;
+	int addr_len = 0, i = 0;
+	long int tmp;
+	char *next_hop;
+	guint32 tmp_addr;
+
+	*success = FALSE;
+
+	if (!*o)
+		return o; /* no prefix */
+
+	tmp = strtol (*o, NULL, 10);
+	if (tmp < 0 || tmp > 32)  /* 32 == max IP4 prefix length */
+		return o;
+
+	memset (route, 0, sizeof (*route));
+	route->plen = tmp;
+	o++;
+
+	if (tmp > 0)
+		addr_len = ((tmp - 1) / 8) + 1;
+
+	/* ensure there's at least the address + next hop left */
+	if (g_strv_length ((char **) o) < addr_len + 4)
+		goto error;
+
+	if (tmp) {
+		const char *addr[4] = { "0", "0", "0", "0" };
+		char *str_addr;
+
+		for (i = 0; i < addr_len; i++)
+			addr[i] = *o++;
+
+		str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL);
+		if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) {
+			g_free (str_addr);
+			goto error;
+		}
+		tmp_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp);
+		route->network = tmp_addr;
+	}
+
+	/* Handle next hop */
+	next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL);
+	if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) {
+		g_free (next_hop);
+		goto error;
+	}
+	route->gateway = tmp_addr;
+	g_free (next_hop);
+
+	*success = TRUE;
+	return o + 4; /* advance to past the next hop */
+
+error:
+	return o;
+}
+
+static gboolean
+ip4_process_dhclient_rfc3442_routes (const char *str,
+                                     guint32 priority,
+                                     NMIP4Config *ip4_config,
+                                     guint32 *gwaddr)
+{
+	char **octets, **o;
+	gboolean have_routes = FALSE;
+	NMPlatformIP4Route route;
+	gboolean success;
+
+	o = octets = g_strsplit_set (str, " .", 0);
+	if (g_strv_length (octets) < 5) {
+		nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+		goto out;
+	}
+
+	while (*o) {
+		memset (&route, 0, sizeof (route));
+		o = (char **) process_dhclient_rfc3442_route ((const char **) o, &route, &success);
+		if (!success) {
+			nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes");
+			break;
+		}
+
+		have_routes = TRUE;
+		if (!route.plen) {
+			/* gateway passed as classless static route */
+			*gwaddr = route.gateway;
+		} else {
+			char addr[INET_ADDRSTRLEN];
+
+			/* normal route */
+			route.source = NM_IP_CONFIG_SOURCE_DHCP;
+			route.metric = priority;
+			nm_ip4_config_add_route (ip4_config, &route);
+
+			nm_log_info (LOGD_DHCP4, "  classless static route %s/%d gw %s",
+			             nm_utils_inet4_ntop (route.network, addr), route.plen,
+			             nm_utils_inet4_ntop (route.gateway, NULL));
+		}
+	}
+
+out:
+	g_strfreev (octets);
+	return have_routes;
+}
+
+static gboolean
+ip4_process_classless_routes (GHashTable *options,
+                              guint32 priority,
+                              NMIP4Config *ip4_config,
+                              guint32 *gwaddr)
+{
+	const char *str, *p;
+
+	g_return_val_if_fail (options != NULL, FALSE);
+	g_return_val_if_fail (ip4_config != NULL, FALSE);
+
+	*gwaddr = 0;
+
+	/* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a
+	 * slightly different format:
+	 *
+	 * option classless-static-routes = array of (destination-descriptor ip-address);
+	 *
+	 * which results in:
+	 *
+	 * 0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6
+	 *
+	 * dhcpcd supports classless static routes natively and uses this same
+	 * option identifier with the following format:
+	 *
+	 * 192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41
+	 */
+	str = g_hash_table_lookup (options, "classless_static_routes");
+
+	/* dhclient doesn't have actual support for rfc3442 classless static routes
+	 * upstream.  Thus, people resort to defining the option in dhclient.conf
+	 * and using arbitrary formats like so:
+	 *
+	 * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
+	 *
+	 * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html
+	 */
+	if (!str)
+		str = g_hash_table_lookup (options, "rfc3442_classless_static_routes");
+
+	/* Microsoft version; same as rfc3442 but with a different option # (249) */
+	if (!str)
+		str = g_hash_table_lookup (options, "ms_classless_static_routes");
+
+	if (!str || !strlen (str))
+		return FALSE;
+
+	p = str;
+	while (*p) {
+		if (!g_ascii_isdigit (*p) && (*p != ' ') && (*p != '.') && (*p != '/')) {
+			nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+			return FALSE;
+		}
+		p++;
+	};
+
+	if (strchr (str, '/')) {
+		/* dhcpcd format */
+		return ip4_process_dhcpcd_rfc3442_routes (str, priority, ip4_config, gwaddr);
+	}
+
+	return ip4_process_dhclient_rfc3442_routes (str, priority, ip4_config, gwaddr);
+}
+
+static void
+process_classful_routes (GHashTable *options, guint32 priority, NMIP4Config *ip4_config)
+{
+	const char *str;
+	char **searches, **s;
+
+	str = g_hash_table_lookup (options, "static_routes");
+	if (!str)
+		return;
+
+	searches = g_strsplit (str, " ", 0);
+	if ((g_strv_length (searches) % 2)) {
+		nm_log_info (LOGD_DHCP, "  static routes provided, but invalid");
+		goto out;
+	}
+
+	for (s = searches; *s; s += 2) {
+		NMPlatformIP4Route route;
+		guint32 rt_addr, rt_route;
+
+		if (inet_pton (AF_INET, *s, &rt_addr) <= 0) {
+			nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route address: '%s'", *s);
+			continue;
+		}
+		if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) {
+			nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route gateway: '%s'", *(s + 1));
+			continue;
+		}
+
+		// FIXME: ensure the IP address and route are sane
+
+		memset (&route, 0, sizeof (route));
+		route.network = rt_addr;
+		/* RFC 2132, updated by RFC 3442:
+		   The Static Routes option (option 33) does not provide a subnet mask
+		   for each route - it is assumed that the subnet mask is implicit in
+		   whatever network number is specified in each route entry */
+		route.plen = nm_utils_ip4_get_default_prefix (rt_addr);
+		if (rt_addr & ~nm_utils_ip4_prefix_to_netmask (route.plen)) {
+			/* RFC 943: target not "this network"; using host routing */
+			route.plen = 32;
+		}
+		route.gateway = rt_route;
+		route.source = NM_IP_CONFIG_SOURCE_DHCP;
+		route.metric = priority;
+
+		nm_ip4_config_add_route (ip4_config, &route);
+		nm_log_info (LOGD_DHCP, "  static route %s",
+		             nm_platform_ip4_route_to_string (&route));
+	}
+
+out:
+	g_strfreev (searches);
+}
+
+static void
+process_domain_search (const char *str, GFunc add_func, gpointer user_data)
+{
+	char **searches, **s;
+	char *unescaped, *p;
+	int i;
+
+	g_return_if_fail (str != NULL);
+	g_return_if_fail (add_func != NULL);
+
+	p = unescaped = g_strdup (str);
+	do {
+		p = strstr (p, "\\032");
+		if (!p)
+			break;
+
+		/* Clear the escaped space with real spaces */
+		for (i = 0; i < 4; i++)
+			*p++ = ' ';
+	} while (*p++);
+
+	if (strchr (unescaped, '\\')) {
+		nm_log_warn (LOGD_DHCP, "  invalid domain search: '%s'", unescaped);
+		goto out;
+	}
+
+	searches = g_strsplit (unescaped, " ", 0);
+	for (s = searches; *s; s++) {
+		if (strlen (*s)) {
+			nm_log_info (LOGD_DHCP, "  domain search '%s'", *s);
+			add_func (*s, user_data);
+		}
+	}
+	g_strfreev (searches);
+
+out:
+	g_free (unescaped);
+}
+
+static void
+ip4_add_domain_search (gpointer data, gpointer user_data)
+{
+	nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data);
+}
+
+NMIP4Config *
+nm_dhcp_utils_ip4_config_from_options (const char *iface,
+                                       GHashTable *options,
+                                       guint32 priority)
+{
+	NMIP4Config *ip4_config = NULL;
+	guint32 tmp_addr;
+	NMPlatformIP4Address address;
+	char *str = NULL;
+	guint32 gwaddr = 0, plen = 0;
+
+	g_return_val_if_fail (options != NULL, NULL);
+
+	ip4_config = nm_ip4_config_new ();
+	memset (&address, 0, sizeof (address));
+	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
+
+	str = g_hash_table_lookup (options, "ip_address");
+	if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
+		address.address = tmp_addr;
+		nm_log_info (LOGD_DHCP4, "  address %s", str);
+	} else
+		goto error;
+
+	str = g_hash_table_lookup (options, "subnet_mask");
+	if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
+		plen = nm_utils_ip4_netmask_to_prefix (tmp_addr);
+		nm_log_info (LOGD_DHCP4, "  plen %d (%s)", plen, str);
+	} else {
+		/* Get default netmask for the IP according to appropriate class. */
+		plen = nm_utils_ip4_get_default_prefix (address.address);
+		nm_log_info (LOGD_DHCP4, "  plen %d (default)", plen);
+	}
+	address.plen = plen;
+
+	/* Routes: if the server returns classless static routes, we MUST ignore
+	 * the 'static_routes' option.
+	 */
+	if (!ip4_process_classless_routes (options, priority, ip4_config, &gwaddr))
+		process_classful_routes (options, priority, ip4_config);
+
+	if (gwaddr) {
+		nm_log_info (LOGD_DHCP4, "  gateway %s", nm_utils_inet4_ntop (gwaddr, NULL));
+		nm_ip4_config_set_gateway (ip4_config, gwaddr);
+	} else {
+		/* If the gateway wasn't provided as a classless static route with a
+		 * subnet length of 0, try to find it using the old-style 'routers' option.
+		 */
+		str = g_hash_table_lookup (options, "routers");
+		if (str) {
+			char **routers = g_strsplit (str, " ", 0);
+			char **s;
+
+			for (s = routers; *s; s++) {
+				/* FIXME: how to handle multiple routers? */
+				if (inet_pton (AF_INET, *s, &gwaddr) > 0) {
+					nm_ip4_config_set_gateway (ip4_config, gwaddr);
+					nm_log_info (LOGD_DHCP4, "  gateway %s", *s);
+					break;
+				} else
+					nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s);
+			}
+			g_strfreev (routers);
+		}
+	}
+
+	/*
+	 * RFC 2132, section 9.7
+	 *   DHCP clients use the contents of the 'server identifier' field
+	 *   as the destination address for any DHCP messages unicast to
+	 *   the DHCP server.
+	 *
+	 * Some ISP's provide leases from central servers that are on
+	 * different subnets that the address offered.  If the host
+	 * does not configure the interface as the default route, the
+	 * dhcp server may not be reachable via unicast, and a host
+	 * specific route is needed.
+	 **/
+	str = g_hash_table_lookup (options, "dhcp_server_identifier");
+	if (str) {
+		if (inet_pton (AF_INET, str, &tmp_addr) > 0) {
+
+			nm_log_info (LOGD_DHCP4, "  server identifier %s", str);
+			if (   nm_utils_ip4_address_clear_host_address(tmp_addr, address.plen) != nm_utils_ip4_address_clear_host_address(address.address, address.plen)
+			    && !nm_ip4_config_get_direct_route_for_host (ip4_config, tmp_addr)) {
+				/* DHCP server not on assigned subnet and the no direct route was returned. Add route */
+				NMPlatformIP4Route route = { 0 };
+
+				route.network = tmp_addr;
+				route.plen = 32;
+				/* this will be a device route if gwaddr is 0 */
+				route.gateway = gwaddr;
+				route.source = NM_IP_CONFIG_SOURCE_DHCP;
+				route.metric = priority;
+				nm_ip4_config_add_route (ip4_config, &route);
+				nm_log_dbg (LOGD_IP, "adding route for server identifier: %s",
+				                      nm_platform_ip4_route_to_string (&route));
+			}
+		}
+		else
+			nm_log_warn (LOGD_DHCP4, "ignoring invalid server identifier '%s'", str);
+	}
+
+	str = g_hash_table_lookup (options, "dhcp_lease_time");
+	if (str) {
+		address.lifetime = address.preferred = strtoul (str, NULL, 10);
+		nm_log_info (LOGD_DHCP4, "  lease time %d", address.lifetime);
+	}
+
+	address.source = NM_IP_CONFIG_SOURCE_DHCP;
+	nm_ip4_config_add_address (ip4_config, &address);
+
+	str = g_hash_table_lookup (options, "host_name");
+	if (str)
+		nm_log_info (LOGD_DHCP4, "  hostname '%s'", str);
+
+	str = g_hash_table_lookup (options, "domain_name_servers");
+	if (str) {
+		char **dns = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = dns; *s; s++) {
+			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+				if (tmp_addr) {
+					nm_ip4_config_add_nameserver (ip4_config, tmp_addr);
+					nm_log_info (LOGD_DHCP4, "  nameserver '%s'", *s);
+				}
+			} else
+				nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s);
+		}
+		g_strfreev (dns);
+	}
+
+	str = g_hash_table_lookup (options, "domain_name");
+	if (str) {
+		char **domains = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = domains; *s; s++) {
+			nm_log_info (LOGD_DHCP4, "  domain name '%s'", *s);
+			nm_ip4_config_add_domain (ip4_config, *s);
+		}
+		g_strfreev (domains);
+	}
+
+	str = g_hash_table_lookup (options, "domain_search");
+	if (str)
+		process_domain_search (str, ip4_add_domain_search, ip4_config);
+
+	str = g_hash_table_lookup (options, "netbios_name_servers");
+	if (str) {
+		char **nbns = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = nbns; *s; s++) {
+			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+				if (tmp_addr) {
+					nm_ip4_config_add_wins (ip4_config, tmp_addr);
+					nm_log_info (LOGD_DHCP4, "  wins '%s'", *s);
+				}
+			} else
+				nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s);
+		}
+		g_strfreev (nbns);
+	}
+
+	str = g_hash_table_lookup (options, "interface_mtu");
+	if (str) {
+		int int_mtu;
+
+		errno = 0;
+		int_mtu = strtol (str, NULL, 10);
+		if ((errno == EINVAL) || (errno == ERANGE))
+			goto error;
+
+		if (int_mtu > 576)
+			nm_ip4_config_set_mtu (ip4_config, int_mtu, NM_IP_CONFIG_SOURCE_DHCP);
+	}
+
+	str = g_hash_table_lookup (options, "nis_domain");
+	if (str) {
+		nm_log_info (LOGD_DHCP4, "  NIS domain '%s'", str);
+		nm_ip4_config_set_nis_domain (ip4_config, str);
+	}
+
+	str = g_hash_table_lookup (options, "nis_servers");
+	if (str) {
+		char **nis = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = nis; *s; s++) {
+			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+				if (tmp_addr) {
+					nm_ip4_config_add_nis_server (ip4_config, tmp_addr);
+					nm_log_info (LOGD_DHCP4, "  nis '%s'", *s);
+				}
+			} else
+				nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s);
+		}
+		g_strfreev (nis);
+	}
+
+	return ip4_config;
+
+error:
+	g_object_unref (ip4_config);
+	return NULL;
+}
+
+/********************************************/
+
+static void
+ip6_add_domain_search (gpointer data, gpointer user_data)
+{
+	nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data);
+}
+
+NMIP6Config *
+nm_dhcp_utils_ip6_config_from_options (const char *iface,
+                                       GHashTable *options,
+                                       guint32 priority,
+                                       gboolean info_only)
+{
+	NMIP6Config *ip6_config = NULL;
+	struct in6_addr tmp_addr;
+	NMPlatformIP6Address address;
+	char *str = NULL;
+	GHashTableIter iter;
+	gpointer key, value;
+
+	g_return_val_if_fail (options != NULL, NULL);
+
+	memset (&address, 0, sizeof (address));
+	address.plen = 128;
+	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
+
+	g_hash_table_iter_init (&iter, options);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'",
+		            iface, (const char *) key, (const char *) value);
+	}
+
+	ip6_config = nm_ip6_config_new ();
+
+	str = g_hash_table_lookup (options, "max_life");
+	if (str) {
+		address.lifetime = strtoul (str, NULL, 10);
+		nm_log_info (LOGD_DHCP6, "  valid_lft %d", address.lifetime);
+	}
+
+	str = g_hash_table_lookup (options, "preferred_life");
+	if (str) {
+		address.preferred = strtoul (str, NULL, 10);
+		nm_log_info (LOGD_DHCP6, "  preferred_lft %d", address.preferred);
+	}
+
+	str = g_hash_table_lookup (options, "ip6_address");
+	if (str) {
+		if (!inet_pton (AF_INET6, str, &tmp_addr)) {
+			nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'",
+			             iface, str);
+			goto error;
+		}
+
+		address.address = tmp_addr;
+		address.source = NM_IP_CONFIG_SOURCE_DHCP;
+		nm_ip6_config_add_address (ip6_config, &address);
+		nm_log_info (LOGD_DHCP6, "  address %s", str);
+	} else if (info_only == FALSE) {
+		/* No address in Managed mode is a hard error */
+		goto error;
+	}
+
+	str = g_hash_table_lookup (options, "host_name");
+	if (str)
+		nm_log_info (LOGD_DHCP6, "  hostname '%s'", str);
+
+	str = g_hash_table_lookup (options, "dhcp6_name_servers");
+	if (str) {
+		char **dns = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = dns; *s; s++) {
+			if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) {
+				if (!IN6_IS_ADDR_UNSPECIFIED (&tmp_addr)) {
+					nm_ip6_config_add_nameserver (ip6_config, &tmp_addr);
+					nm_log_info (LOGD_DHCP6, "  nameserver '%s'", *s);
+				}
+			} else
+				nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s);
+		}
+		g_strfreev (dns);
+	}
+
+	str = g_hash_table_lookup (options, "dhcp6_domain_search");
+	if (str)
+		process_domain_search (str, ip6_add_domain_search, ip6_config);
+
+	return ip6_config;
+
+error:
+	g_object_unref (ip6_config);
+	return NULL;
+}
+
+char *
+nm_dhcp_utils_duid_to_string (const GByteArray *duid)
+{
+	guint32 i = 0;
+	GString *s;
+
+	g_return_val_if_fail (duid != NULL, NULL);
+
+	s = g_string_sized_new (MIN (duid->len * 3, 50));
+	while (i < duid->len) {
+		if (s->len)
+			g_string_append_c (s, ':');
+		g_string_append_printf (s, "%02x", duid->data[i++]);
+	}
+	return g_string_free (s, FALSE);
+}
+
+/**
+ * nm_dhcp_utils_client_id_string_to_bytes:
+ * @client_id: the client ID string
+ *
+ * Accepts either a hex string ("aa:bb:cc") representing a binary client ID
+ * (the first byte is assumed to be the 'type' field per RFC 2132 section 9.14),
+ * or a string representing a non-hardware-address client ID, in which case
+ * the 'type' field is set to 0.
+ *
+ * Returns: the binary client ID suitable for sending over the wire
+ * to the DHCP server.
+ */
+GBytes *
+nm_dhcp_utils_client_id_string_to_bytes (const char *client_id)
+{
+	GBytes *bytes = NULL;
+	guint len;
+	char *c;
+
+	g_return_val_if_fail (client_id && client_id[0], NULL);
+
+	/* Try as hex encoded */
+	if (strchr (client_id, ':'))
+		bytes = nm_utils_hexstr2bin (client_id);
+	if (!bytes) {
+		/* Fall back to string */
+		len = strlen (client_id);
+		c = g_malloc (len + 1);
+		c[0] = 0;  /* type: non-hardware address per RFC 2132 section 9.14 */
+		memcpy (c + 1, client_id, len);
+		bytes = g_bytes_new_take (c, len + 1);
+	}
+
+	return bytes;
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-utils.h b/src/dhcp-manager/nm-dhcp-utils.h
new file mode 100644
index 00000000..a01cf4c2
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-utils.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DHCP_UTILS_H__
+#define __NETWORKMANAGER_DHCP_UTILS_H__
+
+#include <stdlib.h>
+#include <glib.h>
+#include <nm-ip4-config.h>
+#include <nm-ip6-config.h>
+
+NMIP4Config *nm_dhcp_utils_ip4_config_from_options (const char *iface,
+                                                    GHashTable *options,
+                                                    guint priority);
+
+NMIP6Config *nm_dhcp_utils_ip6_config_from_options (const char *iface,
+                                                    GHashTable *options,
+                                                    guint priority,
+                                                    gboolean info_only);
+
+char *       nm_dhcp_utils_duid_to_string          (const GByteArray *duid);
+
+GBytes *     nm_dhcp_utils_client_id_string_to_bytes (const char *client_id);
+
+#endif /* __NETWORKMANAGER_DHCP_UTILS_H__ */
+
diff --git a/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.c b/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.c
new file mode 100644
index 00000000..a6d81727
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.c
@@ -0,0 +1,209 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "sd-event.h"
+#include "time-util.h"
+
+struct sd_event_source {
+	guint refcount;
+	guint id;
+	gpointer user_data;
+
+	GIOChannel *channel;
+	sd_event_io_handler_t io_cb;
+
+	uint64_t usec;
+	sd_event_time_handler_t time_cb;
+};
+
+int
+sd_event_source_set_priority (sd_event_source *s, int64_t priority)
+{
+	return 0;
+}
+
+sd_event_source*
+sd_event_source_unref (sd_event_source *s)
+{
+
+	if (!s)
+		return NULL;
+
+	g_return_val_if_fail (s->refcount, NULL);
+
+	s->refcount--;
+	if (s->refcount == 0) {
+		if (s->id)
+			g_source_remove (s->id);
+		if (s->channel) {
+			/* Don't shut down the channel since systemd will soon close
+			 * the file descriptor itself, which would cause -EBADF.
+			 */
+			g_io_channel_unref (s->channel);
+		}
+		g_free (s);
+	}
+	return NULL;
+}
+
+int
+sd_event_source_set_description(sd_event_source *s, const char *description)
+{
+	if (!s)
+		return -EINVAL;
+
+	g_source_set_name_by_id (s->id, description);
+	return 0;
+}
+
+static gboolean
+io_ready (GIOChannel *channel, GIOCondition condition, struct sd_event_source *source)
+{
+	int r, revents = 0;
+
+	if (condition & G_IO_IN)
+		revents |= EPOLLIN;
+	if (condition & G_IO_OUT)
+		revents |= EPOLLOUT;
+	if (condition & G_IO_PRI)
+		revents |= EPOLLPRI;
+	if (condition & G_IO_ERR)
+		revents |= EPOLLERR;
+	if (condition & G_IO_HUP)
+		revents |= EPOLLHUP;
+
+	r = source->io_cb (source, g_io_channel_unix_get_fd (channel), revents, source->user_data);
+	if (r < 0) {
+		source->id = 0;
+		return G_SOURCE_REMOVE;
+	}
+
+	return G_SOURCE_CONTINUE;
+}
+
+int
+sd_event_add_io (sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata)
+{
+	struct sd_event_source *source;
+	GIOChannel *channel;
+	GIOCondition condition = 0;
+
+	channel = g_io_channel_unix_new (fd);
+	if (!channel)
+		return -EINVAL;
+
+	source = g_new0 (struct sd_event_source, 1);
+	source->refcount = 1;
+	source->io_cb = callback;
+	source->user_data = userdata;
+	source->channel = channel;
+
+	if (events & EPOLLIN)
+		condition |= G_IO_IN;
+	if (events & EPOLLOUT)
+		condition |= G_IO_OUT;
+	if (events & EPOLLPRI)
+		condition |= G_IO_PRI;
+	if (events & EPOLLERR)
+		condition |= G_IO_ERR;
+	if (events & EPOLLHUP)
+		condition |= G_IO_HUP;
+
+	g_io_channel_set_encoding (source->channel, NULL, NULL);
+	g_io_channel_set_buffered (source->channel, FALSE);
+	source->id = g_io_add_watch (source->channel, condition, (GIOFunc) io_ready, source);
+
+	*s = source;
+	return 0;
+}
+
+static gboolean
+time_ready (struct sd_event_source *source)
+{
+	int r;
+
+	r = source->time_cb (source, source->usec, source->user_data);
+	if (r < 0) {
+		source->id = 0;
+		return G_SOURCE_REMOVE;
+	}
+
+	return G_SOURCE_CONTINUE;
+}
+
+int
+sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata)
+{
+	struct sd_event_source *source;
+	uint64_t n = now (clock);
+
+	source = g_new0 (struct sd_event_source, 1);
+	source->refcount = 1;
+	source->time_cb = callback;
+	source->user_data = userdata;
+	source->usec = usec;
+
+	if (usec > 1000)
+		usec = n < usec - 1000 ? usec - n : 1000;
+	source->id = g_timeout_add (usec / 1000, (GSourceFunc) time_ready, source);
+
+	*s = source;
+	return 0;
+}
+
+/* sd_event is basically a GMainContext; but since we only
+ * ever use the default context, nothing to do here.
+ */
+
+int
+sd_event_default (sd_event **e)
+{
+	*e = GUINT_TO_POINTER (1);
+	return 0;
+}
+
+sd_event*
+sd_event_ref (sd_event *e)
+{
+	return e;
+}
+
+sd_event*
+sd_event_unref (sd_event *e)
+{
+	return NULL;
+}
+
+int
+sd_event_now (sd_event *e, clockid_t clock, uint64_t *usec)
+{
+	*usec = now (clock);
+	return 0;
+}
+
+int asynchronous_close(int fd) {
+	safe_close(fd);
+	return -1;
+}
+
diff --git a/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h b/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h
new file mode 100644
index 00000000..ce1716b1
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_SD_ADAPT_H
+#define NM_SD_ADAPT_H
+
+#include <config.h>
+
+#include <glib.h>
+
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <elf.h>
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "nm-logging.h"
+
+static inline guint32
+_slog_level_to_nm (int slevel)
+{
+    switch (slevel) {
+    case LOG_DEBUG:   return LOGL_DEBUG;
+	case LOG_WARNING: return LOGL_WARN;
+	case LOG_ERR:     return LOGL_ERR;
+	case LOG_INFO:
+	case LOG_NOTICE:
+	default:          return LOGL_INFO;
+	}
+}
+
+#define log_meta(level, file, line, func, format, ...) \
+G_STMT_START { \
+	guint32 _l = _slog_level_to_nm ((level)); \
+	if (nm_logging_enabled (_l, LOGD_DHCP)) { \
+		const char *_location = strrchr (file "", '/'); \
+		\
+		_nm_log (_location ? _location + 1 : file, line, func, _l, LOGD_DHCP, format, ## __VA_ARGS__); \
+	} \
+} G_STMT_END
+
+#define log_debug(...)       log_full(LOG_DEBUG, __VA_ARGS__)
+#define log_error(...)       log_full(LOG_ERR, __VA_ARGS__)
+#define log_full(level, ...) log_meta((level), __FILE__, __LINE__, __func__, __VA_ARGS__);
+
+#define log_dhcp_client(client, fmt, ...) \
+	log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
+
+#define log_assert_failed(e, file, line, func) \
+G_STMT_START { \
+	nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assertion failed: " # e); \
+	g_assert (FALSE); \
+} G_STMT_END
+
+#define log_assert_failed_unreachable(t, file, line, func) \
+G_STMT_START { \
+	nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assert unreachable: " # t); \
+	g_assert_not_reached (); \
+} G_STMT_END
+
+#define log_assert_failed_return(e, file, line, func) \
+	nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assert return: " # e); \
+
+#define log_oom nm_log_err(LOGD_CORE, "%s:%s/%s: OOM", __FILE__, __LINE__, __func__)
+
+/* Can't include both net/if.h and linux/if.h; so have to define this here */
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+
+#ifndef MAX_HANDLE_SZ
+#define MAX_HANDLE_SZ 128
+#endif
+
+#define noreturn G_GNUC_NORETURN
+
+#include "sd-id128.h"
+#include "sparse-endian.h"
+#include "async.h"
+#include "util.h"
+
+static inline pid_t gettid(void) {
+        return (pid_t) syscall(SYS_gettid);
+}
+
+#endif /* NM_SD_ADAPT_H */
+
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h
new file mode 100644
index 00000000..28c0e634
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h
@@ -0,0 +1,76 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+  Copyright (C) 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <stdint.h>
+#include <linux/if_packet.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+
+#include "socket-util.h"
+
+#include "sd-dhcp-client.h"
+#include "dhcp-protocol.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link,
+                                 uint32_t xid, const uint8_t *mac_addr,
+                                 size_t mac_addr_len, uint16_t arp_type);
+int dhcp_network_bind_udp_socket(be32_t address, uint16_t port);
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+                                 const void *packet, size_t len);
+int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
+                                 const void *packet, size_t len);
+
+int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload,
+                       uint8_t code, size_t optlen, const void *optval);
+
+typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
+                                const uint8_t *option, void *user_data);
+
+int dhcp_option_parse(DHCPMessage *message, size_t len,
+                      dhcp_option_cb_t cb, void *user_data);
+
+int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
+                      uint8_t type, uint16_t arp_type, size_t optlen,
+                      size_t *optoffset);
+
+uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);
+
+void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
+                                   uint16_t source, be32_t destination_addr,
+                                   uint16_t destination, uint16_t len);
+
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
+#define _cleanup_dhcp_client_unref_ _cleanup_(sd_dhcp_client_unrefp)
+
+/* If we are invoking callbacks of a dhcp-client, ensure unreffing the
+ * client from the callback doesn't destroy the object we are working
+ * on */
+#define DHCP_CLIENT_DONT_DESTROY(client) \
+        _cleanup_dhcp_client_unref_ _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
+
+#define log_dhcp_client(client, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h
new file mode 100644
index 00000000..9e184ac4
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h
@@ -0,0 +1,87 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+  Copyright (C) 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+#include <linux/if_packet.h>
+
+#include "refcnt.h"
+#include "util.h"
+
+#include "dhcp-protocol.h"
+
+#include "sd-dhcp-client.h"
+
+struct sd_dhcp_route {
+        struct in_addr dst_addr;
+        struct in_addr gw_addr;
+        unsigned char dst_prefixlen;
+};
+
+struct sd_dhcp_lease {
+        RefCount n_ref;
+
+        int32_t time_offset;
+        uint32_t t1;
+        uint32_t t2;
+        uint32_t lifetime;
+        uint32_t mtu_aging_timeout;
+        be32_t address;
+        be32_t server_address;
+        be32_t subnet_mask;
+        be32_t router;
+        be32_t next_server;
+        be32_t broadcast;
+        struct in_addr *dns;
+        size_t dns_size;
+        struct in_addr *ntp;
+        size_t ntp_size;
+        struct in_addr *policy_filter;
+        size_t policy_filter_size;
+        struct sd_dhcp_route *static_route;
+        size_t static_route_size;
+        size_t static_route_allocated;
+        uint16_t boot_file_size;
+        uint16_t mdr;
+        uint16_t mtu;
+        uint8_t ttl;
+        bool ip_forward;
+        bool ip_forward_non_local;
+        char *domainname;
+        char *hostname;
+        char *root_path;
+        uint8_t *client_id;
+        size_t client_id_len;
+};
+
+int dhcp_lease_new(sd_dhcp_lease **ret);
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
+                              void *user_data);
+
+int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
+
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
+                             size_t client_id_len);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref);
+#define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp)
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c
new file mode 100644
index 00000000..971b3aa1
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c
@@ -0,0 +1,241 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <linux/if_packet.h>
+#include <linux/if_infiniband.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <linux/filter.h>
+
+#include "socket-util.h"
+
+#include "dhcp-internal.h"
+
+static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
+                            uint32_t xid, const uint8_t *mac_addr,
+                            size_t mac_addr_len,
+                            const uint8_t *bcast_addr,
+                            const struct ether_addr *eth_mac,
+                            uint16_t arp_type, uint8_t dhcp_hlen) {
+        struct sock_filter filter[] = {
+                BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),                                 /* A <- packet length */
+                BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0),         /* packet >= DHCPPacket ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),                /* IP protocol == UDP ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags */
+                BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x20),                             /* A <- A & 0x20 (More Fragments bit) */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                          /* A == 0 ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags + Fragment offset */
+                BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x1fff),                           /* A <- A & 0x1fff (Fragment offset) */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                          /* A == 0 ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)),    /* A <- UDP destination port */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_PORT_CLIENT, 1, 0),           /* UDP destination port == DHCP client port ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)),     /* A <- DHCP op */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0),                  /* op == BOOTREPLY ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)),  /* A <- DHCP header type */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0),                   /* header type == arp_type ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)),   /* A <- mac address length */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0),                  /* address length == dhcp_hlen ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)),    /* A <- client identifier */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0),                        /* client identifier == xid ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))),                     /* A <- 4 bytes of client's MAC */
+                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                                       /* X <- A */
+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)),                 /* A <- 4 bytes of MAC from dhcp.chaddr */
+                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                                /* A xor X */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                                          /* A == 0 ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))),   /* A <- remainder of client's MAC */
+                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                                       /* X <- A */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4),             /* A <- remainder of MAC from dhcp.chaddr */
+                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                                /* A xor X */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                                          /* A == 0 ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)),  /* A <- DHCP magic cookie */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0),          /* cookie == DHCP magic cookie ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
+                BPF_STMT(BPF_RET + BPF_K, 65535),                                      /* return all */
+        };
+        struct sock_fprog fprog = {
+                .len = ELEMENTSOF(filter),
+                .filter = filter
+        };
+        _cleanup_close_ int s = -1;
+        int r, on = 1;
+
+        assert(ifindex > 0);
+        assert(link);
+
+        s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+        if (s < 0)
+                return -errno;
+
+        r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+        if (r < 0)
+                return -errno;
+
+        link->ll.sll_family = AF_PACKET;
+        link->ll.sll_protocol = htons(ETH_P_IP);
+        link->ll.sll_ifindex = ifindex;
+        link->ll.sll_hatype = htons(arp_type);
+        link->ll.sll_halen = mac_addr_len;
+        memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len);
+
+        r = bind(s, &link->sa, sizeof(link->ll));
+        if (r < 0)
+                return -errno;
+
+        r = s;
+        s = -1;
+
+        return r;
+}
+
+int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
+                                 uint32_t xid, const uint8_t *mac_addr,
+                                 size_t mac_addr_len, uint16_t arp_type) {
+        static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+        /* Default broadcast address for IPoIB */
+        static const uint8_t ib_bcast[] = {
+                0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0xff, 0xff, 0xff, 0xff
+          };
+        struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } };
+        const uint8_t *bcast_addr = NULL;
+        uint8_t dhcp_hlen = 0;
+
+        assert_return(mac_addr_len > 0, -EINVAL);
+
+        if (arp_type == ARPHRD_ETHER) {
+                assert_return(mac_addr_len == ETH_ALEN, -EINVAL);
+                memcpy(&eth_mac, mac_addr, ETH_ALEN);
+                bcast_addr = eth_bcast;
+                dhcp_hlen = ETH_ALEN;
+        } else if (arp_type == ARPHRD_INFINIBAND) {
+                assert_return(mac_addr_len == INFINIBAND_ALEN, -EINVAL);
+                bcast_addr = ib_bcast;
+        } else
+                return -EINVAL;
+
+        return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len,
+                                bcast_addr, &eth_mac, arp_type, dhcp_hlen);
+}
+
+int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
+        union sockaddr_union src = {
+                .in.sin_family = AF_INET,
+                .in.sin_port = htobe16(port),
+                .in.sin_addr.s_addr = address,
+        };
+        _cleanup_close_ int s = -1;
+        int r, on = 1, tos = IPTOS_CLASS_CS6;
+
+        s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+        if (s < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+        if (r < 0)
+                return -errno;
+
+        if (address == INADDR_ANY) {
+                r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
+                if (r < 0)
+                        return -errno;
+
+                r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
+                if (r < 0)
+                        return -errno;
+        } else {
+                r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on));
+                if (r < 0)
+                        return -errno;
+        }
+
+        r = bind(s, &src.sa, sizeof(src.in));
+        if (r < 0)
+                return -errno;
+
+        r = s;
+        s = -1;
+
+        return r;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+                                 const void *packet, size_t len) {
+        int r;
+
+        assert(link);
+        assert(packet);
+        assert(len);
+
+        r = sendto(s, packet, len, 0, &link->sa, sizeof(link->ll));
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
+int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
+                                 const void *packet, size_t len) {
+        union sockaddr_union dest = {
+                .in.sin_family = AF_INET,
+                .in.sin_port = htobe16(port),
+                .in.sin_addr.s_addr = address,
+        };
+        int r;
+
+        assert(s >= 0);
+        assert(packet);
+        assert(len);
+
+        r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in));
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-option.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-option.c
new file mode 100644
index 00000000..9759e575
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-option.c
@@ -0,0 +1,257 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "dhcp-internal.h"
+
+static int option_append(uint8_t options[], size_t size, size_t *offset,
+                         uint8_t code, size_t optlen, const void *optval) {
+        assert(options);
+        assert(offset);
+
+        if (code != DHCP_OPTION_END)
+                /* always make sure there is space for an END option */
+                size --;
+
+        switch (code) {
+
+        case DHCP_OPTION_PAD:
+        case DHCP_OPTION_END:
+                if (size < *offset + 1)
+                        return -ENOBUFS;
+
+                options[*offset] = code;
+                *offset += 1;
+                break;
+
+        default:
+                if (size < *offset + optlen + 2)
+                        return -ENOBUFS;
+
+                options[*offset] = code;
+                options[*offset + 1] = optlen;
+
+                if (optlen) {
+                        assert(optval);
+
+                        memcpy(&options[*offset + 2], optval, optlen);
+                }
+
+                *offset += optlen + 2;
+
+                break;
+        }
+
+        return 0;
+}
+
+int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
+                       uint8_t overload,
+                       uint8_t code, size_t optlen, const void *optval) {
+        size_t file_offset = 0, sname_offset =0;
+        bool file, sname;
+        int r;
+
+        assert(message);
+        assert(offset);
+
+        file = overload & DHCP_OVERLOAD_FILE;
+        sname = overload & DHCP_OVERLOAD_SNAME;
+
+        if (*offset < size) {
+                /* still space in the options array */
+                r = option_append(message->options, size, offset, code, optlen, optval);
+                if (r >= 0)
+                        return 0;
+                else if (r == -ENOBUFS && (file || sname)) {
+                        /* did not fit, but we have more buffers to try
+                           close the options array and move the offset to its end */
+                        r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL);
+                        if (r < 0)
+                                return r;
+
+                        *offset = size;
+                } else
+                        return r;
+        }
+
+        if (overload & DHCP_OVERLOAD_FILE) {
+                file_offset = *offset - size;
+
+                if (file_offset < sizeof(message->file)) {
+                        /* still space in the 'file' array */
+                        r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval);
+                        if (r >= 0) {
+                                *offset = size + file_offset;
+                                return 0;
+                        } else if (r == -ENOBUFS && sname) {
+                                /* did not fit, but we have more buffers to try
+                                   close the file array and move the offset to its end */
+                                r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL);
+                                if (r < 0)
+                                        return r;
+
+                                *offset = size + sizeof(message->file);
+                        } else
+                                return r;
+                }
+        }
+
+        if (overload & DHCP_OVERLOAD_SNAME) {
+                sname_offset = *offset - size - (file ? sizeof(message->file) : 0);
+
+                if (sname_offset < sizeof(message->sname)) {
+                        /* still space in the 'sname' array */
+                        r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval);
+                        if (r >= 0) {
+                                *offset = size + (file ? sizeof(message->file) : 0) + sname_offset;
+                                return 0;
+                        } else {
+                                /* no space, or other error, give up */
+                                return r;
+                        }
+                }
+        }
+
+        return -ENOBUFS;
+}
+
+static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
+                         uint8_t *message_type, dhcp_option_cb_t cb,
+                         void *user_data) {
+        uint8_t code, len;
+        size_t offset = 0;
+
+        while (offset < buflen) {
+                switch (options[offset]) {
+                case DHCP_OPTION_PAD:
+                        offset++;
+
+                        break;
+
+                case DHCP_OPTION_END:
+                        return 0;
+
+                case DHCP_OPTION_MESSAGE_TYPE:
+                        if (buflen < offset + 3)
+                                return -ENOBUFS;
+
+                        len = options[++offset];
+                        if (len != 1)
+                                return -EINVAL;
+
+                        if (message_type)
+                                *message_type = options[++offset];
+                        else
+                                offset++;
+
+                        offset++;
+
+                        break;
+
+                case DHCP_OPTION_OVERLOAD:
+                        if (buflen < offset + 3)
+                                return -ENOBUFS;
+
+                        len = options[++offset];
+                        if (len != 1)
+                                return -EINVAL;
+
+                        if (overload)
+                                *overload = options[++offset];
+                        else
+                                offset++;
+
+                        offset++;
+
+                        break;
+
+                default:
+                        if (buflen < offset + 3)
+                                return -ENOBUFS;
+
+                        code = options[offset];
+                        len = options[++offset];
+
+                        if (buflen < ++offset + len)
+                                return -EINVAL;
+
+                        if (cb)
+                                cb(code, len, &options[offset], user_data);
+
+                        offset += len;
+
+                        break;
+                }
+        }
+
+        if (offset < buflen)
+                return -EINVAL;
+
+        return 0;
+}
+
+int dhcp_option_parse(DHCPMessage *message, size_t len,
+                      dhcp_option_cb_t cb, void *user_data) {
+        uint8_t overload = 0;
+        uint8_t message_type = 0;
+        int r;
+
+        if (!message)
+                return -EINVAL;
+
+        if (len < sizeof(DHCPMessage))
+                return -EINVAL;
+
+        len -= sizeof(DHCPMessage);
+
+        r = parse_options(message->options, len, &overload, &message_type,
+                          cb, user_data);
+        if (r < 0)
+                return r;
+
+        if (overload & DHCP_OVERLOAD_FILE) {
+                r = parse_options(message->file, sizeof(message->file),
+                                NULL, &message_type, cb, user_data);
+                if (r < 0)
+                        return r;
+        }
+
+        if (overload & DHCP_OVERLOAD_SNAME) {
+                r = parse_options(message->sname, sizeof(message->sname),
+                                NULL, &message_type, cb, user_data);
+                if (r < 0)
+                        return r;
+        }
+
+        if (message_type)
+                return message_type;
+
+        return -ENOMSG;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c
new file mode 100644
index 00000000..5bb065ba
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c
@@ -0,0 +1,204 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+  Copyright (C) 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <sys/param.h>
+
+#include "util.h"
+#include "list.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-lease-internal.h"
+#include "dhcp-internal.h"
+#include "sd-dhcp-lease.h"
+#include "sd-dhcp-client.h"
+
+#define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
+
+int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
+                      uint8_t type, uint16_t arp_type, size_t optlen,
+                      size_t *optoffset) {
+        size_t offset = 0;
+        int r;
+
+        assert(op == BOOTREQUEST || op == BOOTREPLY);
+        assert(arp_type == ARPHRD_ETHER || arp_type == ARPHRD_INFINIBAND);
+
+        message->op = op;
+        message->htype = arp_type;
+        message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
+        message->xid = htobe32(xid);
+        message->magic = htobe32(DHCP_MAGIC_COOKIE);
+
+        r = dhcp_option_append(message, optlen, &offset, 0,
+                               DHCP_OPTION_MESSAGE_TYPE, 1, &type);
+        if (r < 0)
+                return r;
+
+        *optoffset = offset;
+
+        return 0;
+}
+
+uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
+        uint64_t *buf_64 = (uint64_t*)buf;
+        uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t));
+        uint64_t sum = 0;
+
+        /* See RFC1071 */
+
+        while (buf_64 < end_64) {
+                sum += *buf_64;
+                if (sum < *buf_64)
+                        /* wrap around in one's complement */
+                        sum++;
+
+                buf_64 ++;
+        }
+
+        if (len % sizeof(uint64_t)) {
+                /* If the buffer is not aligned to 64-bit, we need
+                   to zero-pad the last few bytes and add them in */
+                uint64_t buf_tail = 0;
+
+                memcpy(&buf_tail, buf_64, len % sizeof(uint64_t));
+
+                sum += buf_tail;
+                if (sum < buf_tail)
+                        /* wrap around */
+                        sum++;
+        }
+
+        while (sum >> 16)
+                sum = (sum & 0xffff) + (sum >> 16);
+
+        return ~sum;
+}
+
+void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
+                                   uint16_t source_port, be32_t destination_addr,
+                                   uint16_t destination_port, uint16_t len) {
+        packet->ip.version = IPVERSION;
+        packet->ip.ihl = DHCP_IP_SIZE / 4;
+        packet->ip.tot_len = htobe16(len);
+
+        packet->ip.tos = IPTOS_CLASS_CS6;
+
+        packet->ip.protocol = IPPROTO_UDP;
+        packet->ip.saddr = source_addr;
+        packet->ip.daddr = destination_addr;
+
+        packet->udp.source = htobe16(source_port);
+        packet->udp.dest = htobe16(destination_port);
+
+        packet->udp.len = htobe16(len - DHCP_IP_SIZE);
+
+        packet->ip.check = packet->udp.len;
+        packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8);
+
+        packet->ip.ttl = IPDEFTTL;
+        packet->ip.check = 0;
+        packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
+}
+
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
+        size_t hdrlen;
+
+        assert(packet);
+
+        /* IP */
+
+        if (packet->ip.version != IPVERSION) {
+                log_debug("ignoring packet: not IPv4");
+                return -EINVAL;
+        }
+
+        if (packet->ip.ihl < 5) {
+                log_debug("ignoring packet: IPv4 IHL (%u words) invalid",
+                          packet->ip.ihl);
+                return -EINVAL;
+        }
+
+        hdrlen = packet->ip.ihl * 4;
+        if (hdrlen < 20) {
+                log_debug("ignoring packet: IPv4 IHL (%zu bytes) "
+                          "smaller than minimum (20 bytes)", hdrlen);
+                return -EINVAL;
+        }
+
+        if (len < hdrlen) {
+                log_debug("ignoring packet: packet (%zu bytes) "
+                          "smaller than expected (%zu) by IP header", len,
+                          hdrlen);
+                return -EINVAL;
+        }
+
+        /* UDP */
+
+        if (packet->ip.protocol != IPPROTO_UDP) {
+                log_debug("ignoring packet: not UDP");
+                return -EINVAL;
+        }
+
+        if (len < hdrlen + be16toh(packet->udp.len)) {
+                log_debug("ignoring packet: packet (%zu bytes) "
+                          "smaller than expected (%zu) by UDP header", len,
+                          hdrlen + be16toh(packet->udp.len));
+                return -EINVAL;
+        }
+
+        if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
+                log_debug("ignoring packet: to port %u, which "
+                          "is not the DHCP client port (%u)",
+                          be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
+                return -EINVAL;
+        }
+
+        /* checksums - computing these is relatively expensive, so only do it
+           if all the other checks have passed
+         */
+
+        if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) {
+                log_debug("ignoring packet: invalid IP checksum");
+                return -EINVAL;
+        }
+
+        if (checksum && packet->udp.check) {
+                packet->ip.check = packet->udp.len;
+                packet->ip.ttl = 0;
+
+                if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
+                                  be16toh(packet->udp.len) + 12)) {
+                        log_debug("ignoring packet: invalid UDP checksum");
+                        return -EINVAL;
+                }
+        }
+
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h
new file mode 100644
index 00000000..abca9422
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h
@@ -0,0 +1,141 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <stdint.h>
+
+#include "macro.h"
+#include "sparse-endian.h"
+
+struct DHCPMessage {
+        uint8_t op;
+        uint8_t htype;
+        uint8_t hlen;
+        uint8_t hops;
+        be32_t xid;
+        be16_t secs;
+        be16_t flags;
+        be32_t ciaddr;
+        be32_t yiaddr;
+        be32_t siaddr;
+        be32_t giaddr;
+        uint8_t chaddr[16];
+        uint8_t sname[64];
+        uint8_t file[128];
+        be32_t magic;
+        uint8_t options[0];
+} _packed_;
+
+typedef struct DHCPMessage DHCPMessage;
+
+struct DHCPPacket {
+        struct iphdr ip;
+        struct udphdr udp;
+        DHCPMessage dhcp;
+} _packed_;
+
+typedef struct DHCPPacket DHCPPacket;
+
+#define DHCP_IP_SIZE            (int32_t)(sizeof(struct iphdr))
+#define DHCP_IP_UDP_SIZE        (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
+#define DHCP_MESSAGE_SIZE       (int32_t)(sizeof(DHCPMessage))
+#define DHCP_DEFAULT_MIN_SIZE   576 /* the minimum internet hosts must be able to receive */
+#define DHCP_MIN_OPTIONS_SIZE   DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE
+#define DHCP_MAGIC_COOKIE       (uint32_t)(0x63825363)
+
+enum {
+        DHCP_PORT_SERVER                        = 67,
+        DHCP_PORT_CLIENT                        = 68,
+};
+
+enum DHCPState {
+        DHCP_STATE_INIT                         = 0,
+        DHCP_STATE_SELECTING                    = 1,
+        DHCP_STATE_INIT_REBOOT                  = 2,
+        DHCP_STATE_REBOOTING                    = 3,
+        DHCP_STATE_REQUESTING                   = 4,
+        DHCP_STATE_BOUND                        = 5,
+        DHCP_STATE_RENEWING                     = 6,
+        DHCP_STATE_REBINDING                    = 7,
+        DHCP_STATE_STOPPED                      = 8,
+};
+
+typedef enum DHCPState DHCPState;
+
+enum {
+        BOOTREQUEST                             = 1,
+        BOOTREPLY                               = 2,
+};
+
+enum {
+        DHCP_DISCOVER                           = 1,
+        DHCP_OFFER                              = 2,
+        DHCP_REQUEST                            = 3,
+        DHCP_DECLINE                            = 4,
+        DHCP_ACK                                = 5,
+        DHCP_NAK                                = 6,
+        DHCP_RELEASE                            = 7,
+        DHCP_INFORM                             = 8,
+        DHCP_FORCERENEW                         = 9,
+};
+
+enum {
+        DHCP_OVERLOAD_FILE                      = 1,
+        DHCP_OVERLOAD_SNAME                     = 2,
+};
+
+enum {
+        DHCP_OPTION_PAD                         = 0,
+        DHCP_OPTION_SUBNET_MASK                 = 1,
+        DHCP_OPTION_TIME_OFFSET                 = 2,
+        DHCP_OPTION_ROUTER                      = 3,
+        DHCP_OPTION_DOMAIN_NAME_SERVER          = 6,
+        DHCP_OPTION_HOST_NAME                   = 12,
+        DHCP_OPTION_BOOT_FILE_SIZE              = 13,
+        DHCP_OPTION_DOMAIN_NAME                 = 15,
+        DHCP_OPTION_ROOT_PATH                   = 17,
+        DHCP_OPTION_ENABLE_IP_FORWARDING        = 19,
+        DHCP_OPTION_ENABLE_IP_FORWARDING_NL     = 20,
+        DHCP_OPTION_POLICY_FILTER               = 21,
+        DHCP_OPTION_INTERFACE_MDR               = 22,
+        DHCP_OPTION_INTERFACE_TTL               = 23,
+        DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
+        DHCP_OPTION_INTERFACE_MTU               = 26,
+        DHCP_OPTION_BROADCAST                   = 28,
+        DHCP_OPTION_STATIC_ROUTE                = 33,
+        DHCP_OPTION_NTP_SERVER                  = 42,
+        DHCP_OPTION_REQUESTED_IP_ADDRESS        = 50,
+        DHCP_OPTION_IP_ADDRESS_LEASE_TIME       = 51,
+        DHCP_OPTION_OVERLOAD                    = 52,
+        DHCP_OPTION_MESSAGE_TYPE                = 53,
+        DHCP_OPTION_SERVER_IDENTIFIER           = 54,
+        DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
+        DHCP_OPTION_MAXIMUM_MESSAGE_SIZE        = 57,
+        DHCP_OPTION_RENEWAL_T1_TIME             = 58,
+        DHCP_OPTION_REBINDING_T2_TIME           = 59,
+        DHCP_OPTION_VENDOR_CLASS_IDENTIFIER     = 60,
+        DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
+        DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121,
+        DHCP_OPTION_END                         = 255,
+};
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-internal.h
new file mode 100644
index 00000000..e29e2f0e
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-internal.h
@@ -0,0 +1,81 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include "sparse-endian.h"
+#include "sd-event.h"
+#include "list.h"
+#include "macro.h"
+
+typedef struct DHCP6Address DHCP6Address;
+
+struct DHCP6Address {
+        LIST_FIELDS(DHCP6Address, addresses);
+
+        struct {
+                struct in6_addr address;
+                be32_t lifetime_preferred;
+                be32_t lifetime_valid;
+        } iaaddr _packed_;
+};
+
+struct DHCP6IA {
+        uint16_t type;
+        struct {
+                be32_t id;
+                be32_t lifetime_t1;
+                be32_t lifetime_t2;
+        } _packed_;
+        sd_event_source *timeout_t1;
+        sd_event_source *timeout_t2;
+
+        LIST_HEAD(DHCP6Address, addresses);
+};
+
+typedef struct DHCP6IA DHCP6IA;
+
+#define log_dhcp6_client(p, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
+
+int dhcp_network_icmp6_bind_router_solicitation(int index);
+int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
+
+int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
+                        size_t optlen, const void *optval);
+int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
+int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
+                       size_t *optlen, uint8_t **optvalue);
+int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
+                          DHCP6IA *ia);
+
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
+int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
+                                  const void *packet, size_t len);
+
+const char *dhcp6_message_type_to_string(int s) _const_;
+int dhcp6_message_type_from_string(const char *s) _pure_;
+const char *dhcp6_message_status_to_string(int s) _const_;
+int dhcp6_message_status_from_string(const char *s) _pure_;
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-lease-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-lease-internal.h
new file mode 100644
index 00000000..9f7826c4
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-lease-internal.h
@@ -0,0 +1,64 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <stdint.h>
+
+#include "refcnt.h"
+
+#include "sd-dhcp6-lease.h"
+#include "dhcp6-internal.h"
+
+struct sd_dhcp6_lease {
+        RefCount n_ref;
+
+        uint8_t *serverid;
+        size_t serverid_len;
+        uint8_t preference;
+        bool rapid_commit;
+
+        DHCP6IA ia;
+
+        DHCP6Address *addr_iter;
+};
+
+int dhcp6_lease_clear_timers(DHCP6IA *ia);
+int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
+DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
+
+int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
+                             size_t len);
+int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len);
+int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference);
+int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference);
+int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
+int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
+
+int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
+
+int dhcp6_lease_new(sd_dhcp6_lease **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref);
+#define _cleanup_dhcp6_lease_free_ _cleanup_(sd_dhcp6_lease_unrefp)
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-network.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-network.c
new file mode 100644
index 00000000..fbb20ef9
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-network.c
@@ -0,0 +1,198 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <linux/if_packet.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+
+#include "socket-util.h"
+
+#include "dhcp6-internal.h"
+#include "dhcp6-protocol.h"
+
+#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
+        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
+
+#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
+        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
+
+int dhcp_network_icmp6_bind_router_solicitation(int index)
+{
+        struct icmp6_filter filter = { };
+        struct ipv6_mreq mreq = {
+                .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+                .ipv6mr_interface = index,
+        };
+        _cleanup_close_ int s = -1;
+        int r, zero = 0, hops = 255;
+
+        s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                   IPPROTO_ICMPV6);
+        if (s < 0)
+                return -errno;
+
+        ICMP6_FILTER_SETBLOCKALL(&filter);
+        ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+        r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
+                       sizeof(filter));
+        if (r < 0)
+                return -errno;
+
+        /* RFC 3315, section 6.7, bullet point 2 may indicate that an
+           IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
+           Empirical experiments indicates otherwise and therefore an
+           IPV6_MULTICAST_IF socket option is used here instead */
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index,
+                       sizeof(index));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero,
+                       sizeof(zero));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
+                       sizeof(hops));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+                       sizeof(mreq));
+        if (r < 0)
+                return -errno;
+
+        r = s;
+        s = -1;
+        return r;
+}
+
+int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr)
+{
+        struct sockaddr_in6 dst = {
+                .sin6_family = AF_INET6,
+                .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+        };
+        struct {
+                struct nd_router_solicit rs;
+                struct nd_opt_hdr rs_opt;
+                struct ether_addr rs_opt_mac;
+        } _packed_ rs = {
+                .rs.nd_rs_type = ND_ROUTER_SOLICIT,
+        };
+        struct iovec iov[1] = {
+                { &rs, },
+        };
+        struct msghdr msg = {
+                .msg_name = &dst,
+                .msg_namelen = sizeof(dst),
+                .msg_iov = iov,
+                .msg_iovlen = 1,
+        };
+        int r;
+
+        if (ether_addr) {
+                memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN);
+                rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+                rs.rs_opt.nd_opt_len = 1;
+                iov[0].iov_len = sizeof(rs);
+        } else
+                iov[0].iov_len = sizeof(rs.rs);
+
+        r = sendmsg(s, &msg, 0);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+        struct in6_pktinfo pktinfo = {
+                .ipi6_ifindex = index,
+        };
+        union sockaddr_union src = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
+                .in6.sin6_addr = IN6ADDR_ANY_INIT,
+        };
+        _cleanup_close_ int s = -1;
+        int r, off = 0, on = 1;
+
+        if (local_address)
+                memcpy(&src.in6.sin6_addr, local_address,
+                       sizeof(src.in6.sin6_addr));
+
+        s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                   IPPROTO_UDP);
+        if (s < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo,
+                       sizeof(pktinfo));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
+        if (r < 0)
+                return -errno;
+
+        r = bind(s, &src.sa, sizeof(src.in6));
+        if (r < 0)
+                return -errno;
+
+        r = s;
+        s = -1;
+        return r;
+}
+
+int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
+                                  const void *packet, size_t len) {
+        union sockaddr_union dest = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
+        };
+        int r;
+
+        assert(server_address);
+
+        memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr));
+
+        r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6));
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-option.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-option.c
new file mode 100644
index 00000000..3d6e2462
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-option.c
@@ -0,0 +1,323 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <netinet/in.h>
+#include <errno.h>
+#include <string.h>
+
+#include "sparse-endian.h"
+#include "unaligned.h"
+#include "util.h"
+
+#include "dhcp6-internal.h"
+#include "dhcp6-protocol.h"
+
+#define DHCP6_OPTION_IA_NA_LEN                  12
+#define DHCP6_OPTION_IA_TA_LEN                  4
+
+typedef struct DHCP6Option {
+        be16_t code;
+        be16_t len;
+        uint8_t data[];
+} _packed_ DHCP6Option;
+
+static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
+                             size_t optlen) {
+        DHCP6Option *option = (DHCP6Option*) *buf;
+
+        assert_return(buf, -EINVAL);
+        assert_return(*buf, -EINVAL);
+        assert_return(buflen, -EINVAL);
+
+        if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option))
+                return -ENOBUFS;
+
+        option->code = htobe16(optcode);
+        option->len = htobe16(optlen);
+
+        *buf += sizeof(DHCP6Option);
+        *buflen -= sizeof(DHCP6Option);
+
+        return 0;
+}
+
+int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
+                        size_t optlen, const void *optval) {
+        int r;
+
+        assert_return(optval || optlen == 0, -EINVAL);
+
+        r = option_append_hdr(buf, buflen, code, optlen);
+        if (r < 0)
+                return r;
+
+        if (optval)
+                memcpy(*buf, optval, optlen);
+
+        *buf += optlen;
+        *buflen -= optlen;
+
+        return 0;
+}
+
+int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
+        uint16_t len;
+        uint8_t *ia_hdr;
+        size_t ia_buflen, ia_addrlen = 0;
+        DHCP6Address *addr;
+        int r;
+
+        assert_return(buf && *buf && buflen && ia, -EINVAL);
+
+        switch (ia->type) {
+        case DHCP6_OPTION_IA_NA:
+                len = DHCP6_OPTION_IA_NA_LEN;
+                break;
+
+        case DHCP6_OPTION_IA_TA:
+                len = DHCP6_OPTION_IA_TA_LEN;
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        if (*buflen < len)
+                return -ENOBUFS;
+
+        ia_hdr = *buf;
+        ia_buflen = *buflen;
+
+        *buf += sizeof(DHCP6Option);
+        *buflen -= sizeof(DHCP6Option);
+
+        memcpy(*buf, &ia->id, len);
+
+        *buf += len;
+        *buflen -= len;
+
+        LIST_FOREACH(addresses, addr, ia->addresses) {
+                r = option_append_hdr(buf, buflen, DHCP6_OPTION_IAADDR,
+                                      sizeof(addr->iaaddr));
+                if (r < 0)
+                        return r;
+
+                memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
+
+                *buf += sizeof(addr->iaaddr);
+                *buflen -= sizeof(addr->iaaddr);
+
+                ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr);
+        }
+
+        r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+
+static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
+        DHCP6Option *option = (DHCP6Option*) *buf;
+        uint16_t len;
+
+        assert_return(buf, -EINVAL);
+        assert_return(optcode, -EINVAL);
+        assert_return(optlen, -EINVAL);
+
+        if (*buflen < sizeof(DHCP6Option))
+                return -ENOMSG;
+
+        len = be16toh(option->len);
+
+        if (len > *buflen)
+                return -ENOMSG;
+
+        *optcode = be16toh(option->code);
+        *optlen = len;
+
+        *buf += 4;
+        *buflen -= 4;
+
+        return 0;
+}
+
+int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
+                       size_t *optlen, uint8_t **optvalue) {
+        int r;
+
+        assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
+
+        r = option_parse_hdr(buf, buflen, optcode, optlen);
+        if (r < 0)
+                return r;
+
+        if (*optlen > *buflen)
+                return -ENOBUFS;
+
+        *optvalue = *buf;
+        *buflen -= *optlen;
+        *buf += *optlen;
+
+        return 0;
+}
+
+int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
+                          DHCP6IA *ia) {
+        int r;
+        uint16_t opt, status;
+        size_t optlen;
+        size_t iaaddr_offset;
+        DHCP6Address *addr;
+        uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
+
+        assert_return(ia, -EINVAL);
+        assert_return(!ia->addresses, -EINVAL);
+
+        switch (iatype) {
+        case DHCP6_OPTION_IA_NA:
+
+                if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
+                    sizeof(addr->iaaddr)) {
+                        r = -ENOBUFS;
+                        goto error;
+                }
+
+                iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
+                memcpy(&ia->id, *buf, iaaddr_offset);
+
+                lt_t1 = be32toh(ia->lifetime_t1);
+                lt_t2 = be32toh(ia->lifetime_t2);
+
+                if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
+                        log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
+                                         lt_t1, lt_t2);
+                        r = -EINVAL;
+                        goto error;
+                }
+
+                break;
+
+        case DHCP6_OPTION_IA_TA:
+                if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
+                    sizeof(addr->iaaddr)) {
+                        r = -ENOBUFS;
+                        goto error;
+                }
+
+                iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
+                memcpy(&ia->id, *buf, iaaddr_offset);
+
+                ia->lifetime_t1 = 0;
+                ia->lifetime_t2 = 0;
+
+                break;
+
+        default:
+                r = -ENOMSG;
+                goto error;
+        }
+
+        ia->type = iatype;
+
+        *buflen -= iaaddr_offset;
+        *buf += iaaddr_offset;
+
+        while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
+
+                switch (opt) {
+                case DHCP6_OPTION_IAADDR:
+
+                        addr = new0(DHCP6Address, 1);
+                        if (!addr) {
+                                r = -ENOMEM;
+                                goto error;
+                        }
+
+                        LIST_INIT(addresses, addr);
+
+                        memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
+
+                        lt_valid = be32toh(addr->iaaddr.lifetime_valid);
+                        lt_pref = be32toh(addr->iaaddr.lifetime_valid);
+
+                        if (!lt_valid || lt_pref > lt_valid) {
+                                log_dhcp6_client(client, "IA preferred %ds > valid %ds",
+                                                 lt_pref, lt_valid);
+                                free(addr);
+                        } else {
+                                LIST_PREPEND(addresses, ia->addresses, addr);
+                                if (lt_valid < lt_min)
+                                        lt_min = lt_valid;
+                        }
+
+                        break;
+
+                case DHCP6_OPTION_STATUS_CODE:
+                        if (optlen < sizeof(status))
+                                break;
+
+                        status = (*buf)[0] << 8 | (*buf)[1];
+                        if (status) {
+                                log_dhcp6_client(client, "IA status %d",
+                                                 status);
+                                r = -EINVAL;
+                                goto error;
+                        }
+
+                        break;
+
+                default:
+                        log_dhcp6_client(client, "Unknown IA option %d", opt);
+                        break;
+                }
+
+                *buflen -= optlen;
+                *buf += optlen;
+        }
+
+        if (r == -ENOMSG)
+                r = 0;
+
+        if (!ia->lifetime_t1 && !ia->lifetime_t2) {
+                lt_t1 = lt_min / 2;
+                lt_t2 = lt_min / 10 * 8;
+                ia->lifetime_t1 = htobe32(lt_t1);
+                ia->lifetime_t2 = htobe32(lt_t2);
+
+                log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
+                                 lt_t1, lt_t2);
+        }
+
+        if (*buflen)
+                r = -ENOMSG;
+
+error:
+        *buf += *buflen;
+        *buflen = 0;
+
+        return r;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-protocol.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-protocol.h
new file mode 100644
index 00000000..eaa67171
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-protocol.h
@@ -0,0 +1,141 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+
+#include "macro.h"
+#include "sparse-endian.h"
+
+struct DHCP6Message {
+        union {
+                struct {
+                        uint8_t type;
+                        uint8_t _pad[3];
+                } _packed_;
+                be32_t transaction_id;
+        };
+} _packed_;
+
+typedef struct DHCP6Message DHCP6Message;
+
+#define DHCP6_MIN_OPTIONS_SIZE \
+        1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
+
+#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
+        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
+
+enum {
+        DHCP6_PORT_SERVER                       = 547,
+        DHCP6_PORT_CLIENT                       = 546,
+};
+
+#define DHCP6_SOL_MAX_DELAY                     1 * USEC_PER_SEC
+#define DHCP6_SOL_TIMEOUT                       1 * USEC_PER_SEC
+#define DHCP6_SOL_MAX_RT                        120 * USEC_PER_SEC
+#define DHCP6_REQ_TIMEOUT                       1 * USEC_PER_SEC
+#define DHCP6_REQ_MAX_RT                        120 * USEC_PER_SEC
+#define DHCP6_REQ_MAX_RC                        10
+#define DHCP6_REN_TIMEOUT                       10 * USEC_PER_SEC
+#define DHCP6_REN_MAX_RT                        600 * USEC_PER_SEC
+#define DHCP6_REB_TIMEOUT                       10 * USEC_PER_SEC
+#define DHCP6_REB_MAX_RT                        600 * USEC_PER_SEC
+
+enum {
+        DHCP6_DUID_LLT                          = 1,
+        DHCP6_DUID_EN                           = 2,
+        DHCP6_DUID_LL                           = 3,
+        DHCP6_DUID_UUID                         = 4,
+};
+
+enum DHCP6State {
+        DHCP6_STATE_STOPPED                     = 0,
+        DHCP6_STATE_SOLICITATION                = 2,
+        DHCP6_STATE_REQUEST                     = 3,
+        DHCP6_STATE_BOUND                       = 4,
+        DHCP6_STATE_RENEW                       = 5,
+        DHCP6_STATE_REBIND                      = 6,
+};
+
+enum {
+        DHCP6_SOLICIT                           = 1,
+        DHCP6_ADVERTISE                         = 2,
+        DHCP6_REQUEST                           = 3,
+        DHCP6_CONFIRM                           = 4,
+        DHCP6_RENEW                             = 5,
+        DHCP6_REBIND                            = 6,
+        DHCP6_REPLY                             = 7,
+        DHCP6_RELEASE                           = 8,
+        DHCP6_DECLINE                           = 9,
+        DHCP6_RECONFIGURE                       = 10,
+        DHCP6_INFORMATION_REQUEST               = 11,
+        DHCP6_RELAY_FORW                        = 12,
+        DHCP6_RELAY_REPL                        = 13,
+        _DHCP6_MESSAGE_MAX                      = 14,
+};
+
+enum {
+        DHCP6_OPTION_CLIENTID                   = 1,
+        DHCP6_OPTION_SERVERID                   = 2,
+        DHCP6_OPTION_IA_NA                      = 3,
+        DHCP6_OPTION_IA_TA                      = 4,
+        DHCP6_OPTION_IAADDR                     = 5,
+        DHCP6_OPTION_ORO                        = 6,
+        DHCP6_OPTION_PREFERENCE                 = 7,
+        DHCP6_OPTION_ELAPSED_TIME               = 8,
+        DHCP6_OPTION_RELAY_MSG                  = 9,
+        /* option code 10 is unassigned */
+        DHCP6_OPTION_AUTH                       = 11,
+        DHCP6_OPTION_UNICAST                    = 12,
+        DHCP6_OPTION_STATUS_CODE                = 13,
+        DHCP6_OPTION_RAPID_COMMIT               = 14,
+        DHCP6_OPTION_USER_CLASS                 = 15,
+        DHCP6_OPTION_VENDOR_CLASS               = 16,
+        DHCP6_OPTION_VENDOR_OPTS                = 17,
+        DHCP6_OPTION_INTERFACE_ID               = 18,
+        DHCP6_OPTION_RECONF_MSG                 = 19,
+        DHCP6_OPTION_RECONF_ACCEPT              = 20,
+
+        DHCP6_OPTION_DNS_SERVERS                = 23,  /* RFC 3646 */
+        DHCP6_OPTION_DOMAIN_LIST                = 24,  /* RFC 3646 */
+
+        DHCP6_OPTION_SNTP_SERVERS               = 31,  /* RFC 4075 */
+
+        /* option code 35 is unassigned */
+
+        DHCP6_OPTION_NTP_SERVER                 = 56,  /* RFC 5908 */
+
+        /* option codes 89-142 are unassigned */
+        /* option codes 144-65535 are unassigned */
+};
+
+enum {
+        DHCP6_STATUS_SUCCESS                    = 0,
+        DHCP6_STATUS_UNSPEC_FAIL                = 1,
+        DHCP6_STATUS_NO_ADDRS_AVAIL             = 2,
+        DHCP6_STATUS_NO_BINDING                 = 3,
+        DHCP6_STATUS_NOT_ON_LINK                = 4,
+        DHCP6_STATUS_USE_MULTICAST              = 5,
+        _DHCP6_STATUS_MAX                       = 6,
+};
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.c
new file mode 100644
index 00000000..b9334bd6
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.c
@@ -0,0 +1,484 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <netinet/ether.h>
+#include <linux/if.h>
+#include <arpa/inet.h>
+#include <fnmatch.h>
+
+#if 0 /* NM_IGNORED */
+#include "strv.h"
+#include "siphash24.h"
+#include "libudev-private.h"
+#endif
+#include "dhcp-lease-internal.h"
+#if 0 /* NM_IGNORED */
+#include "log.h"
+#include "utf8.h"
+#endif
+#include "util.h"
+#if 0 /* NM_IGNORED */
+#include "conf-parser.h"
+#include "condition.h"
+#endif
+#include "network-internal.h"
+
+#if 0 /* NM_IGNORED */
+const char *net_get_name(struct udev_device *device) {
+        const char *name, *field;
+
+        assert(device);
+
+        /* fetch some persistent data unique (on this machine) to this device */
+        FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") {
+                name = udev_device_get_property_value(device, field);
+                if (name)
+                        return name;
+        }
+
+        return NULL;
+}
+
+#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
+
+int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) {
+        size_t l, sz = 0;
+        const char *name = NULL;
+        int r;
+        uint8_t *v;
+
+        assert(device);
+
+        name = net_get_name(device);
+        if (!name)
+                return -ENOENT;
+
+        l = strlen(name);
+        sz = sizeof(sd_id128_t) + l;
+        v = alloca(sz);
+
+        /* fetch some persistent data unique to this machine */
+        r = sd_id128_get_machine((sd_id128_t*) v);
+        if (r < 0)
+                 return r;
+        memcpy(v + sizeof(sd_id128_t), name, l);
+
+        /* Let's hash the machine ID plus the device name. We
+        * use a fixed, but originally randomly created hash
+        * key here. */
+        siphash24(result, v, sz, HASH_KEY.bytes);
+
+        return 0;
+}
+
+bool net_match_config(const struct ether_addr *match_mac,
+                      const char *match_path,
+                      const char *match_driver,
+                      const char *match_type,
+                      const char *match_name,
+                      Condition *match_host,
+                      Condition *match_virt,
+                      Condition *match_kernel,
+                      Condition *match_arch,
+                      const struct ether_addr *dev_mac,
+                      const char *dev_path,
+                      const char *dev_parent_driver,
+                      const char *dev_driver,
+                      const char *dev_type,
+                      const char *dev_name) {
+
+        if (match_host && !condition_test(match_host))
+                return 0;
+
+        if (match_virt && !condition_test(match_virt))
+                return 0;
+
+        if (match_kernel && !condition_test(match_kernel))
+                return 0;
+
+        if (match_arch && !condition_test(match_arch))
+                return 0;
+
+        if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
+                return 0;
+
+        if (match_path && (!dev_path || fnmatch(match_path, dev_path, 0)))
+                return 0;
+
+        if (match_driver) {
+                if (dev_parent_driver && !streq(match_driver, dev_parent_driver))
+                        return 0;
+                else if (!streq_ptr(match_driver, dev_driver))
+                        return 0;
+        }
+
+        if (match_type && !streq_ptr(match_type, dev_type))
+                return 0;
+
+        if (match_name && (!dev_name || fnmatch(match_name, dev_name, 0)))
+                return 0;
+
+        return 1;
+}
+
+int config_parse_net_condition(const char *unit,
+                               const char *filename,
+                               unsigned line,
+                               const char *section,
+                               unsigned section_line,
+                               const char *lvalue,
+                               int ltype,
+                               const char *rvalue,
+                               void *data,
+                               void *userdata) {
+
+        ConditionType cond = ltype;
+        Condition **ret = data;
+        bool negate;
+        Condition *c;
+        _cleanup_free_ char *s = NULL;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        negate = rvalue[0] == '!';
+        if (negate)
+                rvalue++;
+
+        s = strdup(rvalue);
+        if (!s)
+                return log_oom();
+
+        c = condition_new(cond, s, false, negate);
+        if (!c)
+                return log_oom();
+
+        if (*ret)
+                condition_free(*ret);
+
+        *ret = c;
+        return 0;
+}
+
+int config_parse_ifname(const char *unit,
+                        const char *filename,
+                        unsigned line,
+                        const char *section,
+                        unsigned section_line,
+                        const char *lvalue,
+                        int ltype,
+                        const char *rvalue,
+                        void *data,
+                        void *userdata) {
+
+        char **s = data;
+        _cleanup_free_ char *n = NULL;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        n = strdup(rvalue);
+        if (!n)
+                return log_oom();
+
+        if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        free(*s);
+        if (*n) {
+                *s = n;
+                n = NULL;
+        } else
+                *s = NULL;
+
+        return 0;
+}
+
+int config_parse_ifalias(const char *unit,
+                         const char *filename,
+                         unsigned line,
+                         const char *section,
+                         unsigned section_line,
+                         const char *lvalue,
+                         int ltype,
+                         const char *rvalue,
+                         void *data,
+                         void *userdata) {
+
+        char **s = data;
+        char *n;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        n = strdup(rvalue);
+        if (!n)
+                return log_oom();
+
+        if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+                free(n);
+                return 0;
+        }
+
+        free(*s);
+        if (*n)
+                *s = n;
+        else {
+                free(n);
+                *s = NULL;
+        }
+
+        return 0;
+}
+
+int config_parse_hwaddr(const char *unit,
+                        const char *filename,
+                        unsigned line,
+                        const char *section,
+                        unsigned section_line,
+                        const char *lvalue,
+                        int ltype,
+                        const char *rvalue,
+                        void *data,
+                        void *userdata) {
+        struct ether_addr **hwaddr = data;
+        struct ether_addr *n;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        n = new0(struct ether_addr, 1);
+        if (!n)
+                return log_oom();
+
+        r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+                   &n->ether_addr_octet[0],
+                   &n->ether_addr_octet[1],
+                   &n->ether_addr_octet[2],
+                   &n->ether_addr_octet[3],
+                   &n->ether_addr_octet[4],
+                   &n->ether_addr_octet[5]);
+        if (r != 6) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Not a valid MAC address, ignoring assignment: %s", rvalue);
+                free(n);
+                return 0;
+        }
+
+        free(*hwaddr);
+        *hwaddr = n;
+
+        return 0;
+}
+#endif
+
+void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) {
+        unsigned i;
+
+        assert(f);
+        assert(addresses);
+        assert(size);
+
+        for (i = 0; i < size; i++)
+                fprintf(f, "%s%s", inet_ntoa(addresses[i]),
+                        (i < (size - 1)) ? " ": "");
+}
+
+int deserialize_in_addrs(struct in_addr **ret, const char *string) {
+        _cleanup_free_ struct in_addr *addresses = NULL;
+        int size = 0;
+        const char *word, *state;
+        size_t len;
+
+        assert(ret);
+        assert(string);
+
+        FOREACH_WORD(word, len, string, state) {
+                _cleanup_free_ char *addr_str = NULL;
+                struct in_addr *new_addresses;
+                int r;
+
+                new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr));
+                if (!new_addresses)
+                        return -ENOMEM;
+                else
+                        addresses = new_addresses;
+
+                addr_str = strndup(word, len);
+                if (!addr_str)
+                        return -ENOMEM;
+
+                r = inet_pton(AF_INET, addr_str, &(addresses[size]));
+                if (r <= 0)
+                        continue;
+
+                size ++;
+        }
+
+        *ret = addresses;
+        addresses = NULL;
+
+        return size;
+}
+
+int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
+        _cleanup_free_ struct in6_addr *addresses = NULL;
+        int size = 0;
+        const char *word, *state;
+        size_t len;
+
+        assert(ret);
+        assert(string);
+
+        FOREACH_WORD(word, len, string, state) {
+                _cleanup_free_ char *addr_str = NULL;
+                struct in6_addr *new_addresses;
+                int r;
+
+                new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr));
+                if (!new_addresses)
+                        return -ENOMEM;
+                else
+                        addresses = new_addresses;
+
+                addr_str = strndup(word, len);
+                if (!addr_str)
+                        return -ENOMEM;
+
+                r = inet_pton(AF_INET6, addr_str, &(addresses[size]));
+                if (r <= 0)
+                        continue;
+
+                size++;
+        }
+
+        *ret = addresses;
+        addresses = NULL;
+
+        return size;
+}
+
+void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size) {
+        unsigned i;
+
+        assert(f);
+        assert(key);
+        assert(routes);
+        assert(size);
+
+        fprintf(f, "%s=", key);
+
+        for (i = 0; i < size; i++) {
+                fprintf(f, "%s/%" PRIu8, inet_ntoa(routes[i].dst_addr),
+                        routes[i].dst_prefixlen);
+                fprintf(f, ",%s%s", inet_ntoa(routes[i].gw_addr),
+                        (i < (size - 1)) ? " ": "");
+        }
+
+        fputs("\n", f);
+}
+
+int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
+        _cleanup_free_ struct sd_dhcp_route *routes = NULL;
+        size_t size = 0, allocated = 0;
+        const char *word, *state;
+        size_t len;
+
+        assert(ret);
+        assert(ret_size);
+        assert(ret_allocated);
+        assert(string);
+
+        FOREACH_WORD(word, len, string, state) {
+                /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
+                _cleanup_free_ char* entry = NULL;
+                char *tok, *tok_end;
+                unsigned n;
+                int r;
+
+                if (!GREEDY_REALLOC(routes, allocated, size + 1))
+                        return -ENOMEM;
+
+                entry = strndup(word, len);
+                if(!entry)
+                        return -ENOMEM;
+
+                tok = entry;
+
+                /* get the subnet */
+                tok_end = strchr(tok, '/');
+                if (!tok_end)
+                        continue;
+                *tok_end = '\0';
+
+                r = inet_aton(tok, &routes[size].dst_addr);
+                if (r == 0)
+                        continue;
+
+                tok = tok_end + 1;
+
+                /* get the prefixlen */
+                tok_end = strchr(tok, ',');
+                if (!tok_end)
+                        continue;
+
+                *tok_end = '\0';
+
+                r = safe_atou(tok, &n);
+                if (r < 0 || n > 32)
+                        continue;
+
+                routes[size].dst_prefixlen = (uint8_t) n;
+                tok = tok_end + 1;
+
+                /* get the gateway */
+                r = inet_aton(tok, &routes[size].gw_addr);
+                if (r == 0)
+                        continue;
+
+                size++;
+        }
+
+        *ret_size = size;
+        *ret_allocated = allocated;
+        *ret = routes;
+        routes = NULL;
+
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.h
new file mode 100644
index 00000000..f87c863d
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.h
@@ -0,0 +1,78 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+
+#if 0 /* NM_IGNORED */
+#include "udev.h"
+#include "condition.h"
+
+bool net_match_config(const struct ether_addr *match_mac,
+                      const char *match_path,
+                      const char *match_driver,
+                      const char *match_type,
+                      const char *match_name,
+                      Condition *match_host,
+                      Condition *match_virt,
+                      Condition *match_kernel,
+                      Condition *match_arch,
+                      const struct ether_addr *dev_mac,
+                      const char *dev_path,
+                      const char *dev_parent_driver,
+                      const char *dev_driver,
+                      const char *dev_type,
+                      const char *dev_name);
+
+int config_parse_net_condition(const char *unit, const char *filename, unsigned line,
+                               const char *section, unsigned section_line, const char *lvalue,
+                               int ltype, const char *rvalue, void *data, void *userdata);
+
+int config_parse_hwaddr(const char *unit, const char *filename, unsigned line,
+                        const char *section, unsigned section_line, const char *lvalue,
+                        int ltype, const char *rvalue, void *data, void *userdata);
+
+int config_parse_ifname(const char *unit, const char *filename, unsigned line,
+                        const char *section, unsigned section_line, const char *lvalue,
+                        int ltype, const char *rvalue, void *data, void *userdata);
+
+int config_parse_ifalias(const char *unit, const char *filename, unsigned line,
+                         const char *section, unsigned section_line, const char *lvalue,
+                         int ltype, const char *rvalue, void *data, void *userdata);
+
+int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]);
+const char *net_get_name(struct udev_device *device);
+#endif
+
+void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size);
+int deserialize_in_addrs(struct in_addr **addresses, const char *string);
+int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
+
+/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
+struct sd_dhcp_route;
+
+void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size);
+int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-client.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-client.c
new file mode 100644
index 00000000..3e4cd8d9
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-client.c
@@ -0,0 +1,1726 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <linux/if_infiniband.h>
+#include <netinet/ether.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include "util.h"
+#include "list.h"
+#include "refcnt.h"
+#include "async.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+#include "dhcp-lease-internal.h"
+#include "sd-dhcp-client.h"
+
+#define MAX_CLIENT_ID_LEN 64  /* Arbitrary limit */
+#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
+
+struct sd_dhcp_client {
+        RefCount n_ref;
+
+        DHCPState state;
+        sd_event *event;
+        int event_priority;
+        sd_event_source *timeout_resend;
+        int index;
+        int fd;
+        union sockaddr_union link;
+        sd_event_source *receive_message;
+        bool request_broadcast;
+        uint8_t *req_opts;
+        size_t req_opts_allocated;
+        size_t req_opts_size;
+        be32_t last_addr;
+        uint8_t mac_addr[MAX_MAC_ADDR_LEN];
+        size_t mac_addr_len;
+        uint16_t arp_type;
+        union {
+                struct {
+                        uint8_t type; /* 0: Generic (non-LL) (RFC 2132) */
+                        uint8_t data[MAX_CLIENT_ID_LEN];
+                } _packed_ gen;
+                struct {
+                        uint8_t type; /* 1: Ethernet Link-Layer (RFC 2132) */
+                        uint8_t haddr[ETH_ALEN];
+                } _packed_ eth;
+                struct {
+                        uint8_t type; /* 2 - 254: ARP/Link-Layer (RFC 2132) */
+                        uint8_t haddr[0];
+                } _packed_ ll;
+                struct {
+                        uint8_t type; /* 255: Node-specific (RFC 4361) */
+                        uint8_t iaid[4];
+                        uint8_t duid[MAX_CLIENT_ID_LEN - 4];
+                } _packed_ ns;
+                struct {
+                        uint8_t type;
+                        uint8_t data[MAX_CLIENT_ID_LEN];
+                } _packed_ raw;
+        } client_id;
+        size_t client_id_len;
+        char *hostname;
+        char *vendor_class_identifier;
+        uint32_t mtu;
+        uint32_t xid;
+        usec_t start_time;
+        unsigned int attempt;
+        usec_t request_sent;
+        sd_event_source *timeout_t1;
+        sd_event_source *timeout_t2;
+        sd_event_source *timeout_expire;
+        sd_dhcp_client_cb_t cb;
+        void *userdata;
+        sd_dhcp_lease *lease;
+};
+
+static const uint8_t default_req_opts[] = {
+        DHCP_OPTION_SUBNET_MASK,
+        DHCP_OPTION_ROUTER,
+        DHCP_OPTION_HOST_NAME,
+        DHCP_OPTION_DOMAIN_NAME,
+        DHCP_OPTION_DOMAIN_NAME_SERVER,
+        DHCP_OPTION_NTP_SERVER,
+};
+
+static int client_receive_message_raw(sd_event_source *s, int fd,
+                                      uint32_t revents, void *userdata);
+static int client_receive_message_udp(sd_event_source *s, int fd,
+                                      uint32_t revents, void *userdata);
+static void client_stop(sd_dhcp_client *client, int error);
+
+int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
+                                void *userdata) {
+        assert_return(client, -EINVAL);
+
+        client->cb = cb;
+        client->userdata = userdata;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
+        assert_return(client, -EINVAL);
+
+        client->request_broadcast = !!broadcast;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
+        size_t i;
+
+        assert_return(client, -EINVAL);
+        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+                              DHCP_STATE_STOPPED), -EBUSY);
+
+        switch(option) {
+        case DHCP_OPTION_PAD:
+        case DHCP_OPTION_OVERLOAD:
+        case DHCP_OPTION_MESSAGE_TYPE:
+        case DHCP_OPTION_PARAMETER_REQUEST_LIST:
+        case DHCP_OPTION_END:
+                return -EINVAL;
+
+        default:
+                break;
+        }
+
+        for (i = 0; i < client->req_opts_size; i++)
+                if (client->req_opts[i] == option)
+                        return -EEXIST;
+
+        if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
+                            client->req_opts_size + 1))
+                return -ENOMEM;
+
+        client->req_opts[client->req_opts_size++] = option;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
+                                       const struct in_addr *last_addr) {
+        assert_return(client, -EINVAL);
+        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+                              DHCP_STATE_STOPPED), -EBUSY);
+
+        if (last_addr)
+                client->last_addr = last_addr->s_addr;
+        else
+                client->last_addr = INADDR_ANY;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
+        assert_return(client, -EINVAL);
+        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+                              DHCP_STATE_STOPPED), -EBUSY);
+        assert_return(interface_index > 0, -EINVAL);
+
+        client->index = interface_index;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
+                           size_t addr_len, uint16_t arp_type) {
+        DHCP_CLIENT_DONT_DESTROY(client);
+        bool need_restart = false;
+
+        assert_return(client, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
+        assert_return(arp_type > 0, -EINVAL);
+
+        if (arp_type == ARPHRD_ETHER)
+                assert_return(addr_len == ETH_ALEN, -EINVAL);
+        else if (arp_type == ARPHRD_INFINIBAND)
+                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+        else
+                return -EINVAL;
+
+        if (client->mac_addr_len == addr_len &&
+            memcmp(&client->mac_addr, addr, addr_len) == 0)
+                return 0;
+
+        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
+                log_dhcp_client(client, "Changing MAC address on running DHCP "
+                                "client, restarting");
+                need_restart = true;
+                client_stop(client, DHCP_EVENT_STOP);
+        }
+
+        memcpy(&client->mac_addr, addr, addr_len);
+        client->mac_addr_len = addr_len;
+        client->arp_type = arp_type;
+
+        if (need_restart && client->state != DHCP_STATE_STOPPED)
+                sd_dhcp_client_start(client);
+
+        return 0;
+}
+
+int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
+                                 const uint8_t **data, size_t *data_len) {
+
+        assert_return(client, -EINVAL);
+        assert_return(type, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(data_len, -EINVAL);
+
+        *type = 0;
+        *data = NULL;
+        *data_len = 0;
+        if (client->client_id_len) {
+                *type = client->client_id.raw.type;
+                *data = client->client_id.raw.data;
+                *data_len = client->client_id_len -
+                        sizeof (client->client_id.raw.type);
+        }
+
+        return 0;
+}
+
+int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
+                                 const uint8_t *data, size_t data_len) {
+        DHCP_CLIENT_DONT_DESTROY(client);
+        bool need_restart = false;
+
+        assert_return(client, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
+
+        switch (type) {
+        case ARPHRD_ETHER:
+                if (data_len != ETH_ALEN)
+                        return -EINVAL;
+                break;
+        case ARPHRD_INFINIBAND:
+                if (data_len != INFINIBAND_ALEN)
+                        return -EINVAL;
+                break;
+        default:
+                break;
+        }
+
+        if (client->client_id_len == data_len + sizeof (client->client_id.raw.type) &&
+            client->client_id.raw.type == type &&
+            memcmp(&client->client_id.raw.data, data, data_len) == 0)
+                return 0;
+
+        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
+                log_dhcp_client(client, "Changing client ID on running DHCP "
+                                "client, restarting");
+                need_restart = true;
+                client_stop(client, DHCP_EVENT_STOP);
+        }
+
+        client->client_id.raw.type = type;
+        memcpy(&client->client_id.raw.data, data, data_len);
+        client->client_id_len = data_len + sizeof (client->client_id.raw.type);
+
+        if (need_restart && client->state != DHCP_STATE_STOPPED)
+                sd_dhcp_client_start(client);
+
+        return 0;
+}
+
+int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
+                                const char *hostname) {
+        char *new_hostname = NULL;
+
+        assert_return(client, -EINVAL);
+
+        if (streq_ptr(client->hostname, hostname))
+                return 0;
+
+        if (hostname) {
+                new_hostname = strdup(hostname);
+                if (!new_hostname)
+                        return -ENOMEM;
+        }
+
+        free(client->hostname);
+        client->hostname = new_hostname;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
+                                               const char *vci) {
+        char *new_vci = NULL;
+
+        assert_return(client, -EINVAL);
+
+        new_vci = strdup(vci);
+        if (!new_vci)
+                return -ENOMEM;
+
+        free(client->vendor_class_identifier);
+
+        client->vendor_class_identifier = new_vci;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
+        assert_return(client, -EINVAL);
+        assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
+
+        client->mtu = mtu;
+
+        return 0;
+}
+
+int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
+        assert_return(client, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (client->state != DHCP_STATE_BOUND &&
+            client->state != DHCP_STATE_RENEWING &&
+            client->state != DHCP_STATE_REBINDING)
+                return -EADDRNOTAVAIL;
+
+        *ret = sd_dhcp_lease_ref(client->lease);
+
+        return 0;
+}
+
+static void client_notify(sd_dhcp_client *client, int event) {
+        if (client->cb)
+                client->cb(client, event, client->userdata);
+}
+
+static int client_initialize(sd_dhcp_client *client) {
+        assert_return(client, -EINVAL);
+
+        client->receive_message =
+                sd_event_source_unref(client->receive_message);
+
+        client->fd = asynchronous_close(client->fd);
+
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
+        client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
+        client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
+        client->timeout_expire = sd_event_source_unref(client->timeout_expire);
+
+        client->attempt = 1;
+
+        client->state = DHCP_STATE_INIT;
+        client->xid = 0;
+
+        if (client->lease)
+                client->lease = sd_dhcp_lease_unref(client->lease);
+
+        return 0;
+}
+
+static void client_stop(sd_dhcp_client *client, int error) {
+        assert(client);
+
+        if (error < 0)
+                log_dhcp_client(client, "STOPPED: %s", strerror(-error));
+        else if (error == DHCP_EVENT_STOP)
+                log_dhcp_client(client, "STOPPED");
+        else
+                log_dhcp_client(client, "STOPPED: Unknown event");
+
+        client_notify(client, error);
+
+        client_initialize(client);
+}
+
+static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
+                               uint8_t type, size_t *_optlen, size_t *_optoffset) {
+        _cleanup_free_ DHCPPacket *packet = NULL;
+        size_t optlen, optoffset, size;
+        be16_t max_size;
+        usec_t time_now;
+        uint16_t secs;
+        int r;
+
+        assert(client);
+        assert(client->start_time);
+        assert(ret);
+        assert(_optlen);
+        assert(_optoffset);
+        assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
+
+        /* See RFC2131 section 4.4.1 */
+
+        optlen = DHCP_MIN_OPTIONS_SIZE;
+        size = sizeof(DHCPPacket) + optlen;
+
+        packet = malloc0(size);
+        if (!packet)
+                return -ENOMEM;
+
+        r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
+                              client->arp_type, optlen, &optoffset);
+        if (r < 0)
+                return r;
+
+        /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
+           refuse to issue an DHCP lease if 'secs' is set to zero */
+        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                return r;
+        assert(time_now >= client->start_time);
+
+        /* seconds between sending first and last DISCOVER
+         * must always be strictly positive to deal with broken servers */
+        secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
+        packet->dhcp.secs = htobe16(secs);
+
+        /* RFC2132 section 4.1
+           A client that cannot receive unicast IP datagrams until its protocol
+           software has been configured with an IP address SHOULD set the
+           BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
+           DHCPREQUEST messages that client sends.  The BROADCAST bit will
+           provide a hint to the DHCP server and BOOTP relay agent to broadcast
+           any messages to the client on the client's subnet.
+
+           Note: some interfaces needs this to be enabled, but some networks
+           needs this to be disabled as broadcasts are filteretd, so this
+           needs to be configurable */
+        if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
+                packet->dhcp.flags = htobe16(0x8000);
+
+        /* RFC2132 section 4.1.1:
+           The client MUST include its hardware address in the ’chaddr’ field, if
+           necessary for delivery of DHCP reply messages.  Non-Ethernet
+           interfaces will leave 'chaddr' empty and use the client identifier
+           instead (eg, RFC 4390 section 2.1).
+         */
+        if (client->arp_type == ARPHRD_ETHER)
+                memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
+
+        /* If no client identifier exists, construct one from an ethernet
+           address if present */
+        if (client->client_id_len == 0 && client->arp_type == ARPHRD_ETHER) {
+                client->client_id.eth.type = ARPHRD_ETHER;
+                memcpy(&client->client_id.eth.haddr, &client->mac_addr, ETH_ALEN);
+                client->client_id_len = sizeof (client->client_id.eth);
+        }
+
+        /* Some DHCP servers will refuse to issue an DHCP lease if the Client
+           Identifier option is not set */
+        if (client->client_id_len) {
+                r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
+                                       DHCP_OPTION_CLIENT_IDENTIFIER,
+                                       client->client_id_len,
+                                       &client->client_id.raw);
+                if (r < 0)
+                        return r;
+        }
+
+        /* RFC2131 section 3.5:
+           in its initial DHCPDISCOVER or DHCPREQUEST message, a
+           client may provide the server with a list of specific
+           parameters the client is interested in. If the client
+           includes a list of parameters in a DHCPDISCOVER message,
+           it MUST include that list in any subsequent DHCPREQUEST
+           messages.
+         */
+        r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
+                               DHCP_OPTION_PARAMETER_REQUEST_LIST,
+                               client->req_opts_size, client->req_opts);
+        if (r < 0)
+                return r;
+
+        /* RFC2131 section 3.5:
+           The client SHOULD include the ’maximum DHCP message size’ option to
+           let the server know how large the server may make its DHCP messages.
+
+           Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
+           than the defined default size unless the Maximum Messge Size option
+           is explicitely set
+
+           RFC3442 "Requirements to Avoid Sizing Constraints":
+           Because a full routing table can be quite large, the standard 576
+           octet maximum size for a DHCP message may be too short to contain
+           some legitimate Classless Static Route options.  Because of this,
+           clients implementing the Classless Static Route option SHOULD send a
+           Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
+           stack is capable of receiving larger IP datagrams.  In this case, the
+           client SHOULD set the value of this option to at least the MTU of the
+           interface that the client is configuring.  The client MAY set the
+           value of this option higher, up to the size of the largest UDP packet
+           it is prepared to accept.  (Note that the value specified in the
+           Maximum DHCP Message Size option is the total maximum packet size,
+           including IP and UDP headers.)
+         */
+        max_size = htobe16(size);
+        r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
+                               DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
+                               2, &max_size);
+        if (r < 0)
+                return r;
+
+        *_optlen = optlen;
+        *_optoffset = optoffset;
+        *ret = packet;
+        packet = NULL;
+
+        return 0;
+}
+
+static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
+                                size_t len) {
+        dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
+                                      INADDR_BROADCAST, DHCP_PORT_SERVER, len);
+
+        return dhcp_network_send_raw_socket(client->fd, &client->link,
+                                            packet, len);
+}
+
+static int client_send_discover(sd_dhcp_client *client) {
+        _cleanup_free_ DHCPPacket *discover = NULL;
+        size_t optoffset, optlen;
+        int r;
+
+        assert(client);
+        assert(client->state == DHCP_STATE_INIT ||
+               client->state == DHCP_STATE_SELECTING);
+
+        r = client_message_init(client, &discover, DHCP_DISCOVER,
+                                &optlen, &optoffset);
+        if (r < 0)
+                return r;
+
+        /* the client may suggest values for the network address
+           and lease time in the DHCPDISCOVER message. The client may include
+           the ’requested IP address’ option to suggest that a particular IP
+           address be assigned, and may include the ’IP address lease time’
+           option to suggest the lease time it would like.
+         */
+        if (client->last_addr != INADDR_ANY) {
+                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                       DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                       4, &client->last_addr);
+                if (r < 0)
+                        return r;
+        }
+
+        /* it is unclear from RFC 2131 if client should send hostname in
+           DHCPDISCOVER but dhclient does and so we do as well
+        */
+        if (client->hostname) {
+                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                       DHCP_OPTION_HOST_NAME,
+                                       strlen(client->hostname), client->hostname);
+                if (r < 0)
+                        return r;
+        }
+
+        if (client->vendor_class_identifier) {
+                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                       DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
+                                       strlen(client->vendor_class_identifier),
+                                       client->vendor_class_identifier);
+                if (r < 0)
+                        return r;
+        }
+
+        r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                               DHCP_OPTION_END, 0, NULL);
+        if (r < 0)
+                return r;
+
+        /* We currently ignore:
+           The client SHOULD wait a random time between one and ten seconds to
+           desynchronize the use of DHCP at startup.
+         */
+        r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
+        if (r < 0)
+                return r;
+
+        log_dhcp_client(client, "DISCOVER");
+
+        return 0;
+}
+
+static int client_send_request(sd_dhcp_client *client) {
+        _cleanup_free_ DHCPPacket *request = NULL;
+        size_t optoffset, optlen;
+        int r;
+
+        r = client_message_init(client, &request, DHCP_REQUEST,
+                                &optlen, &optoffset);
+        if (r < 0)
+                return r;
+
+        switch (client->state) {
+        /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
+           SELECTING should be REQUESTING)
+         */
+
+        case DHCP_STATE_REQUESTING:
+                /* Client inserts the address of the selected server in ’server
+                   identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
+                   filled in with the yiaddr value from the chosen DHCPOFFER.
+                 */
+
+                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+                                       DHCP_OPTION_SERVER_IDENTIFIER,
+                                       4, &client->lease->server_address);
+                if (r < 0)
+                        return r;
+
+                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+                                       DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                       4, &client->lease->address);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP_STATE_INIT_REBOOT:
+                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
+                   option MUST be filled in with client’s notion of its previously
+                   assigned address. ’ciaddr’ MUST be zero.
+                 */
+                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+                                       DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                       4, &client->last_addr);
+                if (r < 0)
+                        return r;
+                break;
+
+        case DHCP_STATE_RENEWING:
+                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
+                   option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
+                   client’s IP address.
+                */
+
+                /* fall through */
+        case DHCP_STATE_REBINDING:
+                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
+                   option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
+                   client’s IP address.
+
+                   This message MUST be broadcast to the 0xffffffff IP broadcast address.
+                 */
+                request->dhcp.ciaddr = client->lease->address;
+
+                break;
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_STOPPED:
+                return -EINVAL;
+        }
+
+        if (client->hostname) {
+                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+                                       DHCP_OPTION_HOST_NAME,
+                                       strlen(client->hostname), client->hostname);
+                if (r < 0)
+                        return r;
+        }
+
+        r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+                               DHCP_OPTION_END, 0, NULL);
+        if (r < 0)
+                return r;
+
+        if (client->state == DHCP_STATE_RENEWING) {
+                r = dhcp_network_send_udp_socket(client->fd,
+                                                 client->lease->server_address,
+                                                 DHCP_PORT_SERVER,
+                                                 &request->dhcp,
+                                                 sizeof(DHCPMessage) + optoffset);
+        } else {
+                r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
+        }
+        if (r < 0)
+                return r;
+
+        switch (client->state) {
+        case DHCP_STATE_REQUESTING:
+                log_dhcp_client(client, "REQUEST (requesting)");
+                break;
+        case DHCP_STATE_INIT_REBOOT:
+                log_dhcp_client(client, "REQUEST (init-reboot)");
+                break;
+        case DHCP_STATE_RENEWING:
+                log_dhcp_client(client, "REQUEST (renewing)");
+                break;
+        case DHCP_STATE_REBINDING:
+                log_dhcp_client(client, "REQUEST (rebinding)");
+                break;
+        default:
+                log_dhcp_client(client, "REQUEST (invalid)");
+                break;
+        }
+
+        return 0;
+}
+
+static int client_start(sd_dhcp_client *client);
+
+static int client_timeout_resend(sd_event_source *s, uint64_t usec,
+                                 void *userdata) {
+        sd_dhcp_client *client = userdata;
+        DHCP_CLIENT_DONT_DESTROY(client);
+        usec_t next_timeout = 0;
+        uint64_t time_now;
+        uint32_t time_left;
+        int r;
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                goto error;
+
+        switch (client->state) {
+        case DHCP_STATE_RENEWING:
+
+                time_left = (client->lease->t2 - client->lease->t1) / 2;
+                if (time_left < 60)
+                        time_left = 60;
+
+                next_timeout = time_now + time_left * USEC_PER_SEC;
+
+                break;
+
+        case DHCP_STATE_REBINDING:
+
+                time_left = (client->lease->lifetime - client->lease->t2) / 2;
+                if (time_left < 60)
+                        time_left = 60;
+
+                next_timeout = time_now + time_left * USEC_PER_SEC;
+                break;
+
+        case DHCP_STATE_REBOOTING:
+                /* start over as we did not receive a timely ack or nak */
+                r = client_initialize(client);
+                if (r < 0)
+                        goto error;
+
+                r = client_start(client);
+                if (r < 0)
+                        goto error;
+                else {
+                        log_dhcp_client(client, "REBOOTED");
+                        return 0;
+                }
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_BOUND:
+
+                if (client->attempt < 64)
+                        client->attempt *= 2;
+
+                next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
+
+                break;
+
+        case DHCP_STATE_STOPPED:
+                r = -EINVAL;
+                goto error;
+        }
+
+        next_timeout += (random_u32() & 0x1fffff);
+
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
+        r = sd_event_add_time(client->event,
+                              &client->timeout_resend,
+                              clock_boottime_or_monotonic(),
+                              next_timeout, 10 * USEC_PER_MSEC,
+                              client_timeout_resend, client);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(client->timeout_resend,
+                                         client->event_priority);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
+        if (r < 0)
+                goto error;
+
+        switch (client->state) {
+        case DHCP_STATE_INIT:
+                r = client_send_discover(client);
+                if (r >= 0) {
+                        client->state = DHCP_STATE_SELECTING;
+                        client->attempt = 1;
+                } else {
+                        if (client->attempt >= 64)
+                                goto error;
+                }
+
+                break;
+
+        case DHCP_STATE_SELECTING:
+                r = client_send_discover(client);
+                if (r < 0 && client->attempt >= 64)
+                        goto error;
+
+                break;
+
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+                r = client_send_request(client);
+                if (r < 0 && client->attempt >= 64)
+                         goto error;
+
+                if (client->state == DHCP_STATE_INIT_REBOOT)
+                        client->state = DHCP_STATE_REBOOTING;
+
+                client->request_sent = time_now;
+
+                break;
+
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_BOUND:
+
+                break;
+
+        case DHCP_STATE_STOPPED:
+                r = -EINVAL;
+                goto error;
+        }
+
+        return 0;
+
+error:
+        client_stop(client, r);
+
+        /* Errors were dealt with when stopping the client, don't spill
+           errors into the event loop handler */
+        return 0;
+}
+
+static int client_initialize_io_events(sd_dhcp_client *client,
+                                       sd_event_io_handler_t io_callback) {
+        int r;
+
+        assert(client);
+        assert(client->event);
+
+        r = sd_event_add_io(client->event, &client->receive_message,
+                            client->fd, EPOLLIN, io_callback,
+                            client);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(client->receive_message,
+                                         client->event_priority);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
+        if (r < 0)
+                goto error;
+
+error:
+        if (r < 0)
+                client_stop(client, r);
+
+        return 0;
+}
+
+static int client_initialize_time_events(sd_dhcp_client *client) {
+        int r;
+
+        assert(client);
+        assert(client->event);
+
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
+        r = sd_event_add_time(client->event,
+                              &client->timeout_resend,
+                              clock_boottime_or_monotonic(),
+                              0, 0,
+                              client_timeout_resend, client);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(client->timeout_resend,
+                                         client->event_priority);
+
+        r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
+        if (r < 0)
+                goto error;
+
+error:
+        if (r < 0)
+                client_stop(client, r);
+
+        return 0;
+
+}
+
+static int client_initialize_events(sd_dhcp_client *client,
+                                    sd_event_io_handler_t io_callback) {
+        client_initialize_io_events(client, io_callback);
+        client_initialize_time_events(client);
+
+        return 0;
+}
+
+static int client_start(sd_dhcp_client *client) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->event, -EINVAL);
+        assert_return(client->index > 0, -EINVAL);
+        assert_return(client->fd < 0, -EBUSY);
+        assert_return(client->xid == 0, -EINVAL);
+        assert_return(client->state == DHCP_STATE_INIT ||
+                      client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
+
+        client->xid = random_u32();
+
+        r = dhcp_network_bind_raw_socket(client->index, &client->link,
+                                         client->xid, client->mac_addr,
+                                         client->mac_addr_len, client->arp_type);
+        if (r < 0) {
+                client_stop(client, r);
+                return r;
+        }
+        client->fd = r;
+
+        if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT)
+                client->start_time = now(clock_boottime_or_monotonic());
+
+        return client_initialize_events(client, client_receive_message_raw);
+}
+
+static int client_timeout_expire(sd_event_source *s, uint64_t usec,
+                                 void *userdata) {
+        sd_dhcp_client *client = userdata;
+        DHCP_CLIENT_DONT_DESTROY(client);
+
+        log_dhcp_client(client, "EXPIRED");
+
+        client_notify(client, DHCP_EVENT_EXPIRED);
+
+        /* lease was lost, start over if not freed or stopped in callback */
+        if (client->state != DHCP_STATE_STOPPED) {
+                client_initialize(client);
+                client_start(client);
+        }
+
+        return 0;
+}
+
+static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
+        sd_dhcp_client *client = userdata;
+        DHCP_CLIENT_DONT_DESTROY(client);
+        int r;
+
+        client->receive_message = sd_event_source_unref(client->receive_message);
+        client->fd = asynchronous_close(client->fd);
+
+        client->state = DHCP_STATE_REBINDING;
+        client->attempt = 1;
+
+        r = dhcp_network_bind_raw_socket(client->index, &client->link,
+                                         client->xid, client->mac_addr,
+                                         client->mac_addr_len, client->arp_type);
+        if (r < 0) {
+                client_stop(client, r);
+                return 0;
+        }
+        client->fd = r;
+
+        return client_initialize_events(client, client_receive_message_raw);
+}
+
+static int client_timeout_t1(sd_event_source *s, uint64_t usec,
+                             void *userdata) {
+        sd_dhcp_client *client = userdata;
+        DHCP_CLIENT_DONT_DESTROY(client);
+
+        client->state = DHCP_STATE_RENEWING;
+        client->attempt = 1;
+
+        return client_initialize_time_events(client);
+}
+
+static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
+                               size_t len) {
+        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+        int r;
+
+        r = dhcp_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        if (client->client_id_len) {
+                r = dhcp_lease_set_client_id(lease,
+                                             (uint8_t *) &client->client_id.raw,
+                                             client->client_id_len);
+                if (r < 0)
+                        return r;
+        }
+
+        r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
+        if (r != DHCP_OFFER) {
+                log_dhcp_client(client, "received message was not an OFFER, ignoring");
+                return -ENOMSG;
+        }
+
+        lease->next_server = offer->siaddr;
+
+        lease->address = offer->yiaddr;
+
+        if (lease->address == INADDR_ANY ||
+            lease->server_address == INADDR_ANY ||
+            lease->lifetime == 0) {
+                log_dhcp_client(client, "received lease lacks address, server "
+                                "address or lease lifetime, ignoring");
+                return -ENOMSG;
+        }
+
+        if (lease->subnet_mask == INADDR_ANY) {
+                r = dhcp_lease_set_default_subnet_mask(lease);
+                if (r < 0) {
+                        log_dhcp_client(client, "received lease lacks subnet "
+                                        "mask, and a fallback one can not be "
+                                        "generated, ignoring");
+                        return -ENOMSG;
+                }
+        }
+
+        sd_dhcp_lease_unref(client->lease);
+        client->lease = lease;
+        lease = NULL;
+
+        log_dhcp_client(client, "OFFER");
+
+        return 0;
+}
+
+static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
+                                    size_t len) {
+        int r;
+
+        r = dhcp_option_parse(force, len, NULL, NULL);
+        if (r != DHCP_FORCERENEW)
+                return -ENOMSG;
+
+        log_dhcp_client(client, "FORCERENEW");
+
+        return 0;
+}
+
+static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
+                             size_t len) {
+        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+        int r;
+
+        r = dhcp_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        if (client->client_id_len) {
+                r = dhcp_lease_set_client_id(lease,
+                                             (uint8_t *) &client->client_id.raw,
+                                             client->client_id_len);
+                if (r < 0)
+                        return r;
+        }
+
+        r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
+        if (r == DHCP_NAK) {
+                log_dhcp_client(client, "NAK");
+                return -EADDRNOTAVAIL;
+        }
+
+        if (r != DHCP_ACK) {
+                log_dhcp_client(client, "received message was not an ACK, ignoring");
+                return -ENOMSG;
+        }
+
+        lease->next_server = ack->siaddr;
+
+        lease->address = ack->yiaddr;
+
+        if (lease->address == INADDR_ANY ||
+            lease->server_address == INADDR_ANY ||
+            lease->lifetime == 0) {
+                log_dhcp_client(client, "received lease lacks address, server "
+                                "address or lease lifetime, ignoring");
+                return -ENOMSG;
+        }
+
+        if (lease->subnet_mask == INADDR_ANY) {
+                r = dhcp_lease_set_default_subnet_mask(lease);
+                if (r < 0) {
+                        log_dhcp_client(client, "received lease lacks subnet "
+                                        "mask, and a fallback one can not be "
+                                        "generated, ignoring");
+                        return -ENOMSG;
+                }
+        }
+
+        r = DHCP_EVENT_IP_ACQUIRE;
+        if (client->lease) {
+                if (client->lease->address != lease->address ||
+                    client->lease->subnet_mask != lease->subnet_mask ||
+                    client->lease->router != lease->router) {
+                        r = DHCP_EVENT_IP_CHANGE;
+                } else
+                        r = DHCP_EVENT_RENEW;
+
+                client->lease = sd_dhcp_lease_unref(client->lease);
+        }
+
+        client->lease = lease;
+        lease = NULL;
+
+        log_dhcp_client(client, "ACK");
+
+        return r;
+}
+
+static uint64_t client_compute_timeout(sd_dhcp_client *client,
+                                       uint32_t lifetime, double factor) {
+        assert(client);
+        assert(client->request_sent);
+        assert(lifetime);
+
+        return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
+                + (random_u32() & 0x1fffff);
+}
+
+static int client_set_lease_timeouts(sd_dhcp_client *client) {
+        usec_t time_now;
+        uint64_t lifetime_timeout;
+        uint64_t t2_timeout;
+        uint64_t t1_timeout;
+        char time_string[FORMAT_TIMESPAN_MAX];
+        int r;
+
+        assert(client);
+        assert(client->event);
+        assert(client->lease);
+        assert(client->lease->lifetime);
+
+        client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
+        client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
+        client->timeout_expire = sd_event_source_unref(client->timeout_expire);
+
+        /* don't set timers for infinite leases */
+        if (client->lease->lifetime == 0xffffffff)
+                return 0;
+
+        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                return r;
+        assert(client->request_sent <= time_now);
+
+        /* convert the various timeouts from relative (secs) to absolute (usecs) */
+        lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
+        if (client->lease->t1 && client->lease->t2) {
+                /* both T1 and T2 are given */
+                if (client->lease->t1 < client->lease->t2 &&
+                    client->lease->t2 < client->lease->lifetime) {
+                        /* they are both valid */
+                        t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
+                        t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
+                } else {
+                        /* discard both */
+                        t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
+                        client->lease->t2 = (client->lease->lifetime * 7) / 8;
+                        t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
+                        client->lease->t1 = client->lease->lifetime / 2;
+                }
+        } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
+                /* only T2 is given, and it is valid */
+                t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
+                t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
+                client->lease->t1 = client->lease->lifetime / 2;
+                if (t2_timeout <= t1_timeout) {
+                        /* the computed T1 would be invalid, so discard T2 */
+                        t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
+                        client->lease->t2 = (client->lease->lifetime * 7) / 8;
+                }
+        } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
+                /* only T1 is given, and it is valid */
+                t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
+                t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
+                client->lease->t2 = (client->lease->lifetime * 7) / 8;
+                if (t2_timeout <= t1_timeout) {
+                        /* the computed T2 would be invalid, so discard T1 */
+                        t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
+                        client->lease->t2 = client->lease->lifetime / 2;
+                }
+        } else {
+                /* fall back to the default timeouts */
+                t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
+                client->lease->t1 = client->lease->lifetime / 2;
+                t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
+                client->lease->t2 = (client->lease->lifetime * 7) / 8;
+        }
+
+        /* arm lifetime timeout */
+        r = sd_event_add_time(client->event, &client->timeout_expire,
+                              clock_boottime_or_monotonic(),
+                              lifetime_timeout, 10 * USEC_PER_MSEC,
+                              client_timeout_expire, client);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(client->timeout_expire,
+                                         client->event_priority);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
+        if (r < 0)
+                return r;
+
+        log_dhcp_client(client, "lease expires in %s",
+                        format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                        lifetime_timeout - time_now, 0));
+
+        /* don't arm earlier timeouts if this has already expired */
+        if (lifetime_timeout <= time_now)
+                return 0;
+
+        /* arm T2 timeout */
+        r = sd_event_add_time(client->event,
+                              &client->timeout_t2,
+                              clock_boottime_or_monotonic(),
+                              t2_timeout,
+                              10 * USEC_PER_MSEC,
+                              client_timeout_t2, client);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(client->timeout_t2,
+                                         client->event_priority);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
+        if (r < 0)
+                return r;
+
+        log_dhcp_client(client, "T2 expires in %s",
+                        format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                        t2_timeout - time_now, 0));
+
+        /* don't arm earlier timeout if this has already expired */
+        if (t2_timeout <= time_now)
+                return 0;
+
+        /* arm T1 timeout */
+        r = sd_event_add_time(client->event,
+                              &client->timeout_t1,
+                              clock_boottime_or_monotonic(),
+                              t1_timeout, 10 * USEC_PER_MSEC,
+                              client_timeout_t1, client);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(client->timeout_t1,
+                                         client->event_priority);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
+        if (r < 0)
+                return r;
+
+        log_dhcp_client(client, "T1 expires in %s",
+                        format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                        t1_timeout - time_now, 0));
+
+        return 0;
+}
+
+static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
+                                 int len) {
+        DHCP_CLIENT_DONT_DESTROY(client);
+        int r = 0, notify_event = 0;
+
+        assert(client);
+        assert(client->event);
+        assert(message);
+
+        switch (client->state) {
+        case DHCP_STATE_SELECTING:
+
+                r = client_handle_offer(client, message, len);
+                if (r >= 0) {
+
+                        client->timeout_resend =
+                                sd_event_source_unref(client->timeout_resend);
+
+                        client->state = DHCP_STATE_REQUESTING;
+                        client->attempt = 1;
+
+                        r = sd_event_add_time(client->event,
+                                              &client->timeout_resend,
+                                              clock_boottime_or_monotonic(),
+                                              0, 0,
+                                              client_timeout_resend, client);
+                        if (r < 0)
+                                goto error;
+
+                        r = sd_event_source_set_priority(client->timeout_resend,
+                                                         client->event_priority);
+                        if (r < 0)
+                                goto error;
+
+                        r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
+                        if (r < 0)
+                                goto error;
+                } else if (r == -ENOMSG)
+                        /* invalid message, let's ignore it */
+                        return 0;
+
+                break;
+
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+
+                r = client_handle_ack(client, message, len);
+                if (r >= 0) {
+                        client->timeout_resend =
+                                sd_event_source_unref(client->timeout_resend);
+                        client->receive_message =
+                                sd_event_source_unref(client->receive_message);
+                        client->fd = asynchronous_close(client->fd);
+
+                        if (IN_SET(client->state, DHCP_STATE_REQUESTING,
+                                   DHCP_STATE_REBOOTING))
+                                notify_event = DHCP_EVENT_IP_ACQUIRE;
+                        else if (r != DHCP_EVENT_IP_ACQUIRE)
+                                notify_event = r;
+
+                        client->state = DHCP_STATE_BOUND;
+                        client->attempt = 1;
+
+                        client->last_addr = client->lease->address;
+
+                        r = client_set_lease_timeouts(client);
+                        if (r < 0)
+                                goto error;
+
+                        r = dhcp_network_bind_udp_socket(client->lease->address,
+                                                         DHCP_PORT_CLIENT);
+                        if (r < 0) {
+                                log_dhcp_client(client, "could not bind UDP socket");
+                                goto error;
+                        }
+
+                        client->fd = r;
+
+                        client_initialize_io_events(client, client_receive_message_udp);
+
+                        if (notify_event) {
+                                client_notify(client, notify_event);
+                                if (client->state == DHCP_STATE_STOPPED)
+                                        return 0;
+                        }
+
+                } else if (r == -EADDRNOTAVAIL) {
+                        /* got a NAK, let's restart the client */
+                        client->timeout_resend =
+                                sd_event_source_unref(client->timeout_resend);
+
+                        r = client_initialize(client);
+                        if (r < 0)
+                                goto error;
+
+                        r = client_start(client);
+                        if (r < 0)
+                                goto error;
+
+                        log_dhcp_client(client, "REBOOTED");
+
+                        return 0;
+                } else if (r == -ENOMSG)
+                        /* invalid message, let's ignore it */
+                        return 0;
+
+                break;
+
+        case DHCP_STATE_BOUND:
+                r = client_handle_forcerenew(client, message, len);
+                if (r >= 0) {
+                        r = client_timeout_t1(NULL, 0, client);
+                        if (r < 0)
+                                goto error;
+                } else if (r == -ENOMSG)
+                        /* invalid message, let's ignore it */
+                        return 0;
+
+                break;
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_INIT_REBOOT:
+
+                break;
+
+        case DHCP_STATE_STOPPED:
+                r = -EINVAL;
+                goto error;
+        }
+
+error:
+        if (r < 0)
+                client_stop(client, r);
+
+        return r;
+}
+
+static int client_receive_message_udp(sd_event_source *s, int fd,
+                                      uint32_t revents, void *userdata) {
+        sd_dhcp_client *client = userdata;
+        _cleanup_free_ DHCPMessage *message = NULL;
+        int buflen = 0, len, r;
+        const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
+        const struct ether_addr *expected_chaddr = NULL;
+        uint8_t expected_hlen = 0;
+
+        assert(s);
+        assert(client);
+
+        r = ioctl(fd, FIONREAD, &buflen);
+        if (r < 0)
+                return r;
+
+        if (buflen < 0)
+                /* this can't be right */
+                return -EIO;
+
+        message = malloc0(buflen);
+        if (!message)
+                return -ENOMEM;
+
+        len = read(fd, message, buflen);
+        if (len < 0) {
+                log_dhcp_client(client, "could not receive message from UDP "
+                                "socket: %m");
+                return 0;
+        } else if ((size_t)len < sizeof(DHCPMessage)) {
+                log_dhcp_client(client, "too small to be a DHCP message: ignoring");
+                return 0;
+        }
+
+        if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
+                log_dhcp_client(client, "not a DHCP message: ignoring");
+                return 0;
+        }
+
+        if (message->op != BOOTREPLY) {
+                log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
+                return 0;
+        }
+
+        if (message->htype != client->arp_type) {
+                log_dhcp_client(client, "packet type does not match client type");
+                return 0;
+        }
+
+        if (client->arp_type == ARPHRD_ETHER) {
+                expected_hlen = ETH_ALEN;
+                expected_chaddr = (const struct ether_addr *) &client->mac_addr;
+        } else {
+               /* Non-ethernet links expect zero chaddr */
+               expected_hlen = 0;
+               expected_chaddr = &zero_mac;
+        }
+
+        if (message->hlen != expected_hlen) {
+                log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
+                return 0;
+        }
+
+        if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
+                log_dhcp_client(client, "received chaddr does not match "
+                                "expected: ignoring");
+                return 0;
+        }
+
+        if (client->state != DHCP_STATE_BOUND &&
+            be32toh(message->xid) != client->xid) {
+                /* in BOUND state, we may receive FORCERENEW with xid set by server,
+                   so ignore the xid in this case */
+                log_dhcp_client(client, "received xid (%u) does not match "
+                                "expected (%u): ignoring",
+                                be32toh(message->xid), client->xid);
+                return 0;
+        }
+
+        return client_handle_message(client, message, len);
+}
+
+static int client_receive_message_raw(sd_event_source *s, int fd,
+                                      uint32_t revents, void *userdata) {
+        sd_dhcp_client *client = userdata;
+        _cleanup_free_ DHCPPacket *packet = NULL;
+        uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
+        struct iovec iov = {};
+        struct msghdr msg = {
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_control = cmsgbuf,
+                .msg_controllen = sizeof(cmsgbuf),
+        };
+        struct cmsghdr *cmsg;
+        bool checksum = true;
+        int buflen = 0, len, r;
+
+        assert(s);
+        assert(client);
+
+        r = ioctl(fd, FIONREAD, &buflen);
+        if (r < 0)
+                return r;
+
+        if (buflen < 0)
+                /* this can't be right */
+                return -EIO;
+
+        packet = malloc0(buflen);
+        if (!packet)
+                return -ENOMEM;
+
+        iov.iov_base = packet;
+        iov.iov_len = buflen;
+
+        len = recvmsg(fd, &msg, 0);
+        if (len < 0) {
+                log_dhcp_client(client, "could not receive message from raw "
+                                "socket: %m");
+                return 0;
+        } else if ((size_t)len < sizeof(DHCPPacket))
+                return 0;
+
+        for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                if (cmsg->cmsg_level == SOL_PACKET &&
+                    cmsg->cmsg_type == PACKET_AUXDATA &&
+                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
+                        struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
+
+                        checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
+                        break;
+                }
+        }
+
+        r = dhcp_packet_verify_headers(packet, len, checksum);
+        if (r < 0)
+                return 0;
+
+        len -= DHCP_IP_UDP_SIZE;
+
+        return client_handle_message(client, &packet->dhcp, len);
+}
+
+int sd_dhcp_client_start(sd_dhcp_client *client) {
+        int r;
+
+        assert_return(client, -EINVAL);
+
+        r = client_initialize(client);
+        if (r < 0)
+                return r;
+
+        if (client->last_addr)
+                client->state = DHCP_STATE_INIT_REBOOT;
+
+        r = client_start(client);
+        if (r >= 0)
+                log_dhcp_client(client, "STARTED on ifindex %u", client->index);
+
+        return r;
+}
+
+int sd_dhcp_client_stop(sd_dhcp_client *client) {
+        DHCP_CLIENT_DONT_DESTROY(client);
+
+        assert_return(client, -EINVAL);
+
+        client_stop(client, DHCP_EVENT_STOP);
+        client->state = DHCP_STATE_STOPPED;
+
+        return 0;
+}
+
+int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
+                                int priority) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!client->event, -EBUSY);
+
+        if (event)
+                client->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&client->event);
+                if (r < 0)
+                        return 0;
+        }
+
+        client->event_priority = priority;
+
+        return 0;
+}
+
+int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
+        assert_return(client, -EINVAL);
+
+        client->event = sd_event_unref(client->event);
+
+        return 0;
+}
+
+sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
+        if (!client)
+                return NULL;
+
+        return client->event;
+}
+
+sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
+        if (client)
+                assert_se(REFCNT_INC(client->n_ref) >= 2);
+
+        return client;
+}
+
+sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
+        if (client && REFCNT_DEC(client->n_ref) <= 0) {
+                log_dhcp_client(client, "FREE");
+
+                client_initialize(client);
+
+                client->receive_message =
+                        sd_event_source_unref(client->receive_message);
+
+                sd_dhcp_client_detach_event(client);
+
+                sd_dhcp_lease_unref(client->lease);
+
+                free(client->req_opts);
+                free(client->hostname);
+                free(client->vendor_class_identifier);
+                free(client);
+        }
+
+        return NULL;
+}
+
+int sd_dhcp_client_new(sd_dhcp_client **ret) {
+        _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        client = new0(sd_dhcp_client, 1);
+        if (!client)
+                return -ENOMEM;
+
+        client->n_ref = REFCNT_INIT;
+        client->state = DHCP_STATE_INIT;
+        client->index = -1;
+        client->fd = -1;
+        client->attempt = 1;
+        client->mtu = DHCP_DEFAULT_MIN_SIZE;
+
+        client->req_opts_size = ELEMENTSOF(default_req_opts);
+
+        client->req_opts = memdup(default_req_opts, client->req_opts_size);
+        if (!client->req_opts)
+                return -ENOMEM;
+
+        *ret = client;
+        client = NULL;
+
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c
new file mode 100644
index 00000000..2d13d503
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c
@@ -0,0 +1,865 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+  Copyright (C) 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+
+#include "util.h"
+#include "list.h"
+#if 0 /* NM_IGNORED */
+#include "mkdir.h"
+#endif
+#include "fileio.h"
+#include "unaligned.h"
+#include "in-addr-util.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+#include "dhcp-lease-internal.h"
+#include "sd-dhcp-lease.h"
+#include "sd-dhcp-client.h"
+#include "network-internal.h"
+
+int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        addr->s_addr = lease->address;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
+        assert_return(lease, -EINVAL);
+        assert_return(lease, -EINVAL);
+
+        *lifetime = lease->lifetime;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
+        assert_return(lease, -EINVAL);
+        assert_return(mtu, -EINVAL);
+
+        if (lease->mtu)
+                *mtu = lease->mtu;
+        else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        if (lease->dns_size) {
+                *addr = lease->dns;
+                return lease->dns_size;
+        } else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        if (lease->ntp_size) {
+                *addr = lease->ntp;
+                return lease->ntp_size;
+        } else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
+        assert_return(lease, -EINVAL);
+        assert_return(domainname, -EINVAL);
+
+        if (lease->domainname)
+                *domainname = lease->domainname;
+        else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
+        assert_return(lease, -EINVAL);
+        assert_return(hostname, -EINVAL);
+
+        if (lease->hostname)
+                *hostname = lease->hostname;
+        else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
+        assert_return(lease, -EINVAL);
+        assert_return(root_path, -EINVAL);
+
+        if (lease->root_path)
+                *root_path = lease->root_path;
+        else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        if (lease->router != INADDR_ANY)
+                addr->s_addr = lease->router;
+        else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        addr->s_addr = lease->subnet_mask;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        addr->s_addr = lease->server_address;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        addr->s_addr = lease->next_server;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
+
+        assert_return(lease, -EINVAL);
+        assert_return(routes, -EINVAL);
+
+        if (lease->static_route_size) {
+                *routes = lease->static_route;
+                return lease->static_route_size;
+        } else
+                return -ENOENT;
+
+        return 0;
+}
+
+sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
+        if (lease)
+                assert_se(REFCNT_INC(lease->n_ref) >= 2);
+
+        return lease;
+}
+
+sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
+        if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
+                free(lease->hostname);
+                free(lease->domainname);
+                free(lease->dns);
+                free(lease->ntp);
+                free(lease->static_route);
+                free(lease->client_id);
+                free(lease);
+        }
+
+        return NULL;
+}
+
+static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
+        assert(option);
+        assert(ret);
+
+        if (len == 4) {
+                *ret = unaligned_read_be32((be32_t*) option);
+
+                if (*ret < min)
+                        *ret = min;
+        }
+}
+
+static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
+        lease_parse_u32(option, len, (uint32_t *)ret, 0);
+}
+
+static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
+        assert(option);
+        assert(ret);
+
+        if (len == 2) {
+                *ret = unaligned_read_be16((be16_t*) option);
+
+                if (*ret < min)
+                        *ret = min;
+        }
+}
+
+static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
+        assert(option);
+        assert(ret);
+
+        if (len == 4)
+                memcpy(ret, option, 4);
+}
+
+static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
+        assert(option);
+        assert(ret);
+
+        if (len == 1)
+                *ret = !!(*option);
+}
+
+static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
+        assert(option);
+        assert(ret);
+
+        if (len == 1) {
+                *ret = *option;
+
+                if (*ret < min)
+                        *ret = min;
+        }
+}
+
+static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
+        assert(option);
+        assert(ret);
+
+        if (len >= 1) {
+                char *string;
+
+                string = strndup((const char *)option, len);
+                if (!string)
+                        return -errno;
+
+                free(*ret);
+                *ret = string;
+        }
+
+        return 0;
+}
+
+static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
+        assert(option);
+        assert(ret);
+        assert(ret_size);
+
+        if (len && !(len % (4 * mult))) {
+                size_t size;
+                struct in_addr *addresses;
+
+                size = len / 4;
+
+                addresses = newdup(struct in_addr, option, size);
+                if (!addresses)
+                        return -ENOMEM;
+
+                free(*ret);
+                *ret = addresses;
+                *ret_size = size;
+        }
+
+        return 0;
+}
+
+static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
+        return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
+}
+
+static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
+        return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
+}
+
+static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
+        size_t *routes_size, size_t *routes_allocated) {
+
+        struct in_addr addr;
+
+        assert(option);
+        assert(routes);
+        assert(routes_size);
+        assert(routes_allocated);
+
+        if (!len)
+                return 0;
+
+        if (len % 8 != 0)
+                return -EINVAL;
+
+        if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
+                return -ENOMEM;
+
+        while (len >= 8) {
+                struct sd_dhcp_route *route = *routes + *routes_size;
+                int r;
+
+                r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
+                if (r < 0) {
+                        log_error("Failed to determine destination prefix length from class based IP, ignoring");
+                        continue;
+                }
+
+                lease_parse_be32(option, 4, &addr.s_addr);
+                route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
+                option += 4;
+
+                lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+                option += 4;
+
+                len -= 8;
+                (*routes_size)++;
+        }
+
+        return 0;
+}
+
+/* parses RFC3442 Classless Static Route Option */
+static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
+        size_t *routes_size, size_t *routes_allocated) {
+
+        assert(option);
+        assert(routes);
+        assert(routes_size);
+        assert(routes_allocated);
+
+        /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)*  */
+
+        while (len > 0) {
+                uint8_t dst_octets;
+                struct sd_dhcp_route *route;
+
+                if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
+                    return -ENOMEM;
+
+                route = *routes + *routes_size;
+
+                dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
+                route->dst_prefixlen = *option;
+                option++;
+                len--;
+
+                /* can't have more than 4 octets in IPv4 */
+                if (dst_octets > 4 || len < dst_octets)
+                        return -EINVAL;
+
+                route->dst_addr.s_addr = 0;
+                memcpy(&route->dst_addr.s_addr, option, dst_octets);
+                option += dst_octets;
+                len -= dst_octets;
+
+                if (len < 4)
+                        return -EINVAL;
+
+                lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+                option += 4;
+                len -= 4;
+
+                (*routes_size)++;
+        }
+
+        return 0;
+}
+
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
+                              void *user_data) {
+        sd_dhcp_lease *lease = user_data;
+        int r;
+
+        assert(lease);
+
+        switch(code) {
+
+        case DHCP_OPTION_TIME_OFFSET:
+                lease_parse_s32(option, len, &lease->time_offset);
+
+                break;
+
+        case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
+                lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
+
+                break;
+
+        case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+                lease_parse_u32(option, len, &lease->lifetime, 1);
+
+                break;
+
+        case DHCP_OPTION_SERVER_IDENTIFIER:
+                lease_parse_be32(option, len, &lease->server_address);
+
+                break;
+
+        case DHCP_OPTION_SUBNET_MASK:
+                lease_parse_be32(option, len, &lease->subnet_mask);
+
+                break;
+
+        case DHCP_OPTION_BROADCAST:
+                lease_parse_be32(option, len, &lease->broadcast);
+
+                break;
+
+        case DHCP_OPTION_ROUTER:
+                lease_parse_be32(option, len, &lease->router);
+
+                break;
+
+        case DHCP_OPTION_DOMAIN_NAME_SERVER:
+                r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP_OPTION_NTP_SERVER:
+                r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP_OPTION_POLICY_FILTER:
+                r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP_OPTION_STATIC_ROUTE:
+                r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size,
+                        &lease->static_route_allocated);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP_OPTION_INTERFACE_MTU:
+                lease_parse_u16(option, len, &lease->mtu, 68);
+
+                break;
+
+        case DHCP_OPTION_INTERFACE_MDR:
+                lease_parse_u16(option, len, &lease->mdr, 576);
+
+                break;
+
+        case DHCP_OPTION_INTERFACE_TTL:
+                lease_parse_u8(option, len, &lease->ttl, 1);
+
+                break;
+
+        case DHCP_OPTION_BOOT_FILE_SIZE:
+                lease_parse_u16(option, len, &lease->boot_file_size, 0);
+
+                break;
+
+        case DHCP_OPTION_DOMAIN_NAME:
+        {
+                _cleanup_free_ char *domainname = NULL;
+
+                r = lease_parse_string(option, len, &domainname);
+                if (r < 0)
+                        return r;
+
+                if (!hostname_is_valid(domainname) || is_localhost(domainname))
+                        break;
+
+                free(lease->domainname);
+                lease->domainname = domainname;
+                domainname = NULL;
+
+                break;
+        }
+        case DHCP_OPTION_HOST_NAME:
+        {
+                _cleanup_free_ char *hostname = NULL;
+
+                r = lease_parse_string(option, len, &hostname);
+                if (r < 0)
+                        return r;
+
+                if (!hostname_is_valid(hostname) || is_localhost(hostname))
+                        break;
+
+                free(lease->hostname);
+                lease->hostname = hostname;
+                hostname = NULL;
+
+                break;
+        }
+        case DHCP_OPTION_ROOT_PATH:
+                r = lease_parse_string(option, len, &lease->root_path);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP_OPTION_RENEWAL_T1_TIME:
+                lease_parse_u32(option, len, &lease->t1, 1);
+
+                break;
+
+        case DHCP_OPTION_REBINDING_T2_TIME:
+                lease_parse_u32(option, len, &lease->t2, 1);
+
+                break;
+
+        case DHCP_OPTION_ENABLE_IP_FORWARDING:
+                lease_parse_bool(option, len, &lease->ip_forward);
+
+                break;
+
+        case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
+                lease_parse_bool(option, len, &lease->ip_forward_non_local);
+
+                break;
+
+        case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+                r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size,
+                        &lease->static_route_allocated);
+                if (r < 0)
+                        return r;
+
+                break;
+        }
+
+        return 0;
+}
+
+int dhcp_lease_new(sd_dhcp_lease **ret) {
+        sd_dhcp_lease *lease;
+
+        lease = new0(sd_dhcp_lease, 1);
+        if (!lease)
+                return -ENOMEM;
+
+        lease->router = INADDR_ANY;
+        lease->n_ref = REFCNT_INIT;
+
+        *ret = lease;
+        return 0;
+}
+
+int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        struct in_addr address;
+        const struct in_addr *addresses;
+        const uint8_t *client_id;
+        size_t client_id_len;
+        const char *string;
+        uint16_t mtu;
+        struct sd_dhcp_route *routes;
+        int r;
+
+        assert(lease);
+        assert(lease_file);
+
+        r = fopen_temporary(lease_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
+
+        fchmod(fileno(f), 0644);
+
+        r = sd_dhcp_lease_get_address(lease, &address);
+        if (r < 0)
+                goto finish;
+
+        fprintf(f,
+                "# This is private data. Do not parse.\n"
+                "ADDRESS=%s\n", inet_ntoa(address));
+
+        r = sd_dhcp_lease_get_netmask(lease, &address);
+        if (r < 0)
+                goto finish;
+
+        fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
+
+        r = sd_dhcp_lease_get_router(lease, &address);
+        if (r >= 0)
+                fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
+
+        r = sd_dhcp_lease_get_server_identifier(lease, &address);
+        if (r >= 0)
+                fprintf(f, "SERVER_ADDRESS=%s\n",
+                        inet_ntoa(address));
+
+        r = sd_dhcp_lease_get_next_server(lease, &address);
+        if (r >= 0)
+                fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
+
+        r = sd_dhcp_lease_get_mtu(lease, &mtu);
+        if (r >= 0)
+                fprintf(f, "MTU=%" PRIu16 "\n", mtu);
+
+        fputs("DNS=", f);
+        r = sd_dhcp_lease_get_dns(lease, &addresses);
+        if (r >= 0)
+                serialize_in_addrs(f, addresses, r);
+        fputs("\n", f);
+
+        fputs("NTP=", f);
+        r = sd_dhcp_lease_get_ntp(lease, &addresses);
+        if (r >= 0)
+                serialize_in_addrs(f, addresses, r);
+        fputs("\n", f);
+
+        r = sd_dhcp_lease_get_domainname(lease, &string);
+        if (r >= 0)
+                fprintf(f, "DOMAINNAME=%s\n", string);
+
+        r = sd_dhcp_lease_get_hostname(lease, &string);
+        if (r >= 0)
+                fprintf(f, "HOSTNAME=%s\n", string);
+
+        r = sd_dhcp_lease_get_root_path(lease, &string);
+        if (r >= 0)
+                fprintf(f, "ROOT_PATH=%s\n", string);
+
+        r = sd_dhcp_lease_get_routes(lease, &routes);
+        if (r >= 0)
+                serialize_dhcp_routes(f, "ROUTES", routes, r);
+
+        r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
+        if (r >= 0) {
+                _cleanup_free_ char *client_id_hex = NULL;
+
+                client_id_hex = hexmem (client_id, client_id_len);
+                if (!client_id_hex) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+                fprintf(f, "CLIENTID=%s\n", client_id_hex);
+        }
+
+        r = 0;
+
+        fflush(f);
+
+        if (ferror(f) || rename(temp_path, lease_file) < 0) {
+                r = -errno;
+                unlink(lease_file);
+                unlink(temp_path);
+        }
+
+finish:
+        if (r < 0)
+                log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
+
+        return r;
+}
+
+int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
+        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+        _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
+                            *server_address = NULL, *next_server = NULL,
+                            *dns = NULL, *ntp = NULL, *mtu = NULL,
+                            *routes = NULL, *client_id_hex = NULL;
+        struct in_addr addr;
+        int r;
+
+        assert(lease_file);
+        assert(ret);
+
+        r = dhcp_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        r = parse_env_file(lease_file, NEWLINE,
+                           "ADDRESS", &address,
+                           "ROUTER", &router,
+                           "NETMASK", &netmask,
+                           "SERVER_IDENTIFIER", &server_address,
+                           "NEXT_SERVER", &next_server,
+                           "DNS", &dns,
+                           "NTP", &ntp,
+                           "MTU", &mtu,
+                           "DOMAINNAME", &lease->domainname,
+                           "HOSTNAME", &lease->hostname,
+                           "ROOT_PATH", &lease->root_path,
+                           "ROUTES", &routes,
+                           "CLIENTID", &client_id_hex,
+                           NULL);
+        if (r < 0) {
+                if (r == -ENOENT)
+                        return 0;
+
+                log_error("Failed to read %s: %s", lease_file, strerror(-r));
+                return r;
+        }
+
+        r = inet_pton(AF_INET, address, &addr);
+        if (r < 0)
+                return r;
+
+        lease->address = addr.s_addr;
+
+        if (router) {
+                r = inet_pton(AF_INET, router, &addr);
+                if (r < 0)
+                        return r;
+
+                lease->router = addr.s_addr;
+        }
+
+        r = inet_pton(AF_INET, netmask, &addr);
+        if (r < 0)
+                return r;
+
+        lease->subnet_mask = addr.s_addr;
+
+        if (server_address) {
+                r = inet_pton(AF_INET, server_address, &addr);
+                if (r < 0)
+                        return r;
+
+                lease->server_address = addr.s_addr;
+        }
+
+        if (next_server) {
+                r = inet_pton(AF_INET, next_server, &addr);
+                if (r < 0)
+                        return r;
+
+                lease->next_server = addr.s_addr;
+        }
+
+        if (dns) {
+                r = deserialize_in_addrs(&lease->dns, dns);
+                if (r < 0)
+                        return r;
+
+                lease->dns_size = r;
+        }
+
+        if (ntp) {
+                r = deserialize_in_addrs(&lease->ntp, ntp);
+                if (r < 0)
+                        return r;
+
+                lease->ntp_size = r;
+        }
+
+        if (mtu) {
+                uint16_t u;
+                if (sscanf(mtu, "%" SCNu16, &u) > 0)
+                        lease->mtu = u;
+        }
+
+        if (routes) {
+                r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
+                                &lease->static_route_allocated, routes);
+                if (r < 0)
+                    return r;
+        }
+
+        if (client_id_hex) {
+                if (strlen (client_id_hex) % 2)
+                        return -EINVAL;
+
+                lease->client_id = unhexmem (client_id_hex, strlen (client_id_hex));
+                if (!lease->client_id)
+                        return -ENOMEM;
+                lease->client_id_len = strlen (client_id_hex) / 2;
+        }
+
+        *ret = lease;
+        lease = NULL;
+
+        return 0;
+}
+
+int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
+        struct in_addr address;
+        struct in_addr mask;
+        int r;
+
+        assert(lease);
+
+        address.s_addr = lease->address;
+
+        /* fall back to the default subnet masks based on address class */
+        r = in_addr_default_subnet_mask(&address, &mask);
+        if (r < 0)
+                return r;
+
+        lease->subnet_mask = mask.s_addr;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
+                                size_t *client_id_len) {
+        assert_return(lease, -EINVAL);
+        assert_return(client_id, -EINVAL);
+        assert_return(client_id_len, -EINVAL);
+
+        *client_id = lease->client_id;
+        *client_id_len = lease->client_id_len;
+        return 0;
+}
+
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
+                             size_t client_id_len) {
+        assert_return(lease, -EINVAL);
+        assert_return((!client_id && !client_id_len) ||
+                      (client_id && client_id_len), -EINVAL);
+
+        free (lease->client_id);
+        lease->client_id = NULL;
+        lease->client_id_len = 0;
+
+        if (client_id) {
+                lease->client_id = memdup (client_id, client_id_len);
+                lease->client_id_len = client_id_len;
+        }
+
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-client.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-client.c
new file mode 100644
index 00000000..6c5a6ab5
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-client.c
@@ -0,0 +1,1242 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <linux/if_infiniband.h>
+
+#if 0 /* NM_IGNORED */
+#include "udev.h"
+#include "udev-util.h"
+#include "virt.h"
+#endif
+#include "siphash24.h"
+#include "util.h"
+#include "refcnt.h"
+
+#include "network-internal.h"
+#include "sd-dhcp6-client.h"
+#include "dhcp6-protocol.h"
+#include "dhcp6-internal.h"
+#include "dhcp6-lease-internal.h"
+
+#define SYSTEMD_PEN 43793
+#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+
+/* RFC 3315 section 9.1:
+ *      A DUID can be no more than 128 octets long (not including the type code).
+ */
+#define MAX_DUID_LEN 128
+
+#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
+
+struct sd_dhcp6_client {
+        RefCount n_ref;
+
+        enum DHCP6State state;
+        sd_event *event;
+        int event_priority;
+        int index;
+        uint8_t mac_addr[MAX_MAC_ADDR_LEN];
+        size_t mac_addr_len;
+        uint16_t arp_type;
+        char ifname[IFNAMSIZ];
+        DHCP6IA ia_na;
+        be32_t transaction_id;
+        usec_t transaction_start;
+        struct sd_dhcp6_lease *lease;
+        int fd;
+        be16_t *req_opts;
+        size_t req_opts_allocated;
+        size_t req_opts_len;
+        sd_event_source *receive_message;
+        usec_t retransmit_time;
+        uint8_t retransmit_count;
+        sd_event_source *timeout_resend;
+        sd_event_source *timeout_resend_expire;
+        sd_dhcp6_client_cb_t cb;
+        void *userdata;
+        union {
+                struct {
+                        uint16_t type; /* DHCP6_DUID_LLT */
+                        uint16_t htype;
+                        uint32_t time;
+                        uint8_t haddr[0];
+                } _packed_ llt;
+                struct {
+                        uint16_t type; /* DHCP6_DUID_EN */
+                        uint32_t pen;
+                        uint8_t id[8];
+                } _packed_ en;
+                struct {
+                        uint16_t type; /* DHCP6_DUID_LL */
+                        uint16_t htype;
+                        uint8_t haddr[0];
+                } _packed_ ll;
+                struct {
+                        uint16_t type; /* DHCP6_DUID_UUID */
+                        sd_id128_t uuid;
+                } _packed_ uuid;
+                struct {
+                        uint16_t type;
+                        uint8_t data[MAX_DUID_LEN];
+                } _packed_ raw;
+        } duid;
+        size_t duid_len;
+};
+
+static const uint16_t default_req_opts[] = {
+        DHCP6_OPTION_DNS_SERVERS,
+        DHCP6_OPTION_DOMAIN_LIST,
+        DHCP6_OPTION_NTP_SERVER,
+};
+
+const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
+        [DHCP6_SOLICIT] = "SOLICIT",
+        [DHCP6_ADVERTISE] = "ADVERTISE",
+        [DHCP6_REQUEST] = "REQUEST",
+        [DHCP6_CONFIRM] = "CONFIRM",
+        [DHCP6_RENEW] = "RENEW",
+        [DHCP6_REBIND] = "REBIND",
+        [DHCP6_REPLY] = "REPLY",
+        [DHCP6_RELEASE] = "RELEASE",
+        [DHCP6_DECLINE] = "DECLINE",
+        [DHCP6_RECONFIGURE] = "RECONFIGURE",
+        [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
+        [DHCP6_RELAY_FORW] = "RELAY-FORW",
+        [DHCP6_RELAY_REPL] = "RELAY-REPL",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
+
+const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
+        [DHCP6_STATUS_SUCCESS] = "Success",
+        [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
+        [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
+        [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
+        [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
+        [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
+#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
+
+#define DHCP6_CLIENT_DONT_DESTROY(client) \
+        _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
+
+static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
+
+int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
+                                 sd_dhcp6_client_cb_t cb, void *userdata)
+{
+        assert_return(client, -EINVAL);
+
+        client->cb = cb;
+        client->userdata = userdata;
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
+{
+        assert_return(client, -EINVAL);
+        assert_return(interface_index >= -1, -EINVAL);
+
+        client->index = interface_index;
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
+                            size_t addr_len, uint16_t arp_type)
+{
+        assert_return(client, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
+        assert_return(arp_type > 0, -EINVAL);
+
+        if (arp_type == ARPHRD_ETHER)
+                assert_return(addr_len == ETH_ALEN, -EINVAL);
+        else if (arp_type == ARPHRD_INFINIBAND)
+                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+        else
+                return -EINVAL;
+
+        if (client->mac_addr_len == addr_len &&
+            memcmp(&client->mac_addr, addr, addr_len) == 0)
+                return 0;
+
+        memcpy(&client->mac_addr, addr, addr_len);
+        client->mac_addr_len = addr_len;
+        client->arp_type = arp_type;
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
+                             size_t duid_len)
+{
+        assert_return(client, -EINVAL);
+        assert_return(duid, -EINVAL);
+        assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
+
+        switch (type) {
+        case DHCP6_DUID_LLT:
+                if (duid_len <= sizeof(client->duid.llt) - 2)
+                        return -EINVAL;
+                break;
+        case DHCP6_DUID_EN:
+                if (duid_len != sizeof(client->duid.en) - 2)
+                        return -EINVAL;
+                break;
+        case DHCP6_DUID_LL:
+                if (duid_len <= sizeof(client->duid.ll) - 2)
+                        return -EINVAL;
+                break;
+        case DHCP6_DUID_UUID:
+                if (duid_len != sizeof(client->duid.uuid) - 2)
+                        return -EINVAL;
+                break;
+        default:
+                /* accept unknown type in order to be forward compatible */
+                break;
+        }
+
+        client->duid.raw.type = htobe16(type);
+        memcpy(&client->duid.raw.data, duid, duid_len);
+        client->duid_len = duid_len + 2;  /* +2 for sizeof(type) */
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
+                                       uint16_t option) {
+        size_t t;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+
+        switch(option) {
+        case DHCP6_OPTION_DNS_SERVERS:
+        case DHCP6_OPTION_DOMAIN_LIST:
+        case DHCP6_OPTION_SNTP_SERVERS:
+        case DHCP6_OPTION_NTP_SERVER:
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        for (t = 0; t < client->req_opts_len; t++)
+                if (client->req_opts[t] == htobe16(option))
+                        return -EEXIST;
+
+        if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
+                            client->req_opts_len + 1))
+                return -ENOMEM;
+
+        client->req_opts[client->req_opts_len++] = htobe16(option);
+
+        return 0;
+}
+
+int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
+        assert_return(client, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!client->lease)
+                return -ENOMSG;
+
+        *ret = sd_dhcp6_lease_ref(client->lease);
+
+        return 0;
+}
+
+static void client_notify(sd_dhcp6_client *client, int event) {
+        if (client->cb)
+                client->cb(client, event, client->userdata);
+}
+
+static int client_reset(sd_dhcp6_client *client) {
+        assert_return(client, -EINVAL);
+
+        client->receive_message =
+                sd_event_source_unref(client->receive_message);
+
+        client->fd = safe_close(client->fd);
+
+        client->transaction_id = 0;
+        client->transaction_start = 0;
+
+        client->ia_na.timeout_t1 =
+                sd_event_source_unref(client->ia_na.timeout_t1);
+        client->ia_na.timeout_t2 =
+                sd_event_source_unref(client->ia_na.timeout_t2);
+
+        client->retransmit_time = 0;
+        client->retransmit_count = 0;
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+        client->timeout_resend_expire =
+                sd_event_source_unref(client->timeout_resend_expire);
+
+        client->state = DHCP6_STATE_STOPPED;
+
+        return 0;
+}
+
+static void client_stop(sd_dhcp6_client *client, int error) {
+        DHCP6_CLIENT_DONT_DESTROY(client);
+
+        assert(client);
+
+        client_notify(client, error);
+
+        client_reset(client);
+}
+
+static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
+        _cleanup_free_ DHCP6Message *message = NULL;
+        struct in6_addr all_servers =
+                IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+        size_t len, optlen = 512;
+        uint8_t *opt;
+        int r;
+        usec_t elapsed_usec;
+        be16_t elapsed_time;
+
+        len = sizeof(DHCP6Message) + optlen;
+
+        message = malloc0(len);
+        if (!message)
+                return -ENOMEM;
+
+        opt = (uint8_t *)(message + 1);
+
+        message->transaction_id = client->transaction_id;
+
+        switch(client->state) {
+        case DHCP6_STATE_SOLICITATION:
+                message->type = DHCP6_SOLICIT;
+
+                r = dhcp6_option_append(&opt, &optlen,
+                                        DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+                if (r < 0)
+                        return r;
+
+                r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP6_STATE_REQUEST:
+        case DHCP6_STATE_RENEW:
+
+                if (client->state == DHCP6_STATE_REQUEST)
+                        message->type = DHCP6_REQUEST;
+                else
+                        message->type = DHCP6_RENEW;
+
+                r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
+                                        client->lease->serverid_len,
+                                        client->lease->serverid);
+                if (r < 0)
+                        return r;
+
+                r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP6_STATE_REBIND:
+                message->type = DHCP6_REBIND;
+
+                r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_BOUND:
+                return -EINVAL;
+        }
+
+        r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
+                                client->req_opts_len * sizeof(be16_t),
+                                client->req_opts);
+        if (r < 0)
+                return r;
+
+        r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
+                                client->duid_len, &client->duid);
+        if (r < 0)
+                return r;
+
+        elapsed_usec = time_now - client->transaction_start;
+        if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
+                elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
+        else
+                elapsed_time = 0xffff;
+
+        r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
+                                sizeof(elapsed_time), &elapsed_time);
+        if (r < 0)
+                return r;
+
+        r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
+                                          len - optlen);
+        if (r < 0)
+                return r;
+
+        log_dhcp6_client(client, "Sent %s",
+                         dhcp6_message_type_to_string(message->type));
+
+        return 0;
+}
+
+static int client_timeout_t2(sd_event_source *s, uint64_t usec,
+                             void *userdata) {
+        sd_dhcp6_client *client = userdata;
+
+        assert_return(s, -EINVAL);
+        assert_return(client, -EINVAL);
+        assert_return(client->lease, -EINVAL);
+
+        client->lease->ia.timeout_t2 =
+                sd_event_source_unref(client->lease->ia.timeout_t2);
+
+        log_dhcp6_client(client, "Timeout T2");
+
+        client_start(client, DHCP6_STATE_REBIND);
+
+        return 0;
+}
+
+static int client_timeout_t1(sd_event_source *s, uint64_t usec,
+                             void *userdata) {
+        sd_dhcp6_client *client = userdata;
+
+        assert_return(s, -EINVAL);
+        assert_return(client, -EINVAL);
+        assert_return(client->lease, -EINVAL);
+
+        client->lease->ia.timeout_t1 =
+                sd_event_source_unref(client->lease->ia.timeout_t1);
+
+        log_dhcp6_client(client, "Timeout T1");
+
+        client_start(client, DHCP6_STATE_RENEW);
+
+        return 0;
+}
+
+static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
+                                        void *userdata) {
+        sd_dhcp6_client *client = userdata;
+        DHCP6_CLIENT_DONT_DESTROY(client);
+        enum DHCP6State state;
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        state = client->state;
+
+        client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
+
+        /* RFC 3315, section 18.1.4., says that "...the client may choose to
+           use a Solicit message to locate a new DHCP server..." */
+        if (state == DHCP6_STATE_REBIND)
+                client_start(client, DHCP6_STATE_SOLICITATION);
+
+        return 0;
+}
+
+static usec_t client_timeout_compute_random(usec_t val) {
+        return val - val / 10 +
+                (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
+}
+
+static int client_timeout_resend(sd_event_source *s, uint64_t usec,
+                                 void *userdata) {
+        int r = 0;
+        sd_dhcp6_client *client = userdata;
+        usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
+        usec_t max_retransmit_duration = 0;
+        uint8_t max_retransmit_count = 0;
+        char time_string[FORMAT_TIMESPAN_MAX];
+        uint32_t expire = 0;
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
+        switch (client->state) {
+        case DHCP6_STATE_SOLICITATION:
+
+                if (client->retransmit_count && client->lease) {
+                        client_start(client, DHCP6_STATE_REQUEST);
+                        return 0;
+                }
+
+                init_retransmit_time = DHCP6_SOL_TIMEOUT;
+                max_retransmit_time = DHCP6_SOL_MAX_RT;
+
+                break;
+
+        case DHCP6_STATE_REQUEST:
+                init_retransmit_time = DHCP6_REQ_TIMEOUT;
+                max_retransmit_time = DHCP6_REQ_MAX_RT;
+                max_retransmit_count = DHCP6_REQ_MAX_RC;
+
+                break;
+
+        case DHCP6_STATE_RENEW:
+                init_retransmit_time = DHCP6_REN_TIMEOUT;
+                max_retransmit_time = DHCP6_REN_MAX_RT;
+
+                /* RFC 3315, section 18.1.3. says max retransmit duration will
+                   be the remaining time until T2. Instead of setting MRD,
+                   wait for T2 to trigger with the same end result */
+
+                break;
+
+        case DHCP6_STATE_REBIND:
+                init_retransmit_time = DHCP6_REB_TIMEOUT;
+                max_retransmit_time = DHCP6_REB_MAX_RT;
+
+                if (!client->timeout_resend_expire) {
+                        r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
+                                                         &expire);
+                        if (r < 0) {
+                                client_stop(client, r);
+                                return 0;
+                        }
+                        max_retransmit_duration = expire * USEC_PER_SEC;
+                }
+
+                break;
+
+        case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_BOUND:
+                return 0;
+        }
+
+        if (max_retransmit_count &&
+            client->retransmit_count >= max_retransmit_count) {
+                client_stop(client, DHCP6_EVENT_RETRANS_MAX);
+                return 0;
+        }
+
+        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                goto error;
+
+        r = client_send_message(client, time_now);
+        if (r >= 0)
+                client->retransmit_count++;
+
+        if (!client->retransmit_time) {
+                client->retransmit_time =
+                        client_timeout_compute_random(init_retransmit_time);
+
+                if (client->state == DHCP6_STATE_SOLICITATION)
+                        client->retransmit_time += init_retransmit_time / 10;
+
+        } else {
+                if (max_retransmit_time &&
+                    client->retransmit_time > max_retransmit_time / 2)
+                        client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
+                else
+                        client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
+        }
+
+        log_dhcp6_client(client, "Next retransmission in %s",
+                         format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                                         client->retransmit_time, 0));
+
+        r = sd_event_add_time(client->event, &client->timeout_resend,
+                              clock_boottime_or_monotonic(),
+                              time_now + client->retransmit_time,
+                              10 * USEC_PER_MSEC, client_timeout_resend,
+                              client);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(client->timeout_resend,
+                                         client->event_priority);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
+        if (r < 0)
+                goto error;
+
+        if (max_retransmit_duration && !client->timeout_resend_expire) {
+
+                log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
+                                 max_retransmit_duration / USEC_PER_SEC);
+
+                r = sd_event_add_time(client->event,
+                                      &client->timeout_resend_expire,
+                                      clock_boottime_or_monotonic(),
+                                      time_now + max_retransmit_duration,
+                                      USEC_PER_SEC,
+                                      client_timeout_resend_expire, client);
+                if (r < 0)
+                        goto error;
+
+                r = sd_event_source_set_priority(client->timeout_resend_expire,
+                                                 client->event_priority);
+                if (r < 0)
+                        goto error;
+
+                r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
+                if (r < 0)
+                        goto error;
+        }
+
+error:
+        if (r < 0)
+                client_stop(client, r);
+
+        return 0;
+}
+
+static int client_ensure_iaid(sd_dhcp6_client *client) {
+        const char *name;
+        uint64_t id;
+
+        assert(client);
+
+        if (client->ia_na.id)
+                return 0;
+
+	name = client->ifname;
+        if (name)
+                siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
+        else
+                /* fall back to mac address if no predictable name available */
+                siphash24((uint8_t*)&id, &client->mac_addr,
+                          client->mac_addr_len, HASH_KEY.bytes);
+
+        /* fold into 32 bits */
+        client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
+
+        return 0;
+}
+
+static int client_parse_message(sd_dhcp6_client *client,
+                                DHCP6Message *message, size_t len,
+                                sd_dhcp6_lease *lease) {
+        int r;
+        uint8_t *optval, *option, *id = NULL;
+        uint16_t optcode, status;
+        size_t optlen, id_len;
+        bool clientid = false;
+        be32_t iaid_lease;
+
+        option = (uint8_t *)message + sizeof(DHCP6Message);
+        len -= sizeof(DHCP6Message);
+
+        while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
+                                       &optval)) >= 0) {
+                switch (optcode) {
+                case DHCP6_OPTION_CLIENTID:
+                        if (clientid) {
+                                log_dhcp6_client(client, "%s contains multiple clientids",
+                                                 dhcp6_message_type_to_string(message->type));
+                                return -EINVAL;
+                        }
+
+                        if (optlen != client->duid_len ||
+                            memcmp(&client->duid, optval, optlen) != 0) {
+                                log_dhcp6_client(client, "%s DUID does not match",
+                                                 dhcp6_message_type_to_string(message->type));
+
+                                return -EINVAL;
+                        }
+                        clientid = true;
+
+                        break;
+
+                case DHCP6_OPTION_SERVERID:
+                        r = dhcp6_lease_get_serverid(lease, &id, &id_len);
+                        if (r >= 0 && id) {
+                                log_dhcp6_client(client, "%s contains multiple serverids",
+                                                 dhcp6_message_type_to_string(message->type));
+                                return -EINVAL;
+                        }
+
+                        r = dhcp6_lease_set_serverid(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case DHCP6_OPTION_PREFERENCE:
+                        if (optlen != 1)
+                                return -EINVAL;
+
+                        r = dhcp6_lease_set_preference(lease, *optval);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case DHCP6_OPTION_STATUS_CODE:
+                        if (optlen < 2)
+                                return -EINVAL;
+
+                        status = optval[0] << 8 | optval[1];
+                        if (status) {
+                                log_dhcp6_client(client, "%s Status %s",
+                                                 dhcp6_message_type_to_string(message->type),
+                                                 dhcp6_message_status_to_string(status));
+                                return -EINVAL;
+                        }
+
+                        break;
+
+                case DHCP6_OPTION_IA_NA:
+                        r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
+                                                  &lease->ia);
+                        if (r < 0 && r != -ENOMSG)
+                                return r;
+
+                        r = dhcp6_lease_get_iaid(lease, &iaid_lease);
+                        if (r < 0)
+                                return r;
+
+                        if (client->ia_na.id != iaid_lease) {
+                                log_dhcp6_client(client, "%s has wrong IAID",
+                                                 dhcp6_message_type_to_string(message->type));
+                                return -EINVAL;
+                        }
+
+                        break;
+
+                case DHCP6_OPTION_RAPID_COMMIT:
+                        r = dhcp6_lease_set_rapid_commit(lease);
+                        if (r < 0)
+                                return r;
+
+                        break;
+                }
+        }
+
+        if ((r < 0 && r != -ENOMSG) || !clientid) {
+                log_dhcp6_client(client, "%s has incomplete options",
+                                 dhcp6_message_type_to_string(message->type));
+                return -EINVAL;
+        }
+
+        r = dhcp6_lease_get_serverid(lease, &id, &id_len);
+        if (r < 0)
+                log_dhcp6_client(client, "%s has no server id",
+                                 dhcp6_message_type_to_string(message->type));
+
+        return r;
+}
+
+static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
+                                size_t len)
+{
+        int r;
+        _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+        bool rapid_commit;
+
+        if (reply->type != DHCP6_REPLY)
+                return 0;
+
+        r = dhcp6_lease_new(&lease);
+        if (r < 0)
+                return -ENOMEM;
+
+        r = client_parse_message(client, reply, len, lease);
+        if (r < 0)
+                return r;
+
+        if (client->state == DHCP6_STATE_SOLICITATION) {
+                r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
+                if (r < 0)
+                        return r;
+
+                if (!rapid_commit)
+                        return 0;
+        }
+
+        if (client->lease)
+                dhcp6_lease_clear_timers(&client->lease->ia);
+
+        client->lease = sd_dhcp6_lease_unref(client->lease);
+        client->lease = lease;
+        lease = NULL;
+
+        return DHCP6_STATE_BOUND;
+}
+
+static int client_receive_advertise(sd_dhcp6_client *client,
+                                    DHCP6Message *advertise, size_t len) {
+        int r;
+        _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+        uint8_t pref_advertise = 0, pref_lease = 0;
+
+        if (advertise->type != DHCP6_ADVERTISE)
+                return 0;
+
+        r = dhcp6_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        r = client_parse_message(client, advertise, len, lease);
+        if (r < 0)
+                return r;
+
+        r = dhcp6_lease_get_preference(lease, &pref_advertise);
+        if (r < 0)
+                return r;
+
+        r = dhcp6_lease_get_preference(client->lease, &pref_lease);
+        if (!client->lease || r < 0 || pref_advertise > pref_lease) {
+                sd_dhcp6_lease_unref(client->lease);
+                client->lease = lease;
+                lease = NULL;
+                r = 0;
+        }
+
+        if (pref_advertise == 255 || client->retransmit_count > 1)
+                r = DHCP6_STATE_REQUEST;
+
+        return r;
+}
+
+static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
+                                  void *userdata) {
+        sd_dhcp6_client *client = userdata;
+        DHCP6_CLIENT_DONT_DESTROY(client);
+        _cleanup_free_ DHCP6Message *message = NULL;
+        int r, buflen, len;
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        r = ioctl(fd, FIONREAD, &buflen);
+        if (r < 0 || buflen <= 0)
+                buflen = DHCP6_MIN_OPTIONS_SIZE;
+
+        message = malloc0(buflen);
+        if (!message)
+                return -ENOMEM;
+
+        len = read(fd, message, buflen);
+        if ((size_t)len < sizeof(DHCP6Message)) {
+                log_dhcp6_client(client, "could not receive message from UDP socket: %m");
+                return 0;
+        }
+
+        switch(message->type) {
+        case DHCP6_SOLICIT:
+        case DHCP6_REQUEST:
+        case DHCP6_CONFIRM:
+        case DHCP6_RENEW:
+        case DHCP6_REBIND:
+        case DHCP6_RELEASE:
+        case DHCP6_DECLINE:
+        case DHCP6_INFORMATION_REQUEST:
+        case DHCP6_RELAY_FORW:
+        case DHCP6_RELAY_REPL:
+                return 0;
+
+        case DHCP6_ADVERTISE:
+        case DHCP6_REPLY:
+        case DHCP6_RECONFIGURE:
+                break;
+
+        default:
+                log_dhcp6_client(client, "unknown message type %d",
+                                 message->type);
+                return 0;
+        }
+
+        if (client->transaction_id != (message->transaction_id &
+                                       htobe32(0x00ffffff)))
+                return 0;
+
+        switch (client->state) {
+        case DHCP6_STATE_SOLICITATION:
+                r = client_receive_advertise(client, message, len);
+
+                if (r == DHCP6_STATE_REQUEST) {
+                        client_start(client, r);
+
+                        break;
+                }
+
+                /* fall through for Soliciation Rapid Commit option check */
+        case DHCP6_STATE_REQUEST:
+        case DHCP6_STATE_RENEW:
+        case DHCP6_STATE_REBIND:
+
+                r = client_receive_reply(client, message, len);
+                if (r < 0)
+                        return 0;
+
+                if (r == DHCP6_STATE_BOUND) {
+
+                        r = client_start(client, DHCP6_STATE_BOUND);
+                        if (r < 0) {
+                                client_stop(client, r);
+                                return 0;
+                        }
+
+                        client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
+                }
+
+                break;
+
+        case DHCP6_STATE_BOUND:
+
+                break;
+
+        case DHCP6_STATE_STOPPED:
+                return 0;
+        }
+
+        if (r >= 0) {
+                log_dhcp6_client(client, "Recv %s",
+                                 dhcp6_message_type_to_string(message->type));
+        }
+
+        return 0;
+}
+
+static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
+{
+        int r;
+        usec_t timeout, time_now;
+        char time_string[FORMAT_TIMESPAN_MAX];
+
+        assert_return(client, -EINVAL);
+        assert_return(client->event, -EINVAL);
+        assert_return(client->index > 0, -EINVAL);
+        assert_return(client->state != state, -EINVAL);
+
+        client->timeout_resend_expire =
+                sd_event_source_unref(client->timeout_resend_expire);
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+        client->retransmit_time = 0;
+        client->retransmit_count = 0;
+
+        if (client->state == DHCP6_STATE_STOPPED) {
+                time_now = now(clock_boottime_or_monotonic());
+        } else {
+                r = sd_event_now(client->event, clock_boottime_or_monotonic(),
+                                 &time_now);
+                if (r < 0)
+                        return r;
+        }
+
+        switch (state) {
+        case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_SOLICITATION:
+
+                r = client_ensure_iaid(client);
+                if (r < 0)
+                        return r;
+
+                r = dhcp6_network_bind_udp_socket(client->index, NULL);
+                if (r < 0)
+                        return r;
+
+                client->fd = r;
+
+                r = sd_event_add_io(client->event, &client->receive_message,
+                                    client->fd, EPOLLIN, client_receive_message,
+                                    client);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_priority(client->receive_message,
+                                                 client->event_priority);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_description(client->receive_message, "dhcp6-receive-message");
+                if (r < 0)
+                        return r;
+
+                client->state = DHCP6_STATE_SOLICITATION;
+
+                break;
+
+        case DHCP6_STATE_REQUEST:
+        case DHCP6_STATE_RENEW:
+        case DHCP6_STATE_REBIND:
+
+                client->state = state;
+
+                break;
+
+        case DHCP6_STATE_BOUND:
+
+                if (client->lease->ia.lifetime_t1 == 0xffffffff ||
+                    client->lease->ia.lifetime_t2 == 0xffffffff) {
+
+                        log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
+                                         be32toh(client->lease->ia.lifetime_t1),
+                                         be32toh(client->lease->ia.lifetime_t2));
+
+                        return 0;
+                }
+
+                timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
+
+                log_dhcp6_client(client, "T1 expires in %s",
+                                 format_timespan(time_string,
+                                                 FORMAT_TIMESPAN_MAX,
+                                                 timeout, 0));
+
+                r = sd_event_add_time(client->event,
+                                      &client->lease->ia.timeout_t1,
+                                      clock_boottime_or_monotonic(), time_now + timeout,
+                                      10 * USEC_PER_SEC, client_timeout_t1,
+                                      client);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
+                                                 client->event_priority);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
+                if (r < 0)
+                        return r;
+
+                timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
+
+                log_dhcp6_client(client, "T2 expires in %s",
+                                 format_timespan(time_string,
+                                                 FORMAT_TIMESPAN_MAX,
+                                                 timeout, 0));
+
+                r = sd_event_add_time(client->event,
+                                      &client->lease->ia.timeout_t2,
+                                      clock_boottime_or_monotonic(), time_now + timeout,
+                                      10 * USEC_PER_SEC, client_timeout_t2,
+                                      client);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
+                                                 client->event_priority);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
+                if (r < 0)
+                        return r;
+
+                client->state = state;
+
+                return 0;
+        }
+
+        client->transaction_id = random_u32() & htobe32(0x00ffffff);
+        client->transaction_start = time_now;
+
+        r = sd_event_add_time(client->event, &client->timeout_resend,
+                              clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
+                              client);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(client->timeout_resend,
+                                         client->event_priority);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int sd_dhcp6_client_stop(sd_dhcp6_client *client)
+{
+        client_stop(client, DHCP6_EVENT_STOP);
+
+        return 0;
+}
+
+int sd_dhcp6_client_start(sd_dhcp6_client *client)
+{
+        int r = 0;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->event, -EINVAL);
+        assert_return(client->index > 0, -EINVAL);
+
+        r = client_reset(client);
+        if (r < 0)
+                return r;
+
+        return client_start(client, DHCP6_STATE_SOLICITATION);
+}
+
+int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
+                                 int priority)
+{
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!client->event, -EBUSY);
+
+        if (event)
+                client->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&client->event);
+                if (r < 0)
+                        return 0;
+        }
+
+        client->event_priority = priority;
+
+        return 0;
+}
+
+int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
+        assert_return(client, -EINVAL);
+
+        client->event = sd_event_unref(client->event);
+
+        return 0;
+}
+
+sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
+        if (!client)
+                return NULL;
+
+        return client->event;
+}
+
+sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
+        if (client)
+                assert_se(REFCNT_INC(client->n_ref) >= 2);
+
+        return client;
+}
+
+sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
+        if (client && REFCNT_DEC(client->n_ref) <= 0) {
+                client_reset(client);
+
+                sd_dhcp6_client_detach_event(client);
+
+                free(client->req_opts);
+                free(client);
+
+                return NULL;
+        }
+
+        return client;
+}
+
+int sd_dhcp6_client_new(sd_dhcp6_client **ret)
+{
+        _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
+#if 0 /* NM_IGNORED */
+        sd_id128_t machine_id;
+        int r;
+#endif
+        size_t t;
+
+        assert_return(ret, -EINVAL);
+
+        client = new0(sd_dhcp6_client, 1);
+        if (!client)
+                return -ENOMEM;
+
+        client->n_ref = REFCNT_INIT;
+
+        client->ia_na.type = DHCP6_OPTION_IA_NA;
+
+        client->index = -1;
+
+        client->fd = -1;
+
+#if 0 /* NM_IGNORED */
+        /* initialize DUID */
+        client->duid.en.type = htobe16(DHCP6_DUID_EN);
+        client->duid.en.pen = htobe32(SYSTEMD_PEN);
+        client->duid_len = sizeof(client->duid.en);
+
+        r = sd_id128_get_machine(&machine_id);
+        if (r < 0)
+                return r;
+
+        /* a bit of snake-oil perhaps, but no need to expose the machine-id
+           directly */
+        siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
+#endif
+
+        client->req_opts_len = ELEMENTSOF(default_req_opts);
+
+        client->req_opts = new0(be16_t, client->req_opts_len);
+        if (!client->req_opts)
+                return -ENOMEM;
+
+        for (t = 0; t < client->req_opts_len; t++)
+                client->req_opts[t] = htobe16(default_req_opts[t]);
+
+        *ret = client;
+        client = NULL;
+
+        return 0;
+}
+
+/*******************************************/
+/* NetworkManager additions */
+
+int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname)
+{
+        assert_return(client, -EINVAL);
+        assert_return(ifname, -EINVAL);
+        assert_return(strlen (ifname) < sizeof (client->ifname), -EINVAL);
+
+        strcpy(client->ifname, ifname);
+        return 0;
+}
+
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-lease.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-lease.c
new file mode 100644
index 00000000..0df6984d
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-lease.c
@@ -0,0 +1,223 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <errno.h>
+
+#include "util.h"
+
+#include "dhcp6-lease-internal.h"
+
+int dhcp6_lease_clear_timers(DHCP6IA *ia) {
+        assert_return(ia, -EINVAL);
+
+        ia->timeout_t1 = sd_event_source_unref(ia->timeout_t1);
+        ia->timeout_t2 = sd_event_source_unref(ia->timeout_t2);
+
+        return 0;
+}
+
+int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
+        DHCP6Address *addr;
+        uint32_t valid = 0, t;
+
+        assert_return(ia, -EINVAL);
+        assert_return(expire, -EINVAL);
+
+        LIST_FOREACH(addresses, addr, ia->addresses) {
+                t = be32toh(addr->iaaddr.lifetime_valid);
+                if (valid < t)
+                        valid = t;
+        }
+
+        t = be32toh(ia->lifetime_t2);
+        if (t > valid)
+                return -EINVAL;
+
+        *expire = valid - t;
+
+        return 0;
+}
+
+DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
+        DHCP6Address *address;
+
+        if (!ia)
+                return NULL;
+
+        dhcp6_lease_clear_timers(ia);
+
+        while (ia->addresses) {
+                address = ia->addresses;
+
+                LIST_REMOVE(addresses, ia->addresses, address);
+
+                free(address);
+        }
+
+        return NULL;
+}
+
+int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
+                             size_t len) {
+        assert_return(lease, -EINVAL);
+        assert_return(id, -EINVAL);
+
+        free(lease->serverid);
+
+        lease->serverid = memdup(id, len);
+        if (!lease->serverid)
+                return -EINVAL;
+
+        lease->serverid_len = len;
+
+        return 0;
+}
+
+int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) {
+        assert_return(lease, -EINVAL);
+        assert_return(id, -EINVAL);
+        assert_return(len, -EINVAL);
+
+        *id = lease->serverid;
+        *len = lease->serverid_len;
+
+        return 0;
+}
+
+int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
+        assert_return(lease, -EINVAL);
+
+        lease->preference = preference;
+
+        return 0;
+}
+
+int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) {
+        assert_return(lease, -EINVAL);
+        assert_return(preference, -EINVAL);
+
+        *preference = lease->preference;
+
+        return 0;
+}
+
+int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
+        assert_return(lease, -EINVAL);
+
+        lease->rapid_commit = true;
+
+        return 0;
+}
+
+int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) {
+        assert_return(lease, -EINVAL);
+        assert_return(rapid_commit, -EINVAL);
+
+        *rapid_commit = lease->rapid_commit;
+
+        return 0;
+}
+
+int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
+        assert_return(lease, -EINVAL);
+        assert_return(iaid, -EINVAL);
+
+        *iaid = lease->ia.id;
+
+        return 0;
+}
+
+int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease,
+                                    struct in6_addr *addr,
+                                    uint32_t *lifetime_preferred,
+                                    uint32_t *lifetime_valid) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(lifetime_preferred, -EINVAL);
+        assert_return(lifetime_valid, -EINVAL);
+
+        if (!lease->addr_iter)
+                return -ENOMSG;
+
+        memcpy(addr, &lease->addr_iter->iaaddr.address,
+                sizeof(struct in6_addr));
+        *lifetime_preferred =
+                be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
+        *lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
+
+        lease->addr_iter = lease->addr_iter->addresses_next;
+
+        return 0;
+}
+
+int sd_dhcp6_lease_get_first_address(sd_dhcp6_lease *lease,
+                                     struct in6_addr *addr,
+                                     uint32_t *lifetime_preferred,
+                                     uint32_t *lifetime_valid) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(lifetime_preferred, -EINVAL);
+        assert_return(lifetime_valid, -EINVAL);
+
+        if (!lease->ia.addresses)
+                return -ENOMSG;
+
+        lease->addr_iter = lease->ia.addresses;
+
+        return sd_dhcp6_lease_get_next_address(lease, addr, lifetime_preferred,
+                                               lifetime_valid);
+}
+
+sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
+        if (lease)
+                assert_se(REFCNT_INC(lease->n_ref) >= 2);
+
+        return lease;
+}
+
+sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
+        if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
+                free(lease->serverid);
+                dhcp6_lease_free_ia(&lease->ia);
+
+                free(lease);
+        }
+
+        return NULL;
+}
+
+int dhcp6_lease_new(sd_dhcp6_lease **ret) {
+        sd_dhcp6_lease *lease;
+
+        lease = new0(sd_dhcp6_lease, 1);
+        if (!lease)
+                return -ENOMEM;
+
+        lease->n_ref = REFCNT_INIT;
+
+        LIST_HEAD_INIT(lease->ia.addresses);
+
+        *ret = lease;
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/async.h b/src/dhcp-manager/systemd-dhcp/src/shared/async.h
new file mode 100644
index 00000000..7f1ef795
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/async.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int asynchronous_job(void* (*func)(void *p), void *arg);
+
+int asynchronous_sync(void);
+int asynchronous_close(int fd);
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/fileio.c b/src/dhcp-manager/systemd-dhcp/src/shared/fileio.c
new file mode 100644
index 00000000..0757d5d9
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/fileio.c
@@ -0,0 +1,817 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include <unistd.h>
+
+#include "util.h"
+#include "strv.h"
+#include "utf8.h"
+#include "ctype.h"
+#include "fileio.h"
+
+int write_string_stream(FILE *f, const char *line) {
+        assert(f);
+        assert(line);
+
+        errno = 0;
+
+        fputs(line, f);
+        if (!endswith(line, "\n"))
+                fputc('\n', f);
+
+        fflush(f);
+
+        if (ferror(f))
+                return errno ? -errno : -EIO;
+
+        return 0;
+}
+
+int write_string_file(const char *fn, const char *line) {
+        _cleanup_fclose_ FILE *f = NULL;
+
+        assert(fn);
+        assert(line);
+
+        f = fopen(fn, "we");
+        if (!f)
+                return -errno;
+
+        return write_string_stream(f, line);
+}
+
+int write_string_file_no_create(const char *fn, const char *line) {
+        _cleanup_fclose_ FILE *f = NULL;
+        int fd;
+
+        assert(fn);
+        assert(line);
+
+        /* We manually build our own version of fopen(..., "we") that
+         * works without O_CREAT */
+        fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return -errno;
+
+        f = fdopen(fd, "we");
+        if (!f) {
+                safe_close(fd);
+                return -errno;
+        }
+
+        return write_string_stream(f, line);
+}
+
+int write_string_file_atomic(const char *fn, const char *line) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        assert(fn);
+        assert(line);
+
+        r = fopen_temporary(fn, &f, &p);
+        if (r < 0)
+                return r;
+
+        fchmod_umask(fileno(f), 0644);
+
+        r = write_string_stream(f, line);
+        if (r >= 0) {
+                if (rename(p, fn) < 0)
+                        r = -errno;
+        }
+
+        if (r < 0)
+                unlink(p);
+
+        return r;
+}
+
+int read_one_line_file(const char *fn, char **line) {
+        _cleanup_fclose_ FILE *f = NULL;
+        char t[LINE_MAX], *c;
+
+        assert(fn);
+        assert(line);
+
+        f = fopen(fn, "re");
+        if (!f)
+                return -errno;
+
+        if (!fgets(t, sizeof(t), f)) {
+
+                if (ferror(f))
+                        return errno ? -errno : -EIO;
+
+                t[0] = 0;
+        }
+
+        c = strdup(t);
+        if (!c)
+                return -ENOMEM;
+        truncate_nl(c);
+
+        *line = c;
+        return 0;
+}
+
+int read_full_stream(FILE *f, char **contents, size_t *size) {
+        size_t n, l;
+        _cleanup_free_ char *buf = NULL;
+        struct stat st;
+
+        assert(f);
+        assert(contents);
+
+        if (fstat(fileno(f), &st) < 0)
+                return -errno;
+
+        n = LINE_MAX;
+
+        if (S_ISREG(st.st_mode)) {
+
+                /* Safety check */
+                if (st.st_size > 4*1024*1024)
+                        return -E2BIG;
+
+                /* Start with the right file size, but be prepared for
+                 * files from /proc which generally report a file size
+                 * of 0 */
+                if (st.st_size > 0)
+                        n = st.st_size;
+        }
+
+        l = 0;
+        for (;;) {
+                char *t;
+                size_t k;
+
+                t = realloc(buf, n+1);
+                if (!t)
+                        return -ENOMEM;
+
+                buf = t;
+                k = fread(buf + l, 1, n - l, f);
+
+                if (k <= 0) {
+                        if (ferror(f))
+                                return -errno;
+
+                        break;
+                }
+
+                l += k;
+                n *= 2;
+
+                /* Safety check */
+                if (n > 4*1024*1024)
+                        return -E2BIG;
+        }
+
+        buf[l] = 0;
+        *contents = buf;
+        buf = NULL; /* do not free */
+
+        if (size)
+                *size = l;
+
+        return 0;
+}
+
+int read_full_file(const char *fn, char **contents, size_t *size) {
+        _cleanup_fclose_ FILE *f = NULL;
+
+        assert(fn);
+        assert(contents);
+
+        f = fopen(fn, "re");
+        if (!f)
+                return -errno;
+
+        return read_full_stream(f, contents, size);
+}
+
+static int parse_env_file_internal(
+                FILE *f,
+                const char *fname,
+                const char *newline,
+                int (*push) (const char *filename, unsigned line,
+                             const char *key, char *value, void *userdata, int *n_pushed),
+                void *userdata,
+                int *n_pushed) {
+
+        _cleanup_free_ char *contents = NULL, *key = NULL;
+        size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
+        char *p, *value = NULL;
+        int r;
+        unsigned line = 1;
+
+        enum {
+                PRE_KEY,
+                KEY,
+                PRE_VALUE,
+                VALUE,
+                VALUE_ESCAPE,
+                SINGLE_QUOTE_VALUE,
+                SINGLE_QUOTE_VALUE_ESCAPE,
+                DOUBLE_QUOTE_VALUE,
+                DOUBLE_QUOTE_VALUE_ESCAPE,
+                COMMENT,
+                COMMENT_ESCAPE
+        } state = PRE_KEY;
+
+        assert(newline);
+
+        if (f)
+                r = read_full_stream(f, &contents, NULL);
+        else
+                r = read_full_file(fname, &contents, NULL);
+        if (r < 0)
+                return r;
+
+        for (p = contents; *p; p++) {
+                char c = *p;
+
+                switch (state) {
+
+                case PRE_KEY:
+                        if (strchr(COMMENTS, c))
+                                state = COMMENT;
+                        else if (!strchr(WHITESPACE, c)) {
+                                state = KEY;
+                                last_key_whitespace = (size_t) -1;
+
+                                if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                key[n_key++] = c;
+                        }
+                        break;
+
+                case KEY:
+                        if (strchr(newline, c)) {
+                                state = PRE_KEY;
+                                line ++;
+                                n_key = 0;
+                        } else if (c == '=') {
+                                state = PRE_VALUE;
+                                last_value_whitespace = (size_t) -1;
+                        } else {
+                                if (!strchr(WHITESPACE, c))
+                                        last_key_whitespace = (size_t) -1;
+                                else if (last_key_whitespace == (size_t) -1)
+                                         last_key_whitespace = n_key;
+
+                                if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                key[n_key++] = c;
+                        }
+
+                        break;
+
+                case PRE_VALUE:
+                        if (strchr(newline, c)) {
+                                state = PRE_KEY;
+                                line ++;
+                                key[n_key] = 0;
+
+                                if (value)
+                                        value[n_value] = 0;
+
+                                /* strip trailing whitespace from key */
+                                if (last_key_whitespace != (size_t) -1)
+                                        key[last_key_whitespace] = 0;
+
+                                r = push(fname, line, key, value, userdata, n_pushed);
+                                if (r < 0)
+                                        goto fail;
+
+                                n_key = 0;
+                                value = NULL;
+                                value_alloc = n_value = 0;
+
+                        } else if (c == '\'')
+                                state = SINGLE_QUOTE_VALUE;
+                        else if (c == '\"')
+                                state = DOUBLE_QUOTE_VALUE;
+                        else if (c == '\\')
+                                state = VALUE_ESCAPE;
+                        else if (!strchr(WHITESPACE, c)) {
+                                state = VALUE;
+
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                value[n_value++] = c;
+                        }
+
+                        break;
+
+                case VALUE:
+                        if (strchr(newline, c)) {
+                                state = PRE_KEY;
+                                line ++;
+
+                                key[n_key] = 0;
+
+                                if (value)
+                                        value[n_value] = 0;
+
+                                /* Chomp off trailing whitespace from value */
+                                if (last_value_whitespace != (size_t) -1)
+                                        value[last_value_whitespace] = 0;
+
+                                /* strip trailing whitespace from key */
+                                if (last_key_whitespace != (size_t) -1)
+                                        key[last_key_whitespace] = 0;
+
+                                r = push(fname, line, key, value, userdata, n_pushed);
+                                if (r < 0)
+                                        goto fail;
+
+                                n_key = 0;
+                                value = NULL;
+                                value_alloc = n_value = 0;
+
+                        } else if (c == '\\') {
+                                state = VALUE_ESCAPE;
+                                last_value_whitespace = (size_t) -1;
+                        } else {
+                                if (!strchr(WHITESPACE, c))
+                                        last_value_whitespace = (size_t) -1;
+                                else if (last_value_whitespace == (size_t) -1)
+                                        last_value_whitespace = n_value;
+
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                value[n_value++] = c;
+                        }
+
+                        break;
+
+                case VALUE_ESCAPE:
+                        state = VALUE;
+
+                        if (!strchr(newline, c)) {
+                                /* Escaped newlines we eat up entirely */
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                value[n_value++] = c;
+                        }
+                        break;
+
+                case SINGLE_QUOTE_VALUE:
+                        if (c == '\'')
+                                state = PRE_VALUE;
+                        else if (c == '\\')
+                                state = SINGLE_QUOTE_VALUE_ESCAPE;
+                        else {
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                value[n_value++] = c;
+                        }
+
+                        break;
+
+                case SINGLE_QUOTE_VALUE_ESCAPE:
+                        state = SINGLE_QUOTE_VALUE;
+
+                        if (!strchr(newline, c)) {
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                value[n_value++] = c;
+                        }
+                        break;
+
+                case DOUBLE_QUOTE_VALUE:
+                        if (c == '\"')
+                                state = PRE_VALUE;
+                        else if (c == '\\')
+                                state = DOUBLE_QUOTE_VALUE_ESCAPE;
+                        else {
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                value[n_value++] = c;
+                        }
+
+                        break;
+
+                case DOUBLE_QUOTE_VALUE_ESCAPE:
+                        state = DOUBLE_QUOTE_VALUE;
+
+                        if (!strchr(newline, c)) {
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                value[n_value++] = c;
+                        }
+                        break;
+
+                case COMMENT:
+                        if (c == '\\')
+                                state = COMMENT_ESCAPE;
+                        else if (strchr(newline, c)) {
+                                state = PRE_KEY;
+                                line ++;
+                        }
+                        break;
+
+                case COMMENT_ESCAPE:
+                        state = COMMENT;
+                        break;
+                }
+        }
+
+        if (state == PRE_VALUE ||
+            state == VALUE ||
+            state == VALUE_ESCAPE ||
+            state == SINGLE_QUOTE_VALUE ||
+            state == SINGLE_QUOTE_VALUE_ESCAPE ||
+            state == DOUBLE_QUOTE_VALUE ||
+            state == DOUBLE_QUOTE_VALUE_ESCAPE) {
+
+                key[n_key] = 0;
+
+                if (value)
+                        value[n_value] = 0;
+
+                if (state == VALUE)
+                        if (last_value_whitespace != (size_t) -1)
+                                value[last_value_whitespace] = 0;
+
+                /* strip trailing whitespace from key */
+                if (last_key_whitespace != (size_t) -1)
+                        key[last_key_whitespace] = 0;
+
+                r = push(fname, line, key, value, userdata, n_pushed);
+                if (r < 0)
+                        goto fail;
+        }
+
+        return 0;
+
+fail:
+        free(value);
+        return r;
+}
+
+static int parse_env_file_push(
+                const char *filename, unsigned line,
+                const char *key, char *value,
+                void *userdata,
+                int *n_pushed) {
+
+        const char *k;
+        va_list aq, *ap = userdata;
+
+        if (!utf8_is_valid(key)) {
+                _cleanup_free_ char *p = utf8_escape_invalid(key);
+
+                log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
+                return -EINVAL;
+        }
+
+        if (value && !utf8_is_valid(value)) {
+                _cleanup_free_ char *p = utf8_escape_invalid(value);
+
+                log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
+                return -EINVAL;
+        }
+
+        va_copy(aq, *ap);
+
+        while ((k = va_arg(aq, const char *))) {
+                char **v;
+
+                v = va_arg(aq, char **);
+
+                if (streq(key, k)) {
+                        va_end(aq);
+                        free(*v);
+                        *v = value;
+
+                        if (n_pushed)
+                                (*n_pushed)++;
+
+                        return 1;
+                }
+        }
+
+        va_end(aq);
+        free(value);
+
+        return 0;
+}
+
+int parse_env_file(
+                const char *fname,
+                const char *newline, ...) {
+
+        va_list ap;
+        int r, n_pushed = 0;
+
+        if (!newline)
+                newline = NEWLINE;
+
+        va_start(ap, newline);
+        r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
+        va_end(ap);
+
+        return r < 0 ? r : n_pushed;
+}
+
+static int load_env_file_push(
+                const char *filename, unsigned line,
+                const char *key, char *value,
+                void *userdata,
+                int *n_pushed) {
+        char ***m = userdata;
+        char *p;
+        int r;
+
+        if (!utf8_is_valid(key)) {
+                _cleanup_free_ char *t = utf8_escape_invalid(key);
+
+                log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
+                return -EINVAL;
+        }
+
+        if (value && !utf8_is_valid(value)) {
+                _cleanup_free_ char *t = utf8_escape_invalid(value);
+
+                log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
+                return -EINVAL;
+        }
+
+        p = strjoin(key, "=", strempty(value), NULL);
+        if (!p)
+                return -ENOMEM;
+
+        r = strv_consume(m, p);
+        if (r < 0)
+                return r;
+
+        if (n_pushed)
+                (*n_pushed)++;
+
+        free(value);
+        return 0;
+}
+
+int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
+        char **m = NULL;
+        int r;
+
+        if (!newline)
+                newline = NEWLINE;
+
+        r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
+        if (r < 0) {
+                strv_free(m);
+                return r;
+        }
+
+        *rl = m;
+        return 0;
+}
+
+static int load_env_file_push_pairs(
+                const char *filename, unsigned line,
+                const char *key, char *value,
+                void *userdata,
+                int *n_pushed) {
+        char ***m = userdata;
+        int r;
+
+        if (!utf8_is_valid(key)) {
+                _cleanup_free_ char *t = utf8_escape_invalid(key);
+
+                log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
+                return -EINVAL;
+        }
+
+        if (value && !utf8_is_valid(value)) {
+                _cleanup_free_ char *t = utf8_escape_invalid(value);
+
+                log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
+                return -EINVAL;
+        }
+
+        r = strv_extend(m, key);
+        if (r < 0)
+                return -ENOMEM;
+
+        if (!value) {
+                r = strv_extend(m, "");
+                if (r < 0)
+                        return -ENOMEM;
+        } else {
+                r = strv_push(m, value);
+                if (r < 0)
+                        return r;
+        }
+
+        if (n_pushed)
+                (*n_pushed)++;
+
+        return 0;
+}
+
+int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
+        char **m = NULL;
+        int r;
+
+        if (!newline)
+                newline = NEWLINE;
+
+        r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
+        if (r < 0) {
+                strv_free(m);
+                return r;
+        }
+
+        *rl = m;
+        return 0;
+}
+
+static void write_env_var(FILE *f, const char *v) {
+        const char *p;
+
+        p = strchr(v, '=');
+        if (!p) {
+                /* Fallback */
+                fputs(v, f);
+                fputc('\n', f);
+                return;
+        }
+
+        p++;
+        fwrite(v, 1, p-v, f);
+
+        if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
+                fputc('\"', f);
+
+                for (; *p; p++) {
+                        if (strchr(SHELL_NEED_ESCAPE, *p))
+                                fputc('\\', f);
+
+                        fputc(*p, f);
+                }
+
+                fputc('\"', f);
+        } else
+                fputs(p, f);
+
+        fputc('\n', f);
+}
+
+int write_env_file(const char *fname, char **l) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *p = NULL;
+        char **i;
+        int r;
+
+        assert(fname);
+
+        r = fopen_temporary(fname, &f, &p);
+        if (r < 0)
+                return r;
+
+        fchmod_umask(fileno(f), 0644);
+
+        STRV_FOREACH(i, l)
+                write_env_var(f, *i);
+
+        r = fflush_and_check(f);
+        if (r >= 0) {
+                if (rename(p, fname) >= 0)
+                        return 0;
+
+                r = -errno;
+        }
+
+        unlink(p);
+        return r;
+}
+
+int executable_is_script(const char *path, char **interpreter) {
+        int r;
+        _cleanup_free_ char *line = NULL;
+        int len;
+        char *ans;
+
+        assert(path);
+
+        r = read_one_line_file(path, &line);
+        if (r < 0)
+                return r;
+
+        if (!startswith(line, "#!"))
+                return 0;
+
+        ans = strstrip(line + 2);
+        len = strcspn(ans, " \t");
+
+        if (len == 0)
+                return 0;
+
+        ans = strndup(ans, len);
+        if (!ans)
+                return -ENOMEM;
+
+        *interpreter = ans;
+        return 1;
+}
+
+/**
+ * Retrieve one field from a file like /proc/self/status.  pattern
+ * should start with '\n' and end with a ':'. Whitespace and zeros
+ * after the ':' will be skipped. field must be freed afterwards.
+ */
+int get_status_field(const char *filename, const char *pattern, char **field) {
+        _cleanup_free_ char *status = NULL;
+        char *t;
+        size_t len;
+        int r;
+
+        assert(filename);
+        assert(pattern);
+        assert(field);
+
+        r = read_full_file(filename, &status, NULL);
+        if (r < 0)
+                return r;
+
+        t = strstr(status, pattern);
+        if (!t)
+                return -ENOENT;
+
+        t += strlen(pattern);
+        if (*t) {
+                t += strspn(t, " \t");
+
+                /* Also skip zeros, because when this is used for
+                 * capabilities, we don't want the zeros. This way the
+                 * same capability set always maps to the same string,
+                 * irrespective of the total capability set size. For
+                 * other numbers it shouldn't matter. */
+                t += strspn(t, "0");
+                /* Back off one char if there's nothing but whitespace
+                   and zeros */
+                if (!*t || isspace(*t))
+                        t --;
+        }
+
+        len = strcspn(t, WHITESPACE);
+
+        *field = strndup(t, len);
+        if (!*field)
+                return -ENOMEM;
+
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/fileio.h b/src/dhcp-manager/systemd-dhcp/src/shared/fileio.h
new file mode 100644
index 00000000..5ae51c1e
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/fileio.h
@@ -0,0 +1,45 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+#include <stddef.h>
+#include <stdio.h>
+
+#include "macro.h"
+
+int write_string_stream(FILE *f, const char *line);
+int write_string_file(const char *fn, const char *line);
+int write_string_file_no_create(const char *fn, const char *line);
+int write_string_file_atomic(const char *fn, const char *line);
+
+int read_one_line_file(const char *fn, char **line);
+int read_full_file(const char *fn, char **contents, size_t *size);
+int read_full_stream(FILE *f, char **contents, size_t *size);
+
+int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
+int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
+int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
+
+int write_env_file(const char *fname, char **l);
+
+int executable_is_script(const char *path, char **interpreter);
+
+int get_status_field(const char *filename, const char *pattern, char **field);
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.c b/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.c
new file mode 100644
index 00000000..27edeb8d
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.c
@@ -0,0 +1,294 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include <arpa/inet.h>
+
+#include "in-addr-util.h"
+
+int in_addr_is_null(int family, const union in_addr_union *u) {
+        assert(u);
+
+        if (family == AF_INET)
+                return u->in.s_addr == 0;
+
+        if (family == AF_INET6)
+                return
+                        u->in6.s6_addr32[0] == 0 &&
+                        u->in6.s6_addr32[1] == 0 &&
+                        u->in6.s6_addr32[2] == 0 &&
+                        u->in6.s6_addr32[3] == 0;
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_is_link_local(int family, const union in_addr_union *u) {
+        assert(u);
+
+        if (family == AF_INET)
+                return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
+
+        if (family == AF_INET6)
+                return IN6_IS_ADDR_LINKLOCAL(&u->in6);
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
+        assert(a);
+        assert(b);
+
+        if (family == AF_INET)
+                return a->in.s_addr == b->in.s_addr;
+
+        if (family == AF_INET6)
+                return
+                        a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
+                        a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
+                        a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
+                        a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_prefix_intersect(
+                int family,
+                const union in_addr_union *a,
+                unsigned aprefixlen,
+                const union in_addr_union *b,
+                unsigned bprefixlen) {
+
+        unsigned m;
+
+        assert(a);
+        assert(b);
+
+        /* Checks whether there are any addresses that are in both
+         * networks */
+
+        m = MIN(aprefixlen, bprefixlen);
+
+        if (family == AF_INET) {
+                uint32_t x, nm;
+
+                x = be32toh(a->in.s_addr ^ b->in.s_addr);
+                nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
+
+                return (x & nm) == 0;
+        }
+
+        if (family == AF_INET6) {
+                unsigned i;
+
+                if (m > 128)
+                        m = 128;
+
+                for (i = 0; i < 16; i++) {
+                        uint8_t x, nm;
+
+                        x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
+
+                        if (m < 8)
+                                nm = 0xFF << (8 - m);
+                        else
+                                nm = 0xFF;
+
+                        if ((x & nm) != 0)
+                                return 0;
+
+                        if (m > 8)
+                                m -= 8;
+                        else
+                                m = 0;
+                }
+
+                return 1;
+        }
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
+        assert(u);
+
+        /* Increases the network part of an address by one. Returns
+         * positive it that succeeds, or 0 if this overflows. */
+
+        if (prefixlen <= 0)
+                return 0;
+
+        if (family == AF_INET) {
+                uint32_t c, n;
+
+                if (prefixlen > 32)
+                        prefixlen = 32;
+
+                c = be32toh(u->in.s_addr);
+                n = c + (1UL << (32 - prefixlen));
+                if (n < c)
+                        return 0;
+                n &= 0xFFFFFFFFUL << (32 - prefixlen);
+
+                u->in.s_addr = htobe32(n);
+                return 1;
+        }
+
+        if (family == AF_INET6) {
+                struct in6_addr add = {}, result;
+                uint8_t overflow = 0;
+                unsigned i;
+
+                if (prefixlen > 128)
+                        prefixlen = 128;
+
+                /* First calculate what we have to add */
+                add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
+
+                for (i = 16; i > 0; i--) {
+                        unsigned j = i - 1;
+
+                        result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
+                        overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
+                }
+
+                if (overflow)
+                        return 0;
+
+                u->in6 = result;
+                return 1;
+        }
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
+        char *x;
+        size_t l;
+
+        assert(u);
+        assert(ret);
+
+        if (family == AF_INET)
+                l = INET_ADDRSTRLEN;
+        else if (family == AF_INET6)
+                l = INET6_ADDRSTRLEN;
+        else
+                return -EAFNOSUPPORT;
+
+        x = new(char, l);
+        if (!x)
+                return -ENOMEM;
+
+        errno = 0;
+        if (!inet_ntop(family, u, x, l)) {
+                free(x);
+                return errno ? -errno : -EINVAL;
+        }
+
+        *ret = x;
+        return 0;
+}
+
+int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
+
+        assert(s);
+        assert(ret);
+
+        if (!IN_SET(family, AF_INET, AF_INET6))
+                return -EAFNOSUPPORT;
+
+        errno = 0;
+        if (inet_pton(family, s, ret) <= 0)
+                return errno ? -errno : -EINVAL;
+
+        return 0;
+}
+
+int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
+        int r;
+
+        assert(s);
+        assert(family);
+        assert(ret);
+
+        r = in_addr_from_string(AF_INET, s, ret);
+        if (r >= 0) {
+                *family = AF_INET;
+                return 0;
+        }
+
+        r = in_addr_from_string(AF_INET6, s, ret);
+        if (r >= 0) {
+                *family = AF_INET6;
+                return 0;
+        }
+
+        return -EINVAL;
+}
+
+unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
+        assert(addr);
+
+        return 32 - u32ctz(be32toh(addr->s_addr));
+}
+
+int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
+        uint8_t msb_octet = *(uint8_t*) addr;
+
+        /* addr may not be aligned, so make sure we only access it byte-wise */
+
+        assert(addr);
+        assert(prefixlen);
+
+        if (msb_octet < 128)
+                /* class A, leading bits: 0 */
+                *prefixlen = 8;
+        else if (msb_octet < 192)
+                /* class B, leading bits 10 */
+                *prefixlen = 16;
+        else if (msb_octet < 224)
+                /* class C, leading bits 110 */
+                *prefixlen = 24;
+        else
+                /* class D or E, no default prefixlen */
+                return -ERANGE;
+
+        return 0;
+}
+
+int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
+        unsigned char prefixlen;
+        int r;
+
+        assert(addr);
+        assert(mask);
+
+        r = in_addr_default_prefixlen(addr, &prefixlen);
+        if (r < 0)
+                return r;
+
+        assert(prefixlen > 0 && prefixlen < 32);
+
+        mask->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
+
+        return 0;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h b/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h
new file mode 100644
index 00000000..8da030ce
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h
@@ -0,0 +1,49 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+
+#include "macro.h"
+#include "util.h"
+
+union in_addr_union {
+        struct in_addr in;
+        struct in6_addr in6;
+};
+
+int in_addr_is_null(int family, const union in_addr_union *u);
+int in_addr_is_link_local(int family, const union in_addr_union *u);
+int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
+int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
+int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
+int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
+int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
+int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret);
+unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr);
+int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
+int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
+
+static inline size_t FAMILY_ADDRESS_SIZE(int family) {
+        assert(family == AF_INET || family == AF_INET6);
+        return family == AF_INET6 ? 16 : 4;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/list.h b/src/dhcp-manager/systemd-dhcp/src/shared/list.h
new file mode 100644
index 00000000..c020f7e9
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/list.h
@@ -0,0 +1,138 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define LIST_HEAD(t,name)                                               \
+        t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define LIST_FIELDS(t,name)                                             \
+        t *name##_next, *name##_prev
+
+/* Initialize the list's head */
+#define LIST_HEAD_INIT(head)                                            \
+        do {                                                            \
+                (head) = NULL; }                                        \
+        while(false)
+
+/* Initialize a list item */
+#define LIST_INIT(name,item)                                            \
+        do {                                                            \
+                typeof(*(item)) *_item = (item);                        \
+                assert(_item);                                          \
+                _item->name##_prev = _item->name##_next = NULL;         \
+        } while(false)
+
+/* Prepend an item to the list */
+#define LIST_PREPEND(name,head,item)                                    \
+        do {                                                            \
+                typeof(*(head)) **_head = &(head), *_item = (item);     \
+                assert(_item);                                          \
+                if ((_item->name##_next = *_head))                      \
+                        _item->name##_next->name##_prev = _item;        \
+                _item->name##_prev = NULL;                              \
+                *_head = _item;                                         \
+        } while(false)
+
+/* Remove an item from the list */
+#define LIST_REMOVE(name,head,item)                                     \
+        do {                                                            \
+                typeof(*(head)) **_head = &(head), *_item = (item);     \
+                assert(_item);                                          \
+                if (_item->name##_next)                                 \
+                        _item->name##_next->name##_prev = _item->name##_prev; \
+                if (_item->name##_prev)                                 \
+                        _item->name##_prev->name##_next = _item->name##_next; \
+                else {                                                  \
+                        assert(*_head == _item);                        \
+                        *_head = _item->name##_next;                    \
+                }                                                       \
+                _item->name##_next = _item->name##_prev = NULL;         \
+        } while(false)
+
+/* Find the head of the list */
+#define LIST_FIND_HEAD(name,item,head)                                  \
+        do {                                                            \
+                typeof(*(item)) *_item = (item);                        \
+                if (!_item)                                             \
+                        (head) = NULL;                                  \
+                else {                                                  \
+                        while (_item->name##_prev)                      \
+                                _item = _item->name##_prev;             \
+                        (head) = _item;                                 \
+                }                                                       \
+        } while (false)
+
+/* Find the tail of the list */
+#define LIST_FIND_TAIL(name,item,tail)                                  \
+        do {                                                            \
+                typeof(*(item)) *_item = (item);                        \
+                if (!_item)                                             \
+                        (tail) = NULL;                                  \
+                else {                                                  \
+                        while (_item->name##_next)                      \
+                                _item = _item->name##_next;             \
+                        (tail) = _item;                                 \
+                }                                                       \
+        } while (false)
+
+/* Insert an item after another one (a = where, b = what) */
+#define LIST_INSERT_AFTER(name,head,a,b)                                \
+        do {                                                            \
+                typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
+                assert(_b);                                             \
+                if (!_a) {                                              \
+                        if ((_b->name##_next = *_head))                 \
+                                _b->name##_next->name##_prev = _b;      \
+                        _b->name##_prev = NULL;                         \
+                        *_head = _b;                                    \
+                } else {                                                \
+                        if ((_b->name##_next = _a->name##_next))        \
+                                _b->name##_next->name##_prev = _b;      \
+                        _b->name##_prev = _a;                           \
+                        _a->name##_next = _b;                           \
+                }                                                       \
+        } while(false)
+
+#define LIST_JUST_US(name,item)                                         \
+        (!(item)->name##_prev && !(item)->name##_next)                  \
+
+#define LIST_FOREACH(name,i,head)                                       \
+        for ((i) = (head); (i); (i) = (i)->name##_next)
+
+#define LIST_FOREACH_SAFE(name,i,n,head)                                \
+        for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
+
+#define LIST_FOREACH_BEFORE(name,i,p)                                   \
+        for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
+
+#define LIST_FOREACH_AFTER(name,i,p)                                    \
+        for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
+
+/* Loop starting from p->next until p->prev.
+   p can be adjusted meanwhile. */
+#define LIST_LOOP_BUT_ONE(name,i,head,p)                                \
+        for ((i) = (p)->name##_next ? (p)->name##_next : (head);        \
+             (i) != (p);                                                \
+             (i) = (i)->name##_next ? (i)->name##_next : (head))
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/macro.h b/src/dhcp-manager/systemd-dhcp/src/shared/macro.h
new file mode 100644
index 00000000..e6cf6eec
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/macro.h
@@ -0,0 +1,413 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <inttypes.h>
+
+#define _printf_(a,b) __attribute__ ((format (printf, a, b)))
+#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
+#define _sentinel_ __attribute__ ((sentinel))
+#define _unused_ __attribute__ ((unused))
+#define _destructor_ __attribute__ ((destructor))
+#define _pure_ __attribute__ ((pure))
+#define _const_ __attribute__ ((const))
+#define _deprecated_ __attribute__ ((deprecated))
+#define _packed_ __attribute__ ((packed))
+#define _malloc_ __attribute__ ((malloc))
+#define _weak_ __attribute__ ((weak))
+#define _likely_(x) (__builtin_expect(!!(x),1))
+#define _unlikely_(x) (__builtin_expect(!!(x),0))
+#define _public_ __attribute__ ((visibility("default")))
+#define _hidden_ __attribute__ ((visibility("hidden")))
+#define _weakref_(x) __attribute__((weakref(#x)))
+#define _alignas_(x) __attribute__((aligned(__alignof(x))))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+
+/* Temporarily disable some warnings */
+#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT                     \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"")
+
+#define DISABLE_WARNING_FORMAT_NONLITERAL                               \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
+
+#define DISABLE_WARNING_MISSING_PROTOTYPES                              \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+
+#define DISABLE_WARNING_NONNULL                                         \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wnonnull\"")
+
+#define DISABLE_WARNING_SHADOW                                          \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wshadow\"")
+
+#define REENABLE_WARNING                                                \
+        _Pragma("GCC diagnostic pop")
+
+/* automake test harness */
+#define EXIT_TEST_SKIP 77
+
+#define XSTRINGIFY(x) #x
+#define STRINGIFY(x) XSTRINGIFY(x)
+
+#define XCONCATENATE(x, y) x ## y
+#define CONCATENATE(x, y) XCONCATENATE(x, y)
+
+#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
+#define UNIQ __COUNTER__
+
+/* Rounds up */
+
+#define ALIGN4(l) (((l) + 3) & ~3)
+#define ALIGN8(l) (((l) + 7) & ~7)
+
+#if __SIZEOF_POINTER__ == 8
+#define ALIGN(l) ALIGN8(l)
+#elif __SIZEOF_POINTER__ == 4
+#define ALIGN(l) ALIGN4(l)
+#else
+#error "Wut? Pointers are neither 4 nor 8 bytes long?"
+#endif
+
+#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) p))
+
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+        return ((l + ali - 1) & ~(ali - 1));
+}
+
+#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p, ali))
+
+/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
+static inline unsigned long ALIGN_POWER2(unsigned long u) {
+        /* clz(0) is undefined */
+        if (u == 1)
+                return 1;
+
+        /* left-shift overflow is undefined */
+        if (__builtin_clzl(u - 1UL) < 1)
+                return 0;
+
+        return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
+}
+
+#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+/*
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ */
+#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member)
+#define __container_of(uniq, ptr, type, member)                         \
+        __extension__ ({                                                \
+                const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \
+                (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \
+        })
+
+#undef MAX
+#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
+#define __MAX(aq, a, bq, b)                             \
+        __extension__ ({                                \
+                const typeof(a) UNIQ_T(A, aq) = (a);    \
+                const typeof(b) UNIQ_T(B, bq) = (b);    \
+                UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
+        })
+
+/* evaluates to (void) if _A or _B are not constant or of different types */
+#define CONST_MAX(_A, _B) \
+        __extension__ (__builtin_choose_expr(                           \
+                __builtin_constant_p(_A) &&                             \
+                __builtin_constant_p(_B) &&                             \
+                __builtin_types_compatible_p(typeof(_A), typeof(_B)),   \
+                ((_A) > (_B)) ? (_A) : (_B),                            \
+                (void)0))
+
+/* takes two types and returns the size of the larger one */
+#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
+
+#define MAX3(x,y,z)                                     \
+        __extension__ ({                                \
+                        const typeof(x) _c = MAX(x,y);  \
+                        MAX(_c, z);                     \
+                })
+
+#undef MIN
+#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
+#define __MIN(aq, a, bq, b)                             \
+        __extension__ ({                                \
+                const typeof(a) UNIQ_T(A, aq) = (a);    \
+                const typeof(b) UNIQ_T(B, bq) = (b);    \
+                UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
+        })
+
+#define MIN3(x,y,z)                                     \
+        __extension__ ({                                \
+                        const typeof(x) _c = MIN(x,y);  \
+                        MIN(_c, z);                     \
+                })
+
+#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
+#define __LESS_BY(aq, a, bq, b)                         \
+        __extension__ ({                                \
+                const typeof(a) UNIQ_T(A, aq) = (a);    \
+                const typeof(b) UNIQ_T(B, bq) = (b);    \
+                UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \
+        })
+
+#undef CLAMP
+#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
+#define __CLAMP(xq, x, lowq, low, highq, high)                          \
+        __extension__ ({                                                \
+                const typeof(x) UNIQ_T(X,xq) = (x);                     \
+                const typeof(low) UNIQ_T(LOW,lowq) = (low);             \
+                const typeof(high) UNIQ_T(HIGH,highq) = (high);         \
+                        UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ?             \
+                                UNIQ_T(HIGH,highq) :                    \
+                                UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ?       \
+                                        UNIQ_T(LOW,lowq) :              \
+                                        UNIQ_T(X,xq);                   \
+        })
+
+#define assert_se(expr)                                                 \
+        do {                                                            \
+                if (_unlikely_(!(expr)))                                \
+                        log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+        } while (false)                                                 \
+
+/* We override the glibc assert() here. */
+#undef assert
+#ifdef NDEBUG
+#define assert(expr) do {} while(false)
+#else
+#define assert(expr) assert_se(expr)
+#endif
+
+#define assert_not_reached(t)                                           \
+        do {                                                            \
+                log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+        } while (false)
+
+#if defined(static_assert)
+/* static_assert() is sometimes defined in a way that trips up
+ * -Wdeclaration-after-statement, hence let's temporarily turn off
+ * this warning around it. */
+#define assert_cc(expr)                                                 \
+        DISABLE_WARNING_DECLARATION_AFTER_STATEMENT;                    \
+        static_assert(expr, #expr);                                     \
+        REENABLE_WARNING
+#else
+#define assert_cc(expr)                                                 \
+        DISABLE_WARNING_DECLARATION_AFTER_STATEMENT;                    \
+        struct CONCATENATE(_assert_struct_, __LINE__) {                 \
+                char x[(expr) ? 0 : -1];                                \
+        };                                                              \
+        REENABLE_WARNING
+#endif
+
+#define assert_return(expr, r)                                          \
+        do {                                                            \
+                if (_unlikely_(!(expr))) {                              \
+                        log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+                        return (r);                                     \
+                }                                                       \
+        } while (false)
+
+#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_LONG(p) ((long) ((intptr_t) (p)))
+#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p)))
+#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p)))
+#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
+#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p)))
+#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p)))
+#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
+#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define memzero(x,l) (memset((x), 0, (l)))
+#define zero(x) (memzero(&(x), sizeof(x)))
+
+#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
+
+#define char_array_0(x) x[sizeof(x)-1] = 0;
+
+#define IOVEC_SET_STRING(i, s)                  \
+        do {                                    \
+                struct iovec *_i = &(i);        \
+                char *_s = (char *)(s);         \
+                _i->iov_base = _s;              \
+                _i->iov_len = strlen(_s);       \
+        } while(false)
+
+static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
+        unsigned j;
+        size_t r = 0;
+
+        for (j = 0; j < n; j++)
+                r += i[j].iov_len;
+
+        return r;
+}
+
+static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
+        unsigned j;
+
+        for (j = 0; j < n; j++) {
+                size_t sub;
+
+                if (_unlikely_(k <= 0))
+                        break;
+
+                sub = MIN(i[j].iov_len, k);
+                i[j].iov_len -= sub;
+                i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
+                k -= sub;
+        }
+
+        return k;
+}
+
+#define VA_FORMAT_ADVANCE(format, ap)                                   \
+do {                                                                    \
+        int _argtypes[128];                                             \
+        size_t _i, _k;                                                  \
+        _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
+        assert(_k < ELEMENTSOF(_argtypes));                             \
+        for (_i = 0; _i < _k; _i++) {                                   \
+                if (_argtypes[_i] & PA_FLAG_PTR)  {                     \
+                        (void) va_arg(ap, void*);                       \
+                        continue;                                       \
+                }                                                       \
+                                                                        \
+                switch (_argtypes[_i]) {                                \
+                case PA_INT:                                            \
+                case PA_INT|PA_FLAG_SHORT:                              \
+                case PA_CHAR:                                           \
+                        (void) va_arg(ap, int);                         \
+                        break;                                          \
+                case PA_INT|PA_FLAG_LONG:                               \
+                        (void) va_arg(ap, long int);                    \
+                        break;                                          \
+                case PA_INT|PA_FLAG_LONG_LONG:                          \
+                        (void) va_arg(ap, long long int);               \
+                        break;                                          \
+                case PA_WCHAR:                                          \
+                        (void) va_arg(ap, wchar_t);                     \
+                        break;                                          \
+                case PA_WSTRING:                                        \
+                case PA_STRING:                                         \
+                case PA_POINTER:                                        \
+                        (void) va_arg(ap, void*);                       \
+                        break;                                          \
+                case PA_FLOAT:                                          \
+                case PA_DOUBLE:                                         \
+                        (void) va_arg(ap, double);                      \
+                        break;                                          \
+                case PA_DOUBLE|PA_FLAG_LONG_DOUBLE:                     \
+                        (void) va_arg(ap, long double);                 \
+                        break;                                          \
+                default:                                                \
+                        assert_not_reached("Unknown format string argument."); \
+                }                                                       \
+        }                                                               \
+} while(false)
+
+ /* Because statfs.t_type can be int on some architectures, we have to cast
+  * the const magic to the type, otherwise the compiler warns about
+  * signed/unsigned comparison, because the magic can be 32 bit unsigned.
+ */
+#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
+
+/* Returns the number of chars needed to format variables of the
+ * specified type as a decimal string. Adds in extra space for a
+ * negative '-' prefix. */
+#define DECIMAL_STR_MAX(type)                                           \
+        (2+(sizeof(type) <= 1 ? 3 :                                     \
+            sizeof(type) <= 2 ? 5 :                                     \
+            sizeof(type) <= 4 ? 10 :                                    \
+            sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
+
+#define SET_FLAG(v, flag, b) \
+        (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
+
+#define IN_SET(x, y, ...)                                               \
+        ({                                                              \
+                const typeof(y) _y = (y);                               \
+                typeof(_y) _x = (x);                                    \
+                unsigned _i;                                            \
+                bool _found = false;                                    \
+                for (_i = 0; _i < 1 + sizeof((typeof(_x)[]) { __VA_ARGS__ })/sizeof(typeof(_x)); _i++) \
+                        if (((typeof(_x)[]) { _y, __VA_ARGS__ })[_i] == _x) { \
+                                _found = true;                          \
+                                break;                                  \
+                        }                                               \
+                _found;                                                 \
+        })
+
+#if 0 /* NM_IGNORED */
+/* Define C11 thread_local attribute even on older gcc compiler
+ * version */
+#ifndef thread_local
+/*
+ * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
+ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
+ */
+#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
+#define thread_local _Thread_local
+#else
+#define thread_local __thread
+#endif
+#endif
+
+/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
+ * compiler versions */
+#ifndef noreturn
+#if __STDC_VERSION__ >= 201112L
+#define noreturn _Noreturn
+#else
+#define noreturn __attribute__((noreturn))
+#endif
+#endif
+
+#include "log.h"
+#endif
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/refcnt.h b/src/dhcp-manager/systemd-dhcp/src/shared/refcnt.h
new file mode 100644
index 00000000..0502c20a
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/refcnt.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* A type-safe atomic refcounter */
+
+typedef struct {
+        volatile unsigned _value;
+} RefCount;
+
+#define REFCNT_GET(r) ((r)._value)
+#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1))
+#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1))
+
+#define REFCNT_INIT ((RefCount) { ._value = 1 })
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/siphash24.c b/src/dhcp-manager/systemd-dhcp/src/shared/siphash24.c
new file mode 100644
index 00000000..f9e59c6f
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/siphash24.c
@@ -0,0 +1,137 @@
+/*
+   SipHash reference C implementation
+
+   Written in 2012 by
+   Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
+   Daniel J. Bernstein <djb@cr.yp.to>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+   (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd)
+*/
+#include "config.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "siphash24.h"
+
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint8_t u8;
+
+#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
+
+#define U32TO8_LE(p, v)         \
+    (p)[0] = (u8)((v)      ); (p)[1] = (u8)((v) >>  8); \
+    (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
+
+#define U64TO8_LE(p, v)         \
+  U32TO8_LE((p),     (u32)((v)      ));   \
+  U32TO8_LE((p) + 4, (u32)((v) >> 32));
+
+#define U8TO64_LE(p) \
+  (((u64)((p)[0])      ) | \
+   ((u64)((p)[1]) <<  8) | \
+   ((u64)((p)[2]) << 16) | \
+   ((u64)((p)[3]) << 24) | \
+   ((u64)((p)[4]) << 32) | \
+   ((u64)((p)[5]) << 40) | \
+   ((u64)((p)[6]) << 48) | \
+   ((u64)((p)[7]) << 56))
+
+#define SIPROUND            \
+  do {              \
+    v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
+    v2 += v3; v3=ROTL(v3,16); v3 ^= v2;     \
+    v0 += v3; v3=ROTL(v3,21); v3 ^= v0;     \
+    v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
+  } while(0)
+
+/* SipHash-2-4 */
+void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16])
+{
+  /* "somepseudorandomlygeneratedbytes" */
+  u64 v0 = 0x736f6d6570736575ULL;
+  u64 v1 = 0x646f72616e646f6dULL;
+  u64 v2 = 0x6c7967656e657261ULL;
+  u64 v3 = 0x7465646279746573ULL;
+  u64 b;
+  u64 k0 = U8TO64_LE( k );
+  u64 k1 = U8TO64_LE( k + 8 );
+  u64 m;
+  const u8 *in = _in;
+  const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
+  const int left = inlen & 7;
+  b = ( ( u64 )inlen ) << 56;
+  v3 ^= k1;
+  v2 ^= k0;
+  v1 ^= k1;
+  v0 ^= k0;
+
+  for ( ; in != end; in += 8 )
+  {
+    m = U8TO64_LE( in );
+#ifdef DEBUG
+    printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+    printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+    printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+    printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+    printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
+#endif
+    v3 ^= m;
+    SIPROUND;
+    SIPROUND;
+    v0 ^= m;
+  }
+
+  switch( left )
+  {
+  case 7: b |= ( ( u64 )in[ 6] )  << 48;
+
+  case 6: b |= ( ( u64 )in[ 5] )  << 40;
+
+  case 5: b |= ( ( u64 )in[ 4] )  << 32;
+
+  case 4: b |= ( ( u64 )in[ 3] )  << 24;
+
+  case 3: b |= ( ( u64 )in[ 2] )  << 16;
+
+  case 2: b |= ( ( u64 )in[ 1] )  <<  8;
+
+  case 1: b |= ( ( u64 )in[ 0] ); break;
+
+  case 0: break;
+  }
+
+#ifdef DEBUG
+  printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+  printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+  printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+  printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+  printf( "(%3d) padding   %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
+#endif
+  v3 ^= b;
+  SIPROUND;
+  SIPROUND;
+  v0 ^= b;
+#ifdef DEBUG
+  printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+  printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+  printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+  printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+#endif
+  v2 ^= 0xff;
+  SIPROUND;
+  SIPROUND;
+  SIPROUND;
+  SIPROUND;
+  b = v0 ^ v1 ^ v2  ^ v3;
+  U64TO8_LE( out, b );
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/siphash24.h b/src/dhcp-manager/systemd-dhcp/src/shared/siphash24.h
new file mode 100644
index 00000000..62e1168a
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/siphash24.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]);
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/socket-util.h b/src/dhcp-manager/systemd-dhcp/src/shared/socket-util.h
new file mode 100644
index 00000000..07d0aff7
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/socket-util.h
@@ -0,0 +1,118 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ether.h>
+#include <sys/un.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/if_packet.h>
+
+#include "macro.h"
+#include "util.h"
+
+union sockaddr_union {
+        struct sockaddr sa;
+        struct sockaddr_in in;
+        struct sockaddr_in6 in6;
+        struct sockaddr_un un;
+        struct sockaddr_nl nl;
+        struct sockaddr_storage storage;
+        struct sockaddr_ll ll;
+};
+
+typedef struct SocketAddress {
+        union sockaddr_union sockaddr;
+
+        /* We store the size here explicitly due to the weird
+         * sockaddr_un semantics for abstract sockets */
+        socklen_t size;
+
+        /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
+        int type;
+
+        /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
+        int protocol;
+} SocketAddress;
+
+typedef enum SocketAddressBindIPv6Only {
+        SOCKET_ADDRESS_DEFAULT,
+        SOCKET_ADDRESS_BOTH,
+        SOCKET_ADDRESS_IPV6_ONLY,
+        _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
+        _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
+} SocketAddressBindIPv6Only;
+
+#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
+
+int socket_address_parse(SocketAddress *a, const char *s);
+int socket_address_parse_netlink(SocketAddress *a, const char *s);
+int socket_address_print(const SocketAddress *a, char **p);
+int socket_address_verify(const SocketAddress *a) _pure_;
+int socket_address_unlink(SocketAddress *a);
+
+bool socket_address_can_accept(const SocketAddress *a) _pure_;
+
+int socket_address_listen(
+                const SocketAddress *a,
+                int flags,
+                int backlog,
+                SocketAddressBindIPv6Only only,
+                const char *bind_to_device,
+                bool free_bind,
+                bool transparent,
+                mode_t directory_mode,
+                mode_t socket_mode,
+                const char *label);
+int make_socket_fd(int log_level, const char* address, int flags);
+
+bool socket_address_is(const SocketAddress *a, const char *s, int type);
+bool socket_address_is_netlink(const SocketAddress *a, const char *s);
+
+bool socket_address_matches_fd(const SocketAddress *a, int fd);
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_;
+
+const char* socket_address_get_path(const SocketAddress *a);
+
+bool socket_ipv6_is_supported(void);
+
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, char **ret);
+int getpeername_pretty(int fd, char **ret);
+int getsockname_pretty(int fd, char **ret);
+
+int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
+int getnameinfo_pretty(int fd, char **ret);
+
+const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
+SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
+
+int netlink_family_to_string_alloc(int b, char **s);
+int netlink_family_from_string(const char *s) _pure_;
+
+bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b);
+
+#define ETHER_ADDR_TO_STRING_MAX (3*6)
+
+char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/sparse-endian.h b/src/dhcp-manager/systemd-dhcp/src/shared/sparse-endian.h
new file mode 100644
index 00000000..c913fda8
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/sparse-endian.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2012 Josh Triplett <josh@joshtriplett.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef SPARSE_ENDIAN_H
+#define SPARSE_ENDIAN_H
+
+#include <byteswap.h>
+#include <endian.h>
+#include <stdint.h>
+
+#ifdef __CHECKER__
+#define __bitwise __attribute__((bitwise))
+#define __force __attribute__((force))
+#else
+#define __bitwise
+#define __force
+#endif
+
+typedef uint16_t __bitwise le16_t;
+typedef uint16_t __bitwise be16_t;
+typedef uint32_t __bitwise le32_t;
+typedef uint32_t __bitwise be32_t;
+typedef uint64_t __bitwise le64_t;
+typedef uint64_t __bitwise be64_t;
+
+#undef htobe16
+#undef htole16
+#undef be16toh
+#undef le16toh
+#undef htobe32
+#undef htole32
+#undef be32toh
+#undef le32toh
+#undef htobe64
+#undef htole64
+#undef be64toh
+#undef le64toh
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define bswap_16_on_le(x) __bswap_16(x)
+#define bswap_32_on_le(x) __bswap_32(x)
+#define bswap_64_on_le(x) __bswap_64(x)
+#define bswap_16_on_be(x) (x)
+#define bswap_32_on_be(x) (x)
+#define bswap_64_on_be(x) (x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define bswap_16_on_le(x) (x)
+#define bswap_32_on_le(x) (x)
+#define bswap_64_on_le(x) (x)
+#define bswap_16_on_be(x) __bswap_16(x)
+#define bswap_32_on_be(x) __bswap_32(x)
+#define bswap_64_on_be(x) __bswap_64(x)
+#endif
+
+static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); }
+static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); }
+static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); }
+
+static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); }
+static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); }
+static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); }
+
+static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); }
+static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); }
+static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); }
+
+static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); }
+static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); }
+static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); }
+
+#endif /* SPARSE_ENDIAN_H */
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/strv.c b/src/dhcp-manager/systemd-dhcp/src/shared/strv.c
new file mode 100644
index 00000000..be2bd511
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/strv.c
@@ -0,0 +1,616 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "strv.h"
+
+char *strv_find(char **l, const char *name) {
+        char **i;
+
+        assert(name);
+
+        STRV_FOREACH(i, l)
+                if (streq(*i, name))
+                        return *i;
+
+        return NULL;
+}
+
+char *strv_find_prefix(char **l, const char *name) {
+        char **i;
+
+        assert(name);
+
+        STRV_FOREACH(i, l)
+                if (startswith(*i, name))
+                        return *i;
+
+        return NULL;
+}
+
+char *strv_find_startswith(char **l, const char *name) {
+        char **i, *e;
+
+        assert(name);
+
+        /* Like strv_find_prefix, but actually returns only the
+         * suffix, not the whole item */
+
+        STRV_FOREACH(i, l) {
+                e = startswith(*i, name);
+                if (e)
+                        return e;
+        }
+
+        return NULL;
+}
+
+void strv_free(char **l) {
+        char **k;
+
+        if (!l)
+                return;
+
+        for (k = l; *k; k++)
+                free(*k);
+
+        free(l);
+}
+
+char **strv_copy(char * const *l) {
+        char **r, **k;
+
+        k = r = new(char*, strv_length(l) + 1);
+        if (!r)
+                return NULL;
+
+        if (l)
+                for (; *l; k++, l++) {
+                        *k = strdup(*l);
+                        if (!*k) {
+                                strv_free(r);
+                                return NULL;
+                        }
+                }
+
+        *k = NULL;
+        return r;
+}
+
+unsigned strv_length(char * const *l) {
+        unsigned n = 0;
+
+        if (!l)
+                return 0;
+
+        for (; *l; l++)
+                n++;
+
+        return n;
+}
+
+char **strv_new_ap(const char *x, va_list ap) {
+        const char *s;
+        char **a;
+        unsigned n = 0, i = 0;
+        va_list aq;
+
+        /* As a special trick we ignore all listed strings that equal
+         * (const char*) -1. This is supposed to be used with the
+         * STRV_IFNOTNULL() macro to include possibly NULL strings in
+         * the string list. */
+
+        if (x) {
+                n = x == (const char*) -1 ? 0 : 1;
+
+                va_copy(aq, ap);
+                while ((s = va_arg(aq, const char*))) {
+                        if (s == (const char*) -1)
+                                continue;
+
+                        n++;
+                }
+
+                va_end(aq);
+        }
+
+        a = new(char*, n+1);
+        if (!a)
+                return NULL;
+
+        if (x) {
+                if (x != (const char*) -1) {
+                        a[i] = strdup(x);
+                        if (!a[i])
+                                goto fail;
+                        i++;
+                }
+
+                while ((s = va_arg(ap, const char*))) {
+
+                        if (s == (const char*) -1)
+                                continue;
+
+                        a[i] = strdup(s);
+                        if (!a[i])
+                                goto fail;
+
+                        i++;
+                }
+        }
+
+        a[i] = NULL;
+
+        return a;
+
+fail:
+        strv_free(a);
+        return NULL;
+}
+
+char **strv_new(const char *x, ...) {
+        char **r;
+        va_list ap;
+
+        va_start(ap, x);
+        r = strv_new_ap(x, ap);
+        va_end(ap);
+
+        return r;
+}
+
+int strv_extend_strv(char ***a, char **b) {
+        int r;
+        char **s;
+
+        STRV_FOREACH(s, b) {
+                r = strv_extend(a, *s);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
+        int r;
+        char **s;
+
+        STRV_FOREACH(s, b) {
+                char *v;
+
+                v = strappend(*s, suffix);
+                if (!v)
+                        return -ENOMEM;
+
+                r = strv_push(a, v);
+                if (r < 0) {
+                        free(v);
+                        return r;
+                }
+        }
+
+        return 0;
+}
+
+char **strv_split(const char *s, const char *separator) {
+        const char *word, *state;
+        size_t l;
+        unsigned n, i;
+        char **r;
+
+        assert(s);
+
+        n = 0;
+        FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
+                n++;
+
+        r = new(char*, n+1);
+        if (!r)
+                return NULL;
+
+        i = 0;
+        FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
+                r[i] = strndup(word, l);
+                if (!r[i]) {
+                        strv_free(r);
+                        return NULL;
+                }
+
+                i++;
+        }
+
+        r[i] = NULL;
+        return r;
+}
+
+char **strv_split_newlines(const char *s) {
+        char **l;
+        unsigned n;
+
+        assert(s);
+
+        /* Special version of strv_split() that splits on newlines and
+         * suppresses an empty string at the end */
+
+        l = strv_split(s, NEWLINE);
+        if (!l)
+                return NULL;
+
+        n = strv_length(l);
+        if (n <= 0)
+                return l;
+
+        if (isempty(l[n-1])) {
+                free(l[n-1]);
+                l[n-1] = NULL;
+        }
+
+        return l;
+}
+
+#if 0 /* NM_IGNORED */
+int strv_split_quoted(char ***t, const char *s, bool relax) {
+        size_t n = 0, allocated = 0;
+        _cleanup_strv_free_ char **l = NULL;
+        int r;
+
+        assert(t);
+        assert(s);
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = unquote_first_word(&s, &word, relax);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (!GREEDY_REALLOC(l, allocated, n + 2))
+                        return -ENOMEM;
+
+                l[n++] = word;
+                word = NULL;
+
+                l[n] = NULL;
+        }
+
+        if (!l)
+                l = new0(char*, 1);
+
+        *t = l;
+        l = NULL;
+
+        return 0;
+}
+#endif
+
+char *strv_join(char **l, const char *separator) {
+        char *r, *e;
+        char **s;
+        size_t n, k;
+
+        if (!separator)
+                separator = " ";
+
+        k = strlen(separator);
+
+        n = 0;
+        STRV_FOREACH(s, l) {
+                if (n != 0)
+                        n += k;
+                n += strlen(*s);
+        }
+
+        r = new(char, n+1);
+        if (!r)
+                return NULL;
+
+        e = r;
+        STRV_FOREACH(s, l) {
+                if (e != r)
+                        e = stpcpy(e, separator);
+
+                e = stpcpy(e, *s);
+        }
+
+        *e = 0;
+
+        return r;
+}
+
+char *strv_join_quoted(char **l) {
+        char *buf = NULL;
+        char **s;
+        size_t allocated = 0, len = 0;
+
+        STRV_FOREACH(s, l) {
+                /* assuming here that escaped string cannot be more
+                 * than twice as long, and reserving space for the
+                 * separator and quotes.
+                 */
+                _cleanup_free_ char *esc = NULL;
+                size_t needed;
+
+                if (!GREEDY_REALLOC(buf, allocated,
+                                    len + strlen(*s) * 2 + 3))
+                        goto oom;
+
+                esc = cescape(*s);
+                if (!esc)
+                        goto oom;
+
+                needed = snprintf(buf + len, allocated - len, "%s\"%s\"",
+                                  len > 0 ? " " : "", esc);
+                assert(needed < allocated - len);
+                len += needed;
+        }
+
+        if (!buf)
+                buf = malloc0(1);
+
+        return buf;
+
+ oom:
+        free(buf);
+        return NULL;
+}
+
+int strv_push(char ***l, char *value) {
+        char **c;
+        unsigned n, m;
+
+        if (!value)
+                return 0;
+
+        n = strv_length(*l);
+
+        /* increase and check for overflow */
+        m = n + 2;
+        if (m < n)
+                return -ENOMEM;
+
+        c = realloc_multiply(*l, sizeof(char*), m);
+        if (!c)
+                return -ENOMEM;
+
+        c[n] = value;
+        c[n+1] = NULL;
+
+        *l = c;
+        return 0;
+}
+
+int strv_push_prepend(char ***l, char *value) {
+        char **c;
+        unsigned n, m, i;
+
+        if (!value)
+                return 0;
+
+        n = strv_length(*l);
+
+        /* increase and check for overflow */
+        m = n + 2;
+        if (m < n)
+                return -ENOMEM;
+
+        c = new(char*, m);
+        if (!c)
+                return -ENOMEM;
+
+        for (i = 0; i < n; i++)
+                c[i+1] = (*l)[i];
+
+        c[0] = value;
+        c[n+1] = NULL;
+
+        free(*l);
+        *l = c;
+
+        return 0;
+}
+
+int strv_consume(char ***l, char *value) {
+        int r;
+
+        r = strv_push(l, value);
+        if (r < 0)
+                free(value);
+
+        return r;
+}
+
+int strv_consume_prepend(char ***l, char *value) {
+        int r;
+
+        r = strv_push_prepend(l, value);
+        if (r < 0)
+                free(value);
+
+        return r;
+}
+
+int strv_extend(char ***l, const char *value) {
+        char *v;
+
+        if (!value)
+                return 0;
+
+        v = strdup(value);
+        if (!v)
+                return -ENOMEM;
+
+        return strv_consume(l, v);
+}
+
+char **strv_uniq(char **l) {
+        char **i;
+
+        /* Drops duplicate entries. The first identical string will be
+         * kept, the others dropped */
+
+        STRV_FOREACH(i, l)
+                strv_remove(i+1, *i);
+
+        return l;
+}
+
+char **strv_remove(char **l, const char *s) {
+        char **f, **t;
+
+        if (!l)
+                return NULL;
+
+        assert(s);
+
+        /* Drops every occurrence of s in the string list, edits
+         * in-place. */
+
+        for (f = t = l; *f; f++)
+                if (streq(*f, s))
+                        free(*f);
+                else
+                        *(t++) = *f;
+
+        *t = NULL;
+        return l;
+}
+
+char **strv_parse_nulstr(const char *s, size_t l) {
+        const char *p;
+        unsigned c = 0, i = 0;
+        char **v;
+
+        assert(s || l <= 0);
+
+        if (l <= 0)
+                return new0(char*, 1);
+
+        for (p = s; p < s + l; p++)
+                if (*p == 0)
+                        c++;
+
+        if (s[l-1] != 0)
+                c++;
+
+        v = new0(char*, c+1);
+        if (!v)
+                return NULL;
+
+        p = s;
+        while (p < s + l) {
+                const char *e;
+
+                e = memchr(p, 0, s + l - p);
+
+                v[i] = strndup(p, e ? e - p : s + l - p);
+                if (!v[i]) {
+                        strv_free(v);
+                        return NULL;
+                }
+
+                i++;
+
+                if (!e)
+                        break;
+
+                p = e + 1;
+        }
+
+        assert(i == c);
+
+        return v;
+}
+
+char **strv_split_nulstr(const char *s) {
+        const char *i;
+        char **r = NULL;
+
+        NULSTR_FOREACH(i, s)
+                if (strv_extend(&r, i) < 0) {
+                        strv_free(r);
+                        return NULL;
+                }
+
+        if (!r)
+                return strv_new(NULL, NULL);
+
+        return r;
+}
+
+bool strv_overlap(char **a, char **b) {
+        char **i;
+
+        STRV_FOREACH(i, a)
+                if (strv_contains(b, *i))
+                        return true;
+
+        return false;
+}
+
+static int str_compare(const void *_a, const void *_b) {
+        const char **a = (const char**) _a, **b = (const char**) _b;
+
+        return strcmp(*a, *b);
+}
+
+char **strv_sort(char **l) {
+
+        if (strv_isempty(l))
+                return l;
+
+        qsort(l, strv_length(l), sizeof(char*), str_compare);
+        return l;
+}
+
+void strv_print(char **l) {
+        char **s;
+
+        STRV_FOREACH(s, l)
+                puts(*s);
+}
+
+int strv_extendf(char ***l, const char *format, ...) {
+        va_list ap;
+        char *x;
+        int r;
+
+        va_start(ap, format);
+        r = vasprintf(&x, format, ap);
+        va_end(ap);
+
+        if (r < 0)
+                return -ENOMEM;
+
+        return strv_consume(l, x);
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/strv.h b/src/dhcp-manager/systemd-dhcp/src/shared/strv.h
new file mode 100644
index 00000000..47618bd2
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/strv.h
@@ -0,0 +1,137 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "util.h"
+
+char *strv_find(char **l, const char *name) _pure_;
+char *strv_find_prefix(char **l, const char *name) _pure_;
+char *strv_find_startswith(char **l, const char *name) _pure_;
+
+void strv_free(char **l);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
+#define _cleanup_strv_free_ _cleanup_(strv_freep)
+
+char **strv_copy(char * const *l);
+unsigned strv_length(char * const *l) _pure_;
+
+int strv_extend_strv(char ***a, char **b);
+int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
+int strv_extend(char ***l, const char *value);
+int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
+int strv_push(char ***l, char *value);
+int strv_push_prepend(char ***l, char *value);
+int strv_consume(char ***l, char *value);
+int strv_consume_prepend(char ***l, char *value);
+
+char **strv_remove(char **l, const char *s);
+char **strv_uniq(char **l);
+
+#define strv_contains(l, s) (!!strv_find((l), (s)))
+
+char **strv_new(const char *x, ...) _sentinel_;
+char **strv_new_ap(const char *x, va_list ap);
+
+static inline const char* STRV_IFNOTNULL(const char *x) {
+        return x ? x : (const char *) -1;
+}
+
+static inline bool strv_isempty(char * const *l) {
+        return !l || !*l;
+}
+
+char **strv_split(const char *s, const char *separator);
+char **strv_split_newlines(const char *s);
+
+int strv_split_quoted(char ***t, const char *s, bool relax);
+
+char *strv_join(char **l, const char *separator);
+char *strv_join_quoted(char **l);
+
+char **strv_parse_nulstr(const char *s, size_t l);
+char **strv_split_nulstr(const char *s);
+
+bool strv_overlap(char **a, char **b) _pure_;
+
+#define STRV_FOREACH(s, l)                      \
+        for ((s) = (l); (s) && *(s); (s)++)
+
+#define STRV_FOREACH_BACKWARDS(s, l)            \
+        STRV_FOREACH(s, l)                      \
+                ;                               \
+        for ((s)--; (l) && ((s) >= (l)); (s)--)
+
+#define STRV_FOREACH_PAIR(x, y, l)               \
+        for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
+
+char **strv_sort(char **l);
+void strv_print(char **l);
+
+#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
+
+#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
+
+#define strv_from_stdarg_alloca(first)                          \
+        ({                                                      \
+                char **_l;                                      \
+                                                                \
+                if (!first)                                     \
+                        _l = (char**) &first;                   \
+                else {                                          \
+                        unsigned _n;                            \
+                        va_list _ap;                            \
+                                                                \
+                        _n = 1;                                 \
+                        va_start(_ap, first);                   \
+                        while (va_arg(_ap, char*))              \
+                                _n++;                           \
+                        va_end(_ap);                            \
+                                                                \
+                        _l = newa(char*, _n+1);                 \
+                        _l[_n = 0] = (char*) first;             \
+                        va_start(_ap, first);                   \
+                        for (;;) {                              \
+                                _l[++_n] = va_arg(_ap, char*);  \
+                                if (!_l[_n])                    \
+                                        break;                  \
+                        }                                       \
+                        va_end(_ap);                            \
+                }                                               \
+                _l;                                             \
+        })
+
+#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x)
+
+#define FOREACH_STRING(x, ...)                               \
+        for (char **_l = ({                                  \
+                char **_ll = STRV_MAKE(__VA_ARGS__);         \
+                x = _ll ? _ll[0] : NULL;                     \
+                _ll;                                         \
+        });                                                  \
+        _l && *_l;                                           \
+        x = ({                                               \
+                _l ++;                                       \
+                _l[0];                                       \
+        }))
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/time-util.c b/src/dhcp-manager/systemd-dhcp/src/shared/time-util.c
new file mode 100644
index 00000000..c8fa1acb
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/time-util.c
@@ -0,0 +1,1003 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <time.h>
+#include <string.h>
+#include <sys/timex.h>
+#include <sys/timerfd.h>
+
+#include "util.h"
+#include "time-util.h"
+#if 0 /* NM_IGNORED */
+#include "strv.h"
+#endif
+
+usec_t now(clockid_t clock_id) {
+        struct timespec ts;
+
+        assert_se(clock_gettime(clock_id, &ts) == 0);
+
+        return timespec_load(&ts);
+}
+
+dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
+        assert(ts);
+
+        ts->realtime = now(CLOCK_REALTIME);
+        ts->monotonic = now(CLOCK_MONOTONIC);
+
+        return ts;
+}
+
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
+        int64_t delta;
+        assert(ts);
+
+        if (u == USEC_INFINITY || u <= 0) {
+                ts->realtime = ts->monotonic = u;
+                return ts;
+        }
+
+        ts->realtime = u;
+
+        delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
+        ts->monotonic = now(CLOCK_MONOTONIC);
+
+        if ((int64_t) ts->monotonic > delta)
+                ts->monotonic -= delta;
+        else
+                ts->monotonic = 0;
+
+        return ts;
+}
+
+dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
+        int64_t delta;
+        assert(ts);
+
+        if (u == USEC_INFINITY) {
+                ts->realtime = ts->monotonic = USEC_INFINITY;
+                return ts;
+        }
+
+        ts->monotonic = u;
+        delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
+
+        ts->realtime = now(CLOCK_REALTIME);
+        if ((int64_t) ts->realtime > delta)
+                ts->realtime -= delta;
+        else
+                ts->realtime = 0;
+
+        return ts;
+}
+
+usec_t timespec_load(const struct timespec *ts) {
+        assert(ts);
+
+        if (ts->tv_sec == (time_t) -1 &&
+            ts->tv_nsec == (long) -1)
+                return USEC_INFINITY;
+
+        if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
+                return USEC_INFINITY;
+
+        return
+                (usec_t) ts->tv_sec * USEC_PER_SEC +
+                (usec_t) ts->tv_nsec / NSEC_PER_USEC;
+}
+
+struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
+        assert(ts);
+
+        if (u == USEC_INFINITY) {
+                ts->tv_sec = (time_t) -1;
+                ts->tv_nsec = (long) -1;
+                return ts;
+        }
+
+        ts->tv_sec = (time_t) (u / USEC_PER_SEC);
+        ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
+
+        return ts;
+}
+
+#if 0 /* NM_IGNORED */
+usec_t timeval_load(const struct timeval *tv) {
+        assert(tv);
+
+        if (tv->tv_sec == (time_t) -1 &&
+            tv->tv_usec == (suseconds_t) -1)
+                return USEC_INFINITY;
+
+        if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
+                return USEC_INFINITY;
+
+        return
+                (usec_t) tv->tv_sec * USEC_PER_SEC +
+                (usec_t) tv->tv_usec;
+}
+
+struct timeval *timeval_store(struct timeval *tv, usec_t u) {
+        assert(tv);
+
+        if (u == USEC_INFINITY) {
+                tv->tv_sec = (time_t) -1;
+                tv->tv_usec = (suseconds_t) -1;
+        } else {
+                tv->tv_sec = (time_t) (u / USEC_PER_SEC);
+                tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
+        }
+
+        return tv;
+}
+
+static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
+        struct tm tm;
+        time_t sec;
+
+        assert(buf);
+        assert(l > 0);
+
+        if (t <= 0 || t == USEC_INFINITY)
+                return NULL;
+
+        sec = (time_t) (t / USEC_PER_SEC);
+
+        if (utc)
+                gmtime_r(&sec, &tm);
+        else
+                localtime_r(&sec, &tm);
+        if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
+                return NULL;
+
+        return buf;
+}
+
+char *format_timestamp(char *buf, size_t l, usec_t t) {
+        return format_timestamp_internal(buf, l, t, false);
+}
+
+char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
+        return format_timestamp_internal(buf, l, t, true);
+}
+
+static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool utc) {
+        struct tm tm;
+        time_t sec;
+
+        assert(buf);
+        assert(l > 0);
+
+        if (t <= 0 || t == USEC_INFINITY)
+                return NULL;
+
+        sec = (time_t) (t / USEC_PER_SEC);
+        if (utc)
+                gmtime_r(&sec, &tm);
+        else
+                localtime_r(&sec, &tm);
+
+        if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
+                return NULL;
+        snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
+        if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
+                return NULL;
+
+        return buf;
+}
+
+char *format_timestamp_us(char *buf, size_t l, usec_t t) {
+        return format_timestamp_internal_us(buf, l, t, false);
+}
+
+char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
+        return format_timestamp_internal_us(buf, l, t, true);
+}
+
+char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
+        const char *s;
+        usec_t n, d;
+
+        if (t <= 0 || t == USEC_INFINITY)
+                return NULL;
+
+        n = now(CLOCK_REALTIME);
+        if (n > t) {
+                d = n - t;
+                s = "ago";
+        } else {
+                d = t - n;
+                s = "left";
+        }
+
+        if (d >= USEC_PER_YEAR)
+                snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
+                         d / USEC_PER_YEAR,
+                         (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
+        else if (d >= USEC_PER_MONTH)
+                snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
+                         d / USEC_PER_MONTH,
+                         (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
+        else if (d >= USEC_PER_WEEK)
+                snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
+                         d / USEC_PER_WEEK,
+                         (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
+        else if (d >= 2*USEC_PER_DAY)
+                snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
+        else if (d >= 25*USEC_PER_HOUR)
+                snprintf(buf, l, "1 day " USEC_FMT "h %s",
+                         (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
+        else if (d >= 6*USEC_PER_HOUR)
+                snprintf(buf, l, USEC_FMT "h %s",
+                         d / USEC_PER_HOUR, s);
+        else if (d >= USEC_PER_HOUR)
+                snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
+                         d / USEC_PER_HOUR,
+                         (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
+        else if (d >= 5*USEC_PER_MINUTE)
+                snprintf(buf, l, USEC_FMT "min %s",
+                         d / USEC_PER_MINUTE, s);
+        else if (d >= USEC_PER_MINUTE)
+                snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
+                         d / USEC_PER_MINUTE,
+                         (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
+        else if (d >= USEC_PER_SEC)
+                snprintf(buf, l, USEC_FMT "s %s",
+                         d / USEC_PER_SEC, s);
+        else if (d >= USEC_PER_MSEC)
+                snprintf(buf, l, USEC_FMT "ms %s",
+                         d / USEC_PER_MSEC, s);
+        else if (d > 0)
+                snprintf(buf, l, USEC_FMT"us %s",
+                         d, s);
+        else
+                snprintf(buf, l, "now");
+
+        buf[l-1] = 0;
+        return buf;
+}
+#endif
+
+char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
+        static const struct {
+                const char *suffix;
+                usec_t usec;
+        } table[] = {
+                { "y", USEC_PER_YEAR },
+                { "month", USEC_PER_MONTH },
+                { "w", USEC_PER_WEEK },
+                { "d", USEC_PER_DAY },
+                { "h", USEC_PER_HOUR },
+                { "min", USEC_PER_MINUTE },
+                { "s", USEC_PER_SEC },
+                { "ms", USEC_PER_MSEC },
+                { "us", 1 },
+        };
+
+        unsigned i;
+        char *p = buf;
+        bool something = false;
+
+        assert(buf);
+        assert(l > 0);
+
+        if (t == USEC_INFINITY) {
+                strncpy(p, "infinity", l-1);
+                p[l-1] = 0;
+                return p;
+        }
+
+        if (t <= 0) {
+                strncpy(p, "0", l-1);
+                p[l-1] = 0;
+                return p;
+        }
+
+        /* The result of this function can be parsed with parse_sec */
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+                int k = 0;
+                size_t n;
+                bool done = false;
+                usec_t a, b;
+
+                if (t <= 0)
+                        break;
+
+                if (t < accuracy && something)
+                        break;
+
+                if (t < table[i].usec)
+                        continue;
+
+                if (l <= 1)
+                        break;
+
+                a = t / table[i].usec;
+                b = t % table[i].usec;
+
+                /* Let's see if we should shows this in dot notation */
+                if (t < USEC_PER_MINUTE && b > 0) {
+                        usec_t cc;
+                        int j;
+
+                        j = 0;
+                        for (cc = table[i].usec; cc > 1; cc /= 10)
+                                j++;
+
+                        for (cc = accuracy; cc > 1; cc /= 10) {
+                                b /= 10;
+                                j--;
+                        }
+
+                        if (j > 0) {
+                                k = snprintf(p, l,
+                                             "%s"USEC_FMT".%0*llu%s",
+                                             p > buf ? " " : "",
+                                             a,
+                                             j,
+                                             (unsigned long long) b,
+                                             table[i].suffix);
+
+                                t = 0;
+                                done = true;
+                        }
+                }
+
+                /* No? Then let's show it normally */
+                if (!done) {
+                        k = snprintf(p, l,
+                                     "%s"USEC_FMT"%s",
+                                     p > buf ? " " : "",
+                                     a,
+                                     table[i].suffix);
+
+                        t = b;
+                }
+
+                n = MIN((size_t) k, l);
+
+                l -= n;
+                p += n;
+
+                something = true;
+        }
+
+        *p = 0;
+
+        return buf;
+}
+
+#if 0 /* NM_IGNORED */
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
+
+        assert(f);
+        assert(name);
+        assert(t);
+
+        if (!dual_timestamp_is_set(t))
+                return;
+
+        fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
+                name,
+                t->realtime,
+                t->monotonic);
+}
+
+void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+        unsigned long long a, b;
+
+        assert(value);
+        assert(t);
+
+        if (sscanf(value, "%llu %llu", &a, &b) != 2)
+                log_debug("Failed to parse finish timestamp value %s", value);
+        else {
+                t->realtime = a;
+                t->monotonic = b;
+        }
+}
+
+int parse_timestamp(const char *t, usec_t *usec) {
+        static const struct {
+                const char *name;
+                const int nr;
+        } day_nr[] = {
+                { "Sunday",    0 },
+                { "Sun",       0 },
+                { "Monday",    1 },
+                { "Mon",       1 },
+                { "Tuesday",   2 },
+                { "Tue",       2 },
+                { "Wednesday", 3 },
+                { "Wed",       3 },
+                { "Thursday",  4 },
+                { "Thu",       4 },
+                { "Friday",    5 },
+                { "Fri",       5 },
+                { "Saturday",  6 },
+                { "Sat",       6 },
+        };
+
+        const char *k;
+        struct tm tm, copy;
+        time_t x;
+        usec_t plus = 0, minus = 0, ret;
+        int r, weekday = -1;
+        unsigned i;
+
+        /*
+         * Allowed syntaxes:
+         *
+         *   2012-09-22 16:34:22
+         *   2012-09-22 16:34     (seconds will be set to 0)
+         *   2012-09-22           (time will be set to 00:00:00)
+         *   16:34:22             (date will be set to today)
+         *   16:34                (date will be set to today, seconds to 0)
+         *   now
+         *   yesterday            (time is set to 00:00:00)
+         *   today                (time is set to 00:00:00)
+         *   tomorrow             (time is set to 00:00:00)
+         *   +5min
+         *   -5days
+         *   @2147483647          (seconds since epoch)
+         *
+         */
+
+        assert(t);
+        assert(usec);
+
+        x = time(NULL);
+        assert_se(localtime_r(&x, &tm));
+        tm.tm_isdst = -1;
+
+        if (streq(t, "now"))
+                goto finish;
+
+        else if (streq(t, "today")) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (streq(t, "yesterday")) {
+                tm.tm_mday --;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (streq(t, "tomorrow")) {
+                tm.tm_mday ++;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (t[0] == '+') {
+                r = parse_sec(t+1, &plus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+
+        } else if (t[0] == '-') {
+                r = parse_sec(t+1, &minus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+
+        } else if (t[0] == '@')
+                return parse_sec(t + 1, usec);
+
+        else if (endswith(t, " ago")) {
+                _cleanup_free_ char *z = strndup(t, strlen(t) - 4);
+
+                if (!z)
+                        return -ENOMEM;
+
+                r = parse_sec(z, &minus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        } else if (endswith(t, " left")) {
+                _cleanup_free_ char *z = strndup(t, strlen(t) - 4);
+
+                if (!z)
+                        return -ENOMEM;
+
+                r = parse_sec(z, &plus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        }
+
+        for (i = 0; i < ELEMENTSOF(day_nr); i++) {
+                size_t skip;
+
+                if (!startswith_no_case(t, day_nr[i].name))
+                        continue;
+
+                skip = strlen(day_nr[i].name);
+                if (t[skip] != ' ')
+                        continue;
+
+                weekday = day_nr[i].nr;
+                t += skip + 1;
+                break;
+        }
+
+        copy = tm;
+        k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        return -EINVAL;
+
+finish:
+        x = mktime(&tm);
+        if (x == (time_t) -1)
+                return -EINVAL;
+
+        if (weekday >= 0 && tm.tm_wday != weekday)
+                return -EINVAL;
+
+        ret = (usec_t) x * USEC_PER_SEC;
+
+        ret += plus;
+        if (ret > minus)
+                ret -= minus;
+        else
+                ret = 0;
+
+        *usec = ret;
+
+        return 0;
+}
+
+int parse_sec(const char *t, usec_t *usec) {
+        static const struct {
+                const char *suffix;
+                usec_t usec;
+        } table[] = {
+                { "seconds", USEC_PER_SEC },
+                { "second", USEC_PER_SEC },
+                { "sec", USEC_PER_SEC },
+                { "s", USEC_PER_SEC },
+                { "minutes", USEC_PER_MINUTE },
+                { "minute", USEC_PER_MINUTE },
+                { "min", USEC_PER_MINUTE },
+                { "months", USEC_PER_MONTH },
+                { "month", USEC_PER_MONTH },
+                { "msec", USEC_PER_MSEC },
+                { "ms", USEC_PER_MSEC },
+                { "m", USEC_PER_MINUTE },
+                { "hours", USEC_PER_HOUR },
+                { "hour", USEC_PER_HOUR },
+                { "hr", USEC_PER_HOUR },
+                { "h", USEC_PER_HOUR },
+                { "days", USEC_PER_DAY },
+                { "day", USEC_PER_DAY },
+                { "d", USEC_PER_DAY },
+                { "weeks", USEC_PER_WEEK },
+                { "week", USEC_PER_WEEK },
+                { "w", USEC_PER_WEEK },
+                { "years", USEC_PER_YEAR },
+                { "year", USEC_PER_YEAR },
+                { "y", USEC_PER_YEAR },
+                { "usec", 1ULL },
+                { "us", 1ULL },
+                { "", USEC_PER_SEC }, /* default is sec */
+        };
+
+        const char *p, *s;
+        usec_t r = 0;
+        bool something = false;
+
+        assert(t);
+        assert(usec);
+
+        p = t;
+
+        p += strspn(p, WHITESPACE);
+        s = startswith(p, "infinity");
+        if (s) {
+                s += strspn(s, WHITESPACE);
+                if (*s != 0)
+                        return -EINVAL;
+
+                *usec = USEC_INFINITY;
+                return 0;
+        }
+
+        for (;;) {
+                long long l, z = 0;
+                char *e;
+                unsigned i, n = 0;
+
+                p += strspn(p, WHITESPACE);
+
+                if (*p == 0) {
+                        if (!something)
+                                return -EINVAL;
+
+                        break;
+                }
+
+                errno = 0;
+                l = strtoll(p, &e, 10);
+
+                if (errno > 0)
+                        return -errno;
+
+                if (l < 0)
+                        return -ERANGE;
+
+                if (*e == '.') {
+                        char *b = e + 1;
+
+                        errno = 0;
+                        z = strtoll(b, &e, 10);
+                        if (errno > 0)
+                                return -errno;
+
+                        if (z < 0)
+                                return -ERANGE;
+
+                        if (e == b)
+                                return -EINVAL;
+
+                        n = e - b;
+
+                } else if (e == p)
+                        return -EINVAL;
+
+                e += strspn(e, WHITESPACE);
+
+                for (i = 0; i < ELEMENTSOF(table); i++)
+                        if (startswith(e, table[i].suffix)) {
+                                usec_t k = (usec_t) z * table[i].usec;
+
+                                for (; n > 0; n--)
+                                        k /= 10;
+
+                                r += (usec_t) l * table[i].usec + k;
+                                p = e + strlen(table[i].suffix);
+
+                                something = true;
+                                break;
+                        }
+
+                if (i >= ELEMENTSOF(table))
+                        return -EINVAL;
+
+        }
+
+        *usec = r;
+
+        return 0;
+}
+
+int parse_nsec(const char *t, nsec_t *nsec) {
+        static const struct {
+                const char *suffix;
+                nsec_t nsec;
+        } table[] = {
+                { "seconds", NSEC_PER_SEC },
+                { "second", NSEC_PER_SEC },
+                { "sec", NSEC_PER_SEC },
+                { "s", NSEC_PER_SEC },
+                { "minutes", NSEC_PER_MINUTE },
+                { "minute", NSEC_PER_MINUTE },
+                { "min", NSEC_PER_MINUTE },
+                { "months", NSEC_PER_MONTH },
+                { "month", NSEC_PER_MONTH },
+                { "msec", NSEC_PER_MSEC },
+                { "ms", NSEC_PER_MSEC },
+                { "m", NSEC_PER_MINUTE },
+                { "hours", NSEC_PER_HOUR },
+                { "hour", NSEC_PER_HOUR },
+                { "hr", NSEC_PER_HOUR },
+                { "h", NSEC_PER_HOUR },
+                { "days", NSEC_PER_DAY },
+                { "day", NSEC_PER_DAY },
+                { "d", NSEC_PER_DAY },
+                { "weeks", NSEC_PER_WEEK },
+                { "week", NSEC_PER_WEEK },
+                { "w", NSEC_PER_WEEK },
+                { "years", NSEC_PER_YEAR },
+                { "year", NSEC_PER_YEAR },
+                { "y", NSEC_PER_YEAR },
+                { "usec", NSEC_PER_USEC },
+                { "us", NSEC_PER_USEC },
+                { "nsec", 1ULL },
+                { "ns", 1ULL },
+                { "", 1ULL }, /* default is nsec */
+        };
+
+        const char *p, *s;
+        nsec_t r = 0;
+        bool something = false;
+
+        assert(t);
+        assert(nsec);
+
+        p = t;
+
+        p += strspn(p, WHITESPACE);
+        s = startswith(p, "infinity");
+        if (s) {
+                s += strspn(s, WHITESPACE);
+                if (!*s != 0)
+                        return -EINVAL;
+
+                *nsec = NSEC_INFINITY;
+                return 0;
+        }
+
+        for (;;) {
+                long long l, z = 0;
+                char *e;
+                unsigned i, n = 0;
+
+                p += strspn(p, WHITESPACE);
+
+                if (*p == 0) {
+                        if (!something)
+                                return -EINVAL;
+
+                        break;
+                }
+
+                errno = 0;
+                l = strtoll(p, &e, 10);
+
+                if (errno > 0)
+                        return -errno;
+
+                if (l < 0)
+                        return -ERANGE;
+
+                if (*e == '.') {
+                        char *b = e + 1;
+
+                        errno = 0;
+                        z = strtoll(b, &e, 10);
+                        if (errno > 0)
+                                return -errno;
+
+                        if (z < 0)
+                                return -ERANGE;
+
+                        if (e == b)
+                                return -EINVAL;
+
+                        n = e - b;
+
+                } else if (e == p)
+                        return -EINVAL;
+
+                e += strspn(e, WHITESPACE);
+
+                for (i = 0; i < ELEMENTSOF(table); i++)
+                        if (startswith(e, table[i].suffix)) {
+                                nsec_t k = (nsec_t) z * table[i].nsec;
+
+                                for (; n > 0; n--)
+                                        k /= 10;
+
+                                r += (nsec_t) l * table[i].nsec + k;
+                                p = e + strlen(table[i].suffix);
+
+                                something = true;
+                                break;
+                        }
+
+                if (i >= ELEMENTSOF(table))
+                        return -EINVAL;
+
+        }
+
+        *nsec = r;
+
+        return 0;
+}
+
+bool ntp_synced(void) {
+        struct timex txc = {};
+
+        if (adjtimex(&txc) < 0)
+                return false;
+
+        if (txc.status & STA_UNSYNC)
+                return false;
+
+        return true;
+}
+
+int get_timezones(char ***ret) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_strv_free_ char **zones = NULL;
+        size_t n_zones = 0, n_allocated = 0;
+
+        assert(ret);
+
+        zones = strv_new("UTC", NULL);
+        if (!zones)
+                return -ENOMEM;
+
+        n_allocated = 2;
+        n_zones = 1;
+
+        f = fopen("/usr/share/zoneinfo/zone.tab", "re");
+        if (f) {
+                char l[LINE_MAX];
+
+                FOREACH_LINE(l, f, return -errno) {
+                        char *p, *w;
+                        size_t k;
+
+                        p = strstrip(l);
+
+                        if (isempty(p) || *p == '#')
+                                continue;
+
+                        /* Skip over country code */
+                        p += strcspn(p, WHITESPACE);
+                        p += strspn(p, WHITESPACE);
+
+                        /* Skip over coordinates */
+                        p += strcspn(p, WHITESPACE);
+                        p += strspn(p, WHITESPACE);
+
+                        /* Found timezone name */
+                        k = strcspn(p, WHITESPACE);
+                        if (k <= 0)
+                                continue;
+
+                        w = strndup(p, k);
+                        if (!w)
+                                return -ENOMEM;
+
+                        if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
+                                free(w);
+                                return -ENOMEM;
+                        }
+
+                        zones[n_zones++] = w;
+                        zones[n_zones] = NULL;
+                }
+
+                strv_sort(zones);
+
+        } else if (errno != ENOENT)
+                return -errno;
+
+        *ret = zones;
+        zones = NULL;
+
+        return 0;
+}
+
+bool timezone_is_valid(const char *name) {
+        bool slash = false;
+        const char *p, *t;
+        struct stat st;
+
+        if (!name || *name == 0 || *name == '/')
+                return false;
+
+        for (p = name; *p; p++) {
+                if (!(*p >= '0' && *p <= '9') &&
+                    !(*p >= 'a' && *p <= 'z') &&
+                    !(*p >= 'A' && *p <= 'Z') &&
+                    !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
+                        return false;
+
+                if (*p == '/') {
+
+                        if (slash)
+                                return false;
+
+                        slash = true;
+                } else
+                        slash = false;
+        }
+
+        if (slash)
+                return false;
+
+        t = strappenda("/usr/share/zoneinfo/", name);
+        if (stat(t, &st) < 0)
+                return false;
+
+        if (!S_ISREG(st.st_mode))
+                return false;
+
+        return true;
+}
+#endif
+
+clockid_t clock_boottime_or_monotonic(void) {
+        static clockid_t clock = -1;
+        int fd;
+
+        if (clock != -1)
+                return clock;
+
+        fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
+        if (fd < 0)
+                clock = CLOCK_MONOTONIC;
+        else {
+                safe_close(fd);
+                clock = CLOCK_BOOTTIME;
+        }
+
+        return clock;
+}
+
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/time-util.h b/src/dhcp-manager/systemd-dhcp/src/shared/time-util.h
new file mode 100644
index 00000000..578a0c1c
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/time-util.h
@@ -0,0 +1,111 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <inttypes.h>
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+#include "nm-sd-adapt.h"
+
+#define NSEC_FMT "%" PRIu64
+#define USEC_FMT "%" PRIu64
+
+#include "macro.h"
+
+typedef struct dual_timestamp {
+        usec_t realtime;
+        usec_t monotonic;
+} dual_timestamp;
+
+#define USEC_INFINITY ((usec_t) -1)
+#define NSEC_INFINITY ((nsec_t) -1)
+
+#define MSEC_PER_SEC  1000ULL
+#define USEC_PER_SEC  ((usec_t) 1000000ULL)
+#define USEC_PER_MSEC ((usec_t) 1000ULL)
+#define NSEC_PER_SEC  ((nsec_t) 1000000000ULL)
+#define NSEC_PER_MSEC ((nsec_t) 1000000ULL)
+#define NSEC_PER_USEC ((nsec_t) 1000ULL)
+
+#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC))
+#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC))
+#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE))
+#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE))
+#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR))
+#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR))
+#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY))
+#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY))
+#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC))
+#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC))
+#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
+#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
+
+#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
+#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
+#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
+#define FORMAT_TIMESPAN_MAX 64
+
+#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
+
+#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0, 0 })
+
+usec_t now(clockid_t clock);
+
+dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
+dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
+
+static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
+        return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
+                (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
+}
+
+usec_t timespec_load(const struct timespec *ts) _pure_;
+struct timespec *timespec_store(struct timespec *ts, usec_t u);
+
+usec_t timeval_load(const struct timeval *tv) _pure_;
+struct timeval *timeval_store(struct timeval *tv, usec_t u);
+
+char *format_timestamp(char *buf, size_t l, usec_t t);
+char *format_timestamp_utc(char *buf, size_t l, usec_t t);
+char *format_timestamp_us(char *buf, size_t l, usec_t t);
+char *format_timestamp_us_utc(char *buf, size_t l, usec_t t);
+char *format_timestamp_relative(char *buf, size_t l, usec_t t);
+char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
+
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
+void dual_timestamp_deserialize(const char *value, dual_timestamp *t);
+
+int parse_timestamp(const char *t, usec_t *usec);
+
+int parse_sec(const char *t, usec_t *usec);
+int parse_nsec(const char *t, nsec_t *nsec);
+
+bool ntp_synced(void);
+
+int get_timezones(char ***l);
+bool timezone_is_valid(const char *name);
+
+clockid_t clock_boottime_or_monotonic(void);
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/unaligned.h b/src/dhcp-manager/systemd-dhcp/src/shared/unaligned.h
new file mode 100644
index 00000000..d6181dd9
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/unaligned.h
@@ -0,0 +1,66 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+
+static inline uint16_t unaligned_read_be16(const void *_u) {
+        const uint8_t *u = _u;
+
+        return (((uint16_t) u[0]) << 8) |
+                ((uint16_t) u[1]);
+}
+
+static inline uint32_t unaligned_read_be32(const void *_u) {
+        const uint8_t *u = _u;
+
+        return (((uint32_t) unaligned_read_be16(u)) << 16) |
+                ((uint32_t) unaligned_read_be16(u + 2));
+}
+
+static inline uint64_t unaligned_read_be64(const void *_u) {
+        const uint8_t *u = _u;
+
+        return (((uint64_t) unaligned_read_be32(u)) << 32) |
+                ((uint64_t) unaligned_read_be32(u + 4));
+}
+
+static inline void unaligned_write_be16(void *_u, uint16_t a) {
+        uint8_t *u = _u;
+
+        u[0] = (uint8_t) (a >> 8);
+        u[1] = (uint8_t) a;
+}
+
+static inline void unaligned_write_be32(void *_u, uint32_t a) {
+        uint8_t *u = _u;
+
+        unaligned_write_be16(u, (uint16_t) (a >> 16));
+        unaligned_write_be16(u + 2, (uint16_t) a);
+}
+
+static inline void unaligned_write_be64(void *_u, uint64_t a) {
+        uint8_t *u = _u;
+
+        unaligned_write_be32(u, (uint32_t) (a >> 32));
+        unaligned_write_be32(u + 4, (uint32_t) a);
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/utf8.c b/src/dhcp-manager/systemd-dhcp/src/shared/utf8.c
new file mode 100644
index 00000000..cfedbfcb
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/utf8.c
@@ -0,0 +1,314 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2008-2011 Kay Sievers
+  Copyright 2012 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* Parts of this file are based on the GLIB utf8 validation functions. The
+ * original license text follows. */
+
+/* gutf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "utf8.h"
+#include "util.h"
+
+static inline bool is_unicode_valid(uint32_t ch) {
+
+        if (ch >= 0x110000) /* End of unicode space */
+                return false;
+        if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
+                return false;
+        if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
+                return false;
+        if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
+                return false;
+
+        return true;
+}
+
+static bool is_unicode_control(uint32_t ch) {
+
+        /*
+          0 to ' '-1 is the C0 range.
+          DEL=0x7F, and DEL+1 to 0x9F is C1 range.
+          '\t' is in C0 range, but more or less harmless and commonly used.
+        */
+
+        return (ch < ' ' && ch != '\t' && ch != '\n') ||
+                (0x7F <= ch && ch <= 0x9F);
+}
+
+/* count of characters used to encode one unicode char */
+static int utf8_encoded_expected_len(const char *str) {
+        unsigned char c;
+
+        assert(str);
+
+        c = (unsigned char) str[0];
+        if (c < 0x80)
+                return 1;
+        if ((c & 0xe0) == 0xc0)
+                return 2;
+        if ((c & 0xf0) == 0xe0)
+                return 3;
+        if ((c & 0xf8) == 0xf0)
+                return 4;
+        if ((c & 0xfc) == 0xf8)
+                return 5;
+        if ((c & 0xfe) == 0xfc)
+                return 6;
+
+        return 0;
+}
+
+/* decode one unicode char */
+int utf8_encoded_to_unichar(const char *str) {
+        int unichar, len, i;
+
+        assert(str);
+
+        len = utf8_encoded_expected_len(str);
+
+        switch (len) {
+        case 1:
+                return (int)str[0];
+        case 2:
+                unichar = str[0] & 0x1f;
+                break;
+        case 3:
+                unichar = (int)str[0] & 0x0f;
+                break;
+        case 4:
+                unichar = (int)str[0] & 0x07;
+                break;
+        case 5:
+                unichar = (int)str[0] & 0x03;
+                break;
+        case 6:
+                unichar = (int)str[0] & 0x01;
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        for (i = 1; i < len; i++) {
+                if (((int)str[i] & 0xc0) != 0x80)
+                        return -EINVAL;
+                unichar <<= 6;
+                unichar |= (int)str[i] & 0x3f;
+        }
+
+        return unichar;
+}
+
+bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
+        const uint8_t *p;
+
+        assert(str);
+
+        for (p = (const uint8_t*) str; length;) {
+                int encoded_len, val;
+
+                encoded_len = utf8_encoded_valid_unichar((const char *) p);
+                if (encoded_len < 0 ||
+                    (size_t) encoded_len > length)
+                        return false;
+
+                val = utf8_encoded_to_unichar((const char*) p);
+                if (val < 0 ||
+                    is_unicode_control(val) ||
+                    (!newline && val == '\n'))
+                        return false;
+
+                length -= encoded_len;
+                p += encoded_len;
+        }
+
+        return true;
+}
+
+const char *utf8_is_valid(const char *str) {
+        const uint8_t *p;
+
+        assert(str);
+
+        for (p = (const uint8_t*) str; *p; ) {
+                int len;
+
+                len = utf8_encoded_valid_unichar((const char *)p);
+                if (len < 0)
+                        return NULL;
+
+                p += len;
+        }
+
+        return str;
+}
+
+char *utf8_escape_invalid(const char *str) {
+        char *p, *s;
+
+        assert(str);
+
+        p = s = malloc(strlen(str) * 4 + 1);
+        if (!p)
+                return NULL;
+
+        while (*str) {
+                int len;
+
+                len = utf8_encoded_valid_unichar(str);
+                if (len > 0) {
+                        s = mempcpy(s, str, len);
+                        str += len;
+                } else {
+                        s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
+                        str += 1;
+                }
+        }
+
+        *s = '\0';
+
+        return p;
+}
+
+char *ascii_is_valid(const char *str) {
+        const char *p;
+
+        assert(str);
+
+        for (p = str; *p; p++)
+                if ((unsigned char) *p >= 128)
+                        return NULL;
+
+        return (char*) str;
+}
+
+char *utf16_to_utf8(const void *s, size_t length) {
+        char *r;
+        const uint8_t *f;
+        uint8_t *t;
+
+        r = new(char, (length*3+1)/2 + 1);
+        if (!r)
+                return NULL;
+
+        t = (uint8_t*) r;
+
+        for (f = s; f < (const uint8_t*) s + length; f += 2) {
+                uint16_t c;
+
+                c = (f[1] << 8) | f[0];
+
+                if (c == 0) {
+                        *t = 0;
+                        return r;
+                } else if (c < 0x80) {
+                        *(t++) = (uint8_t) c;
+                } else if (c < 0x800) {
+                        *(t++) = (uint8_t) (0xc0 | (c >> 6));
+                        *(t++) = (uint8_t) (0x80 | (c & 0x3f));
+                } else {
+                        *(t++) = (uint8_t) (0xe0 | (c >> 12));
+                        *(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+                        *(t++) = (uint8_t) (0x80 | (c & 0x3f));
+                }
+        }
+
+        *t = 0;
+
+        return r;
+}
+
+/* expected size used to encode one unicode char */
+static int utf8_unichar_to_encoded_len(int unichar) {
+
+        if (unichar < 0x80)
+                return 1;
+        if (unichar < 0x800)
+                return 2;
+        if (unichar < 0x10000)
+                return 3;
+        if (unichar < 0x200000)
+                return 4;
+        if (unichar < 0x4000000)
+                return 5;
+
+        return 6;
+}
+
+/* validate one encoded unicode char and return its length */
+int utf8_encoded_valid_unichar(const char *str) {
+        int len, unichar, i;
+
+        assert(str);
+
+        len = utf8_encoded_expected_len(str);
+        if (len == 0)
+                return -EINVAL;
+
+        /* ascii is valid */
+        if (len == 1)
+                return 1;
+
+        /* check if expected encoded chars are available */
+        for (i = 0; i < len; i++)
+                if ((str[i] & 0x80) != 0x80)
+                        return -EINVAL;
+
+        unichar = utf8_encoded_to_unichar(str);
+
+        /* check if encoded length matches encoded value */
+        if (utf8_unichar_to_encoded_len(unichar) != len)
+                return -EINVAL;
+
+        /* check if value has valid range */
+        if (!is_unicode_valid(unichar))
+                return -EINVAL;
+
+        return len;
+}
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/utf8.h b/src/dhcp-manager/systemd-dhcp/src/shared/utf8.h
new file mode 100644
index 00000000..c0879959
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/utf8.h
@@ -0,0 +1,42 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "macro.h"
+
+#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
+
+const char *utf8_is_valid(const char *s) _pure_;
+char *ascii_is_valid(const char *s) _pure_;
+char *utf8_escape_invalid(const char *s);
+
+bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_;
+_pure_ static inline bool utf8_is_printable(const char* str, size_t length) {
+        return utf8_is_printable_newline(str, length, true);
+}
+
+char *utf16_to_utf8(const void *s, size_t length);
+
+int utf8_encoded_valid_unichar(const char *str);
+int utf8_encoded_to_unichar(const char *str);
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/util.c b/src/dhcp-manager/systemd-dhcp/src/shared/util.c
new file mode 100644
index 00000000..05d4eb75
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/util.c
@@ -0,0 +1,7350 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "nm-sd-adapt.h"
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <linux/sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+#include <linux/tiocl.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
+#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/utsname.h>
+#include <pwd.h>
+#include <netinet/ip.h>
+#include <linux/kd.h>
+#include <dlfcn.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <glob.h>
+#include <grp.h>
+#include <sys/mman.h>
+#include <sys/vfs.h>
+#include <sys/mount.h>
+#include <linux/magic.h>
+#include <limits.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <sys/personality.h>
+#include <libgen.h>
+#undef basename
+
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+
+#include "macro.h"
+#include "util.h"
+#if 0 /* NM_IGNORED */
+#include "ioprio.h"
+#include "missing.h"
+#include "log.h"
+#include "strv.h"
+#include "label.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "exit-status.h"
+#include "hashmap.h"
+#include "env-util.h"
+#include "fileio.h"
+#include "device-nodes.h"
+#include "utf8.h"
+#include "gunicode.h"
+#include "virt.h"
+#include "def.h"
+#endif
+
+#if 0 /* NM_IGNORED */
+int saved_argc = 0;
+char **saved_argv = NULL;
+
+static volatile unsigned cached_columns = 0;
+static volatile unsigned cached_lines = 0;
+
+size_t page_size(void) {
+        static thread_local size_t pgsz = 0;
+        long r;
+
+        if (_likely_(pgsz > 0))
+                return pgsz;
+
+        r = sysconf(_SC_PAGESIZE);
+        assert(r > 0);
+
+        pgsz = (size_t) r;
+        return pgsz;
+}
+#endif
+
+bool streq_ptr(const char *a, const char *b) {
+
+        /* Like streq(), but tries to make sense of NULL pointers */
+
+        if (a && b)
+                return streq(a, b);
+
+        if (!a && !b)
+                return true;
+
+        return false;
+}
+
+char* endswith(const char *s, const char *postfix) {
+        size_t sl, pl;
+
+        assert(s);
+        assert(postfix);
+
+        sl = strlen(s);
+        pl = strlen(postfix);
+
+        if (pl == 0)
+                return (char*) s + sl;
+
+        if (sl < pl)
+                return NULL;
+
+        if (memcmp(s + sl - pl, postfix, pl) != 0)
+                return NULL;
+
+        return (char*) s + sl - pl;
+}
+
+char* first_word(const char *s, const char *word) {
+        size_t sl, wl;
+        const char *p;
+
+        assert(s);
+        assert(word);
+
+        /* Checks if the string starts with the specified word, either
+         * followed by NUL or by whitespace. Returns a pointer to the
+         * NUL or the first character after the whitespace. */
+
+        sl = strlen(s);
+        wl = strlen(word);
+
+        if (sl < wl)
+                return NULL;
+
+        if (wl == 0)
+                return (char*) s;
+
+        if (memcmp(s, word, wl) != 0)
+                return NULL;
+
+        p = s + wl;
+        if (*p == 0)
+                return (char*) p;
+
+        if (!strchr(WHITESPACE, *p))
+                return NULL;
+
+        p += strspn(p, WHITESPACE);
+        return (char*) p;
+}
+
+int close_nointr(int fd) {
+        assert(fd >= 0);
+
+        if (close(fd) >= 0)
+                return 0;
+
+        /*
+         * Just ignore EINTR; a retry loop is the wrong thing to do on
+         * Linux.
+         *
+         * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+         * https://bugzilla.gnome.org/show_bug.cgi?id=682819
+         * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
+         * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
+         */
+        if (errno == EINTR)
+                return 0;
+
+        return -errno;
+}
+
+int safe_close(int fd) {
+
+        /*
+         * Like close_nointr() but cannot fail. Guarantees errno is
+         * unchanged. Is a NOP with negative fds passed, and returns
+         * -1, so that it can be used in this syntax:
+         *
+         * fd = safe_close(fd);
+         */
+
+        if (fd >= 0) {
+                PROTECT_ERRNO;
+
+                /* The kernel might return pretty much any error code
+                 * via close(), but the fd will be closed anyway. The
+                 * only condition we want to check for here is whether
+                 * the fd was invalid at all... */
+
+                assert_se(close_nointr(fd) != -EBADF);
+        }
+
+        return -1;
+}
+
+#if 0 /* NM_IGNORED */
+void close_many(const int fds[], unsigned n_fd) {
+        unsigned i;
+
+        assert(fds || n_fd <= 0);
+
+        for (i = 0; i < n_fd; i++)
+                safe_close(fds[i]);
+}
+
+int unlink_noerrno(const char *path) {
+        PROTECT_ERRNO;
+        int r;
+
+        r = unlink(path);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
+int parse_boolean(const char *v) {
+        assert(v);
+
+        if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
+                return 1;
+        else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
+                return 0;
+
+        return -EINVAL;
+}
+
+int parse_pid(const char *s, pid_t* ret_pid) {
+        unsigned long ul = 0;
+        pid_t pid;
+        int r;
+
+        assert(s);
+        assert(ret_pid);
+
+        r = safe_atolu(s, &ul);
+        if (r < 0)
+                return r;
+
+        pid = (pid_t) ul;
+
+        if ((unsigned long) pid != ul)
+                return -ERANGE;
+
+        if (pid <= 0)
+                return -ERANGE;
+
+        *ret_pid = pid;
+        return 0;
+}
+
+int parse_uid(const char *s, uid_t* ret_uid) {
+        unsigned long ul = 0;
+        uid_t uid;
+        int r;
+
+        assert(s);
+        assert(ret_uid);
+
+        r = safe_atolu(s, &ul);
+        if (r < 0)
+                return r;
+
+        uid = (uid_t) ul;
+
+        if ((unsigned long) uid != ul)
+                return -ERANGE;
+
+        /* Some libc APIs use (uid_t) -1 as special placeholder */
+        if (uid == (uid_t) 0xFFFFFFFF)
+                return -ENXIO;
+
+        /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
+        if (uid == (uid_t) 0xFFFF)
+                return -ENXIO;
+
+        *ret_uid = uid;
+        return 0;
+}
+#endif
+
+int safe_atou(const char *s, unsigned *ret_u) {
+        char *x = NULL;
+        unsigned long l;
+
+        assert(s);
+        assert(ret_u);
+
+        errno = 0;
+        l = strtoul(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno > 0 ? -errno : -EINVAL;
+
+        if ((unsigned long) (unsigned) l != l)
+                return -ERANGE;
+
+        *ret_u = (unsigned) l;
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int safe_atoi(const char *s, int *ret_i) {
+        char *x = NULL;
+        long l;
+
+        assert(s);
+        assert(ret_i);
+
+        errno = 0;
+        l = strtol(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno > 0 ? -errno : -EINVAL;
+
+        if ((long) (int) l != l)
+                return -ERANGE;
+
+        *ret_i = (int) l;
+        return 0;
+}
+
+int safe_atou8(const char *s, uint8_t *ret) {
+        char *x = NULL;
+        unsigned long l;
+
+        assert(s);
+        assert(ret);
+
+        errno = 0;
+        l = strtoul(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno > 0 ? -errno : -EINVAL;
+
+        if ((unsigned long) (uint8_t) l != l)
+                return -ERANGE;
+
+        *ret = (uint8_t) l;
+        return 0;
+}
+
+int safe_atou16(const char *s, uint16_t *ret) {
+        char *x = NULL;
+        unsigned long l;
+
+        assert(s);
+        assert(ret);
+
+        errno = 0;
+        l = strtoul(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno > 0 ? -errno : -EINVAL;
+
+        if ((unsigned long) (uint16_t) l != l)
+                return -ERANGE;
+
+        *ret = (uint16_t) l;
+        return 0;
+}
+
+int safe_atoi16(const char *s, int16_t *ret) {
+        char *x = NULL;
+        long l;
+
+        assert(s);
+        assert(ret);
+
+        errno = 0;
+        l = strtol(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno > 0 ? -errno : -EINVAL;
+
+        if ((long) (int16_t) l != l)
+                return -ERANGE;
+
+        *ret = (int16_t) l;
+        return 0;
+}
+
+int safe_atollu(const char *s, long long unsigned *ret_llu) {
+        char *x = NULL;
+        unsigned long long l;
+
+        assert(s);
+        assert(ret_llu);
+
+        errno = 0;
+        l = strtoull(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_llu = l;
+        return 0;
+}
+
+int safe_atolli(const char *s, long long int *ret_lli) {
+        char *x = NULL;
+        long long l;
+
+        assert(s);
+        assert(ret_lli);
+
+        errno = 0;
+        l = strtoll(s, &x, 0);
+
+        if (!x || x == s || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_lli = l;
+        return 0;
+}
+
+int safe_atod(const char *s, double *ret_d) {
+        char *x = NULL;
+        double d = 0;
+
+        assert(s);
+        assert(ret_d);
+
+        RUN_WITH_LOCALE(LC_NUMERIC_MASK, "C") {
+                errno = 0;
+                d = strtod(s, &x);
+        }
+
+        if (!x || x == s || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_d = (double) d;
+        return 0;
+}
+#endif
+
+static size_t strcspn_escaped(const char *s, const char *reject) {
+        bool escaped = false;
+        size_t n;
+
+        for (n=0; s[n]; n++) {
+                if (escaped)
+                        escaped = false;
+                else if (s[n] == '\\')
+                        escaped = true;
+                else if (strchr(reject, s[n]))
+                        break;
+        }
+        /* if s ends in \, return index of previous char */
+        return n - escaped;
+}
+
+/* Split a string into words. */
+const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
+        const char *current;
+
+        current = *state;
+
+        if (!*current) {
+                assert(**state == '\0');
+                return NULL;
+        }
+
+        current += strspn(current, separator);
+        if (!*current) {
+                *state = current;
+                return NULL;
+        }
+
+        if (quoted && strchr("\'\"", *current)) {
+                char quotechars[2] = {*current, '\0'};
+
+                *l = strcspn_escaped(current + 1, quotechars);
+                if (current[*l + 1] == '\0' ||
+                    (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
+                        /* right quote missing or garbage at the end*/
+                        *state = current;
+                        return NULL;
+                }
+                assert(current[*l + 1] == quotechars[0]);
+                *state = current++ + *l + 2;
+        } else if (quoted) {
+                *l = strcspn_escaped(current, separator);
+                *state = current + *l;
+        } else {
+                *l = strcspn(current, separator);
+                *state = current + *l;
+        }
+
+        return current;
+}
+
+#if 0 /* NM_IGNORED */
+int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+        int r;
+        _cleanup_free_ char *line = NULL;
+        long unsigned ppid;
+        const char *p;
+
+        assert(pid >= 0);
+        assert(_ppid);
+
+        if (pid == 0) {
+                *_ppid = getppid();
+                return 0;
+        }
+
+        p = procfs_file_alloca(pid, "stat");
+        r = read_one_line_file(p, &line);
+        if (r < 0)
+                return r;
+
+        /* Let's skip the pid and comm fields. The latter is enclosed
+         * in () but does not escape any () in its value, so let's
+         * skip over it manually */
+
+        p = strrchr(line, ')');
+        if (!p)
+                return -EIO;
+
+        p++;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%lu ", /* ppid */
+                   &ppid) != 1)
+                return -EIO;
+
+        if ((long unsigned) (pid_t) ppid != ppid)
+                return -ERANGE;
+
+        *_ppid = (pid_t) ppid;
+
+        return 0;
+}
+
+int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
+        int r;
+        _cleanup_free_ char *line = NULL;
+        const char *p;
+
+        assert(pid >= 0);
+        assert(st);
+
+        p = procfs_file_alloca(pid, "stat");
+        r = read_one_line_file(p, &line);
+        if (r < 0)
+                return r;
+
+        /* Let's skip the pid and comm fields. The latter is enclosed
+         * in () but does not escape any () in its value, so let's
+         * skip over it manually */
+
+        p = strrchr(line, ')');
+        if (!p)
+                return -EIO;
+
+        p++;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%*d "  /* ppid */
+                   "%*d "  /* pgrp */
+                   "%*d "  /* session */
+                   "%*d "  /* tty_nr */
+                   "%*d "  /* tpgid */
+                   "%*u "  /* flags */
+                   "%*u "  /* minflt */
+                   "%*u "  /* cminflt */
+                   "%*u "  /* majflt */
+                   "%*u "  /* cmajflt */
+                   "%*u "  /* utime */
+                   "%*u "  /* stime */
+                   "%*d "  /* cutime */
+                   "%*d "  /* cstime */
+                   "%*d "  /* priority */
+                   "%*d "  /* nice */
+                   "%*d "  /* num_threads */
+                   "%*d "  /* itrealvalue */
+                   "%llu "  /* starttime */,
+                   st) != 1)
+                return -EIO;
+
+        return 0;
+}
+#endif
+
+int fchmod_umask(int fd, mode_t m) {
+        mode_t u;
+        int r;
+
+        u = umask(0777);
+        r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
+        umask(u);
+
+        return r;
+}
+
+char *truncate_nl(char *s) {
+        assert(s);
+
+        s[strcspn(s, NEWLINE)] = 0;
+        return s;
+}
+
+#if 0 /* NM_IGNORED */
+int get_process_state(pid_t pid) {
+        const char *p;
+        char state;
+        int r;
+        _cleanup_free_ char *line = NULL;
+
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "stat");
+        r = read_one_line_file(p, &line);
+        if (r < 0)
+                return r;
+
+        p = strrchr(line, ')');
+        if (!p)
+                return -EIO;
+
+        p++;
+
+        if (sscanf(p, " %c", &state) != 1)
+                return -EIO;
+
+        return (unsigned char) state;
+}
+
+int get_process_comm(pid_t pid, char **name) {
+        const char *p;
+        int r;
+
+        assert(name);
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "comm");
+
+        r = read_one_line_file(p, name);
+        if (r == -ENOENT)
+                return -ESRCH;
+
+        return r;
+}
+
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+        _cleanup_fclose_ FILE *f = NULL;
+        char *r = NULL, *k;
+        const char *p;
+        int c;
+
+        assert(line);
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "cmdline");
+
+        f = fopen(p, "re");
+        if (!f)
+                return -errno;
+
+        if (max_length == 0) {
+                size_t len = 0, allocated = 0;
+
+                while ((c = getc(f)) != EOF) {
+
+                        if (!GREEDY_REALLOC(r, allocated, len+2)) {
+                                free(r);
+                                return -ENOMEM;
+                        }
+
+                        r[len++] = isprint(c) ? c : ' ';
+                }
+
+                if (len > 0)
+                        r[len-1] = 0;
+
+        } else {
+                bool space = false;
+                size_t left;
+
+                r = new(char, max_length);
+                if (!r)
+                        return -ENOMEM;
+
+                k = r;
+                left = max_length;
+                while ((c = getc(f)) != EOF) {
+
+                        if (isprint(c)) {
+                                if (space) {
+                                        if (left <= 4)
+                                                break;
+
+                                        *(k++) = ' ';
+                                        left--;
+                                        space = false;
+                                }
+
+                                if (left <= 4)
+                                        break;
+
+                                *(k++) = (char) c;
+                                left--;
+                        }  else
+                                space = true;
+                }
+
+                if (left <= 4) {
+                        size_t n = MIN(left-1, 3U);
+                        memcpy(k, "...", n);
+                        k[n] = 0;
+                } else
+                        *k = 0;
+        }
+
+        /* Kernel threads have no argv[] */
+        if (isempty(r)) {
+                _cleanup_free_ char *t = NULL;
+                int h;
+
+                free(r);
+
+                if (!comm_fallback)
+                        return -ENOENT;
+
+                h = get_process_comm(pid, &t);
+                if (h < 0)
+                        return h;
+
+                r = strjoin("[", t, "]", NULL);
+                if (!r)
+                        return -ENOMEM;
+        }
+
+        *line = r;
+        return 0;
+}
+
+int is_kernel_thread(pid_t pid) {
+        const char *p;
+        size_t count;
+        char c;
+        bool eof;
+        FILE *f;
+
+        if (pid == 0)
+                return 0;
+
+        assert(pid > 0);
+
+        p = procfs_file_alloca(pid, "cmdline");
+        f = fopen(p, "re");
+        if (!f)
+                return -errno;
+
+        count = fread(&c, 1, 1, f);
+        eof = feof(f);
+        fclose(f);
+
+        /* Kernel threads have an empty cmdline */
+
+        if (count <= 0)
+                return eof ? 1 : -errno;
+
+        return 0;
+}
+
+int get_process_capeff(pid_t pid, char **capeff) {
+        const char *p;
+
+        assert(capeff);
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "status");
+
+        return get_status_field(p, "\nCapEff:", capeff);
+}
+
+int get_process_exe(pid_t pid, char **name) {
+        const char *p;
+        char *d;
+        int r;
+
+        assert(pid >= 0);
+        assert(name);
+
+        p = procfs_file_alloca(pid, "exe");
+
+        r = readlink_malloc(p, name);
+        if (r < 0)
+                return r == -ENOENT ? -ESRCH : r;
+
+        d = endswith(*name, " (deleted)");
+        if (d)
+                *d = '\0';
+
+        return 0;
+}
+
+static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
+        _cleanup_fclose_ FILE *f = NULL;
+        char line[LINE_MAX];
+        const char *p;
+
+        assert(field);
+        assert(uid);
+
+        if (pid == 0)
+                return getuid();
+
+        p = procfs_file_alloca(pid, "status");
+        f = fopen(p, "re");
+        if (!f)
+                return -errno;
+
+        FOREACH_LINE(line, f, return -errno) {
+                char *l;
+
+                l = strstrip(line);
+
+                if (startswith(l, field)) {
+                        l += strlen(field);
+                        l += strspn(l, WHITESPACE);
+
+                        l[strcspn(l, WHITESPACE)] = 0;
+
+                        return parse_uid(l, uid);
+                }
+        }
+
+        return -EIO;
+}
+
+int get_process_uid(pid_t pid, uid_t *uid) {
+        return get_process_id(pid, "Uid:", uid);
+}
+
+int get_process_gid(pid_t pid, gid_t *gid) {
+        assert_cc(sizeof(uid_t) == sizeof(gid_t));
+        return get_process_id(pid, "Gid:", gid);
+}
+#endif
+
+char *strnappend(const char *s, const char *suffix, size_t b) {
+        size_t a;
+        char *r;
+
+        if (!s && !suffix)
+                return strdup("");
+
+        if (!s)
+                return strndup(suffix, b);
+
+        if (!suffix)
+                return strdup(s);
+
+        assert(s);
+        assert(suffix);
+
+        a = strlen(s);
+        if (b > ((size_t) -1) - a)
+                return NULL;
+
+        r = new(char, a+b+1);
+        if (!r)
+                return NULL;
+
+        memcpy(r, s, a);
+        memcpy(r+a, suffix, b);
+        r[a+b] = 0;
+
+        return r;
+}
+
+char *strappend(const char *s, const char *suffix) {
+        return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+}
+
+#if 0 /* NM_IGNORED */
+int readlinkat_malloc(int fd, const char *p, char **ret) {
+        size_t l = 100;
+        int r;
+
+        assert(p);
+        assert(ret);
+
+        for (;;) {
+                char *c;
+                ssize_t n;
+
+                c = new(char, l);
+                if (!c)
+                        return -ENOMEM;
+
+                n = readlinkat(fd, p, c, l-1);
+                if (n < 0) {
+                        r = -errno;
+                        free(c);
+                        return r;
+                }
+
+                if ((size_t) n < l-1) {
+                        c[n] = 0;
+                        *ret = c;
+                        return 0;
+                }
+
+                free(c);
+                l *= 2;
+        }
+}
+
+int readlink_malloc(const char *p, char **ret) {
+        return readlinkat_malloc(AT_FDCWD, p, ret);
+}
+
+int readlink_value(const char *p, char **ret) {
+        _cleanup_free_ char *link = NULL;
+        char *value;
+        int r;
+
+        r = readlink_malloc(p, &link);
+        if (r < 0)
+                return r;
+
+        value = basename(link);
+        if (!value)
+                return -ENOENT;
+
+        value = strdup(value);
+        if (!value)
+                return -ENOMEM;
+
+        *ret = value;
+
+        return 0;
+}
+
+int readlink_and_make_absolute(const char *p, char **r) {
+        _cleanup_free_ char *target = NULL;
+        char *k;
+        int j;
+
+        assert(p);
+        assert(r);
+
+        j = readlink_malloc(p, &target);
+        if (j < 0)
+                return j;
+
+        k = file_in_same_dir(p, target);
+        if (!k)
+                return -ENOMEM;
+
+        *r = k;
+        return 0;
+}
+
+int readlink_and_canonicalize(const char *p, char **r) {
+        char *t, *s;
+        int j;
+
+        assert(p);
+        assert(r);
+
+        j = readlink_and_make_absolute(p, &t);
+        if (j < 0)
+                return j;
+
+        s = canonicalize_file_name(t);
+        if (s) {
+                free(t);
+                *r = s;
+        } else
+                *r = t;
+
+        path_kill_slashes(*r);
+
+        return 0;
+}
+
+int reset_all_signal_handlers(void) {
+        int sig, r = 0;
+
+        for (sig = 1; sig < _NSIG; sig++) {
+                struct sigaction sa = {
+                        .sa_handler = SIG_DFL,
+                        .sa_flags = SA_RESTART,
+                };
+
+                /* These two cannot be caught... */
+                if (sig == SIGKILL || sig == SIGSTOP)
+                        continue;
+
+                /* On Linux the first two RT signals are reserved by
+                 * glibc, and sigaction() will return EINVAL for them. */
+                if ((sigaction(sig, &sa, NULL) < 0))
+                        if (errno != EINVAL && r == 0)
+                                r = -errno;
+        }
+
+        return r;
+}
+
+int reset_signal_mask(void) {
+        sigset_t ss;
+
+        if (sigemptyset(&ss) < 0)
+                return -errno;
+
+        if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
+                return -errno;
+
+        return 0;
+}
+#endif
+
+char *strstrip(char *s) {
+        char *e;
+
+        /* Drops trailing whitespace. Modifies the string in
+         * place. Returns pointer to first non-space character */
+
+        s += strspn(s, WHITESPACE);
+
+        for (e = strchr(s, 0); e > s; e --)
+                if (!strchr(WHITESPACE, e[-1]))
+                        break;
+
+        *e = 0;
+
+        return s;
+}
+
+#if 0 /* NM_IGNORED */
+char *delete_chars(char *s, const char *bad) {
+        char *f, *t;
+
+        /* Drops all whitespace, regardless where in the string */
+
+        for (f = s, t = s; *f; f++) {
+                if (strchr(bad, *f))
+                        continue;
+
+                *(t++) = *f;
+        }
+
+        *t = 0;
+
+        return s;
+}
+
+char *file_in_same_dir(const char *path, const char *filename) {
+        char *e, *r;
+        size_t k;
+
+        assert(path);
+        assert(filename);
+
+        /* This removes the last component of path and appends
+         * filename, unless the latter is absolute anyway or the
+         * former isn't */
+
+        if (path_is_absolute(filename))
+                return strdup(filename);
+
+        if (!(e = strrchr(path, '/')))
+                return strdup(filename);
+
+        k = strlen(filename);
+        if (!(r = new(char, e-path+1+k+1)))
+                return NULL;
+
+        memcpy(r, path, e-path+1);
+        memcpy(r+(e-path)+1, filename, k+1);
+
+        return r;
+}
+
+int rmdir_parents(const char *path, const char *stop) {
+        size_t l;
+        int r = 0;
+
+        assert(path);
+        assert(stop);
+
+        l = strlen(path);
+
+        /* Skip trailing slashes */
+        while (l > 0 && path[l-1] == '/')
+                l--;
+
+        while (l > 0) {
+                char *t;
+
+                /* Skip last component */
+                while (l > 0 && path[l-1] != '/')
+                        l--;
+
+                /* Skip trailing slashes */
+                while (l > 0 && path[l-1] == '/')
+                        l--;
+
+                if (l <= 0)
+                        break;
+
+                if (!(t = strndup(path, l)))
+                        return -ENOMEM;
+
+                if (path_startswith(stop, t)) {
+                        free(t);
+                        return 0;
+                }
+
+                r = rmdir(t);
+                free(t);
+
+                if (r < 0)
+                        if (errno != ENOENT)
+                                return -errno;
+        }
+
+        return 0;
+}
+#endif
+
+char hexchar(int x) {
+        static const char table[16] = "0123456789abcdef";
+
+        return table[x & 15];
+}
+
+int unhexchar(char c) {
+
+        if (c >= '0' && c <= '9')
+                return c - '0';
+
+        if (c >= 'a' && c <= 'f')
+                return c - 'a' + 10;
+
+        if (c >= 'A' && c <= 'F')
+                return c - 'A' + 10;
+
+        return -EINVAL;
+}
+
+char *hexmem(const void *p, size_t l) {
+        char *r, *z;
+        const uint8_t *x;
+
+        z = r = malloc(l * 2 + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < (const uint8_t*) p + l; x++) {
+                *(z++) = hexchar(*x >> 4);
+                *(z++) = hexchar(*x & 15);
+        }
+
+        *z = 0;
+        return r;
+}
+
+void *unhexmem(const char *p, size_t l) {
+        uint8_t *r, *z;
+        const char *x;
+
+        assert(p);
+
+        z = r = malloc((l + 1) / 2 + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < p + l; x += 2) {
+                int a, b;
+
+                a = unhexchar(x[0]);
+                if (x+1 < p + l)
+                        b = unhexchar(x[1]);
+                else
+                        b = 0;
+
+                *(z++) = (uint8_t) a << 4 | (uint8_t) b;
+        }
+
+        *z = 0;
+        return r;
+}
+
+char octchar(int x) {
+        return '0' + (x & 7);
+}
+
+int unoctchar(char c) {
+
+        if (c >= '0' && c <= '7')
+                return c - '0';
+
+        return -EINVAL;
+}
+
+char decchar(int x) {
+        return '0' + (x % 10);
+}
+
+int undecchar(char c) {
+
+        if (c >= '0' && c <= '9')
+                return c - '0';
+
+        return -EINVAL;
+}
+
+char *cescape(const char *s) {
+        char *r, *t;
+        const char *f;
+
+        assert(s);
+
+        /* Does C style string escaping. */
+
+        r = new(char, strlen(s)*4 + 1);
+        if (!r)
+                return NULL;
+
+        for (f = s, t = r; *f; f++)
+
+                switch (*f) {
+
+                case '\a':
+                        *(t++) = '\\';
+                        *(t++) = 'a';
+                        break;
+                case '\b':
+                        *(t++) = '\\';
+                        *(t++) = 'b';
+                        break;
+                case '\f':
+                        *(t++) = '\\';
+                        *(t++) = 'f';
+                        break;
+                case '\n':
+                        *(t++) = '\\';
+                        *(t++) = 'n';
+                        break;
+                case '\r':
+                        *(t++) = '\\';
+                        *(t++) = 'r';
+                        break;
+                case '\t':
+                        *(t++) = '\\';
+                        *(t++) = 't';
+                        break;
+                case '\v':
+                        *(t++) = '\\';
+                        *(t++) = 'v';
+                        break;
+                case '\\':
+                        *(t++) = '\\';
+                        *(t++) = '\\';
+                        break;
+                case '"':
+                        *(t++) = '\\';
+                        *(t++) = '"';
+                        break;
+                case '\'':
+                        *(t++) = '\\';
+                        *(t++) = '\'';
+                        break;
+
+                default:
+                        /* For special chars we prefer octal over
+                         * hexadecimal encoding, simply because glib's
+                         * g_strescape() does the same */
+                        if ((*f < ' ') || (*f >= 127)) {
+                                *(t++) = '\\';
+                                *(t++) = octchar((unsigned char) *f >> 6);
+                                *(t++) = octchar((unsigned char) *f >> 3);
+                                *(t++) = octchar((unsigned char) *f);
+                        } else
+                                *(t++) = *f;
+                        break;
+                }
+
+        *t = 0;
+
+        return r;
+}
+
+char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
+        char *r, *t;
+        const char *f;
+        size_t pl;
+
+        assert(s);
+
+        /* Undoes C style string escaping, and optionally prefixes it. */
+
+        pl = prefix ? strlen(prefix) : 0;
+
+        r = new(char, pl+length+1);
+        if (!r)
+                return NULL;
+
+        if (prefix)
+                memcpy(r, prefix, pl);
+
+        for (f = s, t = r + pl; f < s + length; f++) {
+
+                if (*f != '\\') {
+                        *(t++) = *f;
+                        continue;
+                }
+
+                f++;
+
+                switch (*f) {
+
+                case 'a':
+                        *(t++) = '\a';
+                        break;
+                case 'b':
+                        *(t++) = '\b';
+                        break;
+                case 'f':
+                        *(t++) = '\f';
+                        break;
+                case 'n':
+                        *(t++) = '\n';
+                        break;
+                case 'r':
+                        *(t++) = '\r';
+                        break;
+                case 't':
+                        *(t++) = '\t';
+                        break;
+                case 'v':
+                        *(t++) = '\v';
+                        break;
+                case '\\':
+                        *(t++) = '\\';
+                        break;
+                case '"':
+                        *(t++) = '"';
+                        break;
+                case '\'':
+                        *(t++) = '\'';
+                        break;
+
+                case 's':
+                        /* This is an extension of the XDG syntax files */
+                        *(t++) = ' ';
+                        break;
+
+                case 'x': {
+                        /* hexadecimal encoding */
+                        int a, b;
+
+                        a = unhexchar(f[1]);
+                        b = unhexchar(f[2]);
+
+                        if (a < 0 || b < 0 || (a == 0 && b == 0)) {
+                                /* Invalid escape code, let's take it literal then */
+                                *(t++) = '\\';
+                                *(t++) = 'x';
+                        } else {
+                                *(t++) = (char) ((a << 4) | b);
+                                f += 2;
+                        }
+
+                        break;
+                }
+
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7': {
+                        /* octal encoding */
+                        int a, b, c;
+
+                        a = unoctchar(f[0]);
+                        b = unoctchar(f[1]);
+                        c = unoctchar(f[2]);
+
+                        if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
+                                /* Invalid escape code, let's take it literal then */
+                                *(t++) = '\\';
+                                *(t++) = f[0];
+                        } else {
+                                *(t++) = (char) ((a << 6) | (b << 3) | c);
+                                f += 2;
+                        }
+
+                        break;
+                }
+
+                case 0:
+                        /* premature end of string.*/
+                        *(t++) = '\\';
+                        goto finish;
+
+                default:
+                        /* Invalid escape code, let's take it literal then */
+                        *(t++) = '\\';
+                        *(t++) = *f;
+                        break;
+                }
+        }
+
+finish:
+        *t = 0;
+        return r;
+}
+
+char *cunescape_length(const char *s, size_t length) {
+        return cunescape_length_with_prefix(s, length, NULL);
+}
+
+#if 0 /* NM_IGNORED */
+char *cunescape(const char *s) {
+        assert(s);
+
+        return cunescape_length(s, strlen(s));
+}
+
+char *xescape(const char *s, const char *bad) {
+        char *r, *t;
+        const char *f;
+
+        /* Escapes all chars in bad, in addition to \ and all special
+         * chars, in \xFF style escaping. May be reversed with
+         * cunescape. */
+
+        r = new(char, strlen(s) * 4 + 1);
+        if (!r)
+                return NULL;
+
+        for (f = s, t = r; *f; f++) {
+
+                if ((*f < ' ') || (*f >= 127) ||
+                    (*f == '\\') || strchr(bad, *f)) {
+                        *(t++) = '\\';
+                        *(t++) = 'x';
+                        *(t++) = hexchar(*f >> 4);
+                        *(t++) = hexchar(*f);
+                } else
+                        *(t++) = *f;
+        }
+
+        *t = 0;
+
+        return r;
+}
+
+char *ascii_strlower(char *t) {
+        char *p;
+
+        assert(t);
+
+        for (p = t; *p; p++)
+                if (*p >= 'A' && *p <= 'Z')
+                        *p = *p - 'A' + 'a';
+
+        return t;
+}
+
+_pure_ static bool ignore_file_allow_backup(const char *filename) {
+        assert(filename);
+
+        return
+                filename[0] == '.' ||
+                streq(filename, "lost+found") ||
+                streq(filename, "aquota.user") ||
+                streq(filename, "aquota.group") ||
+                endswith(filename, ".rpmnew") ||
+                endswith(filename, ".rpmsave") ||
+                endswith(filename, ".rpmorig") ||
+                endswith(filename, ".dpkg-old") ||
+                endswith(filename, ".dpkg-new") ||
+                endswith(filename, ".dpkg-tmp") ||
+                endswith(filename, ".swp");
+}
+
+bool ignore_file(const char *filename) {
+        assert(filename);
+
+        if (endswith(filename, "~"))
+                return true;
+
+        return ignore_file_allow_backup(filename);
+}
+
+int fd_nonblock(int fd, bool nonblock) {
+        int flags, nflags;
+
+        assert(fd >= 0);
+
+        flags = fcntl(fd, F_GETFL, 0);
+        if (flags < 0)
+                return -errno;
+
+        if (nonblock)
+                nflags = flags | O_NONBLOCK;
+        else
+                nflags = flags & ~O_NONBLOCK;
+
+        if (nflags == flags)
+                return 0;
+
+        if (fcntl(fd, F_SETFL, nflags) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int fd_cloexec(int fd, bool cloexec) {
+        int flags, nflags;
+
+        assert(fd >= 0);
+
+        flags = fcntl(fd, F_GETFD, 0);
+        if (flags < 0)
+                return -errno;
+
+        if (cloexec)
+                nflags = flags | FD_CLOEXEC;
+        else
+                nflags = flags & ~FD_CLOEXEC;
+
+        if (nflags == flags)
+                return 0;
+
+        if (fcntl(fd, F_SETFD, nflags) < 0)
+                return -errno;
+
+        return 0;
+}
+
+_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
+        unsigned i;
+
+        assert(n_fdset == 0 || fdset);
+
+        for (i = 0; i < n_fdset; i++)
+                if (fdset[i] == fd)
+                        return true;
+
+        return false;
+}
+
+int close_all_fds(const int except[], unsigned n_except) {
+        _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
+        int r = 0;
+
+        assert(n_except == 0 || except);
+
+        d = opendir("/proc/self/fd");
+        if (!d) {
+                int fd;
+                struct rlimit rl;
+
+                /* When /proc isn't available (for example in chroots)
+                 * the fallback is brute forcing through the fd
+                 * table */
+
+                assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
+                for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
+
+                        if (fd_in_set(fd, except, n_except))
+                                continue;
+
+                        if (close_nointr(fd) < 0)
+                                if (errno != EBADF && r == 0)
+                                        r = -errno;
+                }
+
+                return r;
+        }
+
+        while ((de = readdir(d))) {
+                int fd = -1;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                if (safe_atoi(de->d_name, &fd) < 0)
+                        /* Let's better ignore this, just in case */
+                        continue;
+
+                if (fd < 3)
+                        continue;
+
+                if (fd == dirfd(d))
+                        continue;
+
+                if (fd_in_set(fd, except, n_except))
+                        continue;
+
+                if (close_nointr(fd) < 0) {
+                        /* Valgrind has its own FD and doesn't want to have it closed */
+                        if (errno != EBADF && r == 0)
+                                r = -errno;
+                }
+        }
+
+        return r;
+}
+#endif
+
+bool chars_intersect(const char *a, const char *b) {
+        const char *p;
+
+        /* Returns true if any of the chars in a are in b. */
+        for (p = a; *p; p++)
+                if (strchr(b, *p))
+                        return true;
+
+        return false;
+}
+
+#if 0 /* NM_IGNORED */
+bool fstype_is_network(const char *fstype) {
+        static const char table[] =
+                "cifs\0"
+                "smbfs\0"
+                "sshfs\0"
+                "ncpfs\0"
+                "ncp\0"
+                "nfs\0"
+                "nfs4\0"
+                "gfs\0"
+                "gfs2\0"
+                "glusterfs\0";
+
+        const char *x;
+
+        x = startswith(fstype, "fuse.");
+        if (x)
+                fstype = x;
+
+        return nulstr_contains(table, fstype);
+}
+
+int chvt(int vt) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        if (vt < 0) {
+                int tiocl[2] = {
+                        TIOCL_GETKMSGREDIRECT,
+                        0
+                };
+
+                if (ioctl(fd, TIOCLINUX, tiocl) < 0)
+                        return -errno;
+
+                vt = tiocl[0] <= 0 ? 1 : tiocl[0];
+        }
+
+        if (ioctl(fd, VT_ACTIVATE, vt) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
+        struct termios old_termios, new_termios;
+        char c, line[LINE_MAX];
+
+        assert(f);
+        assert(ret);
+
+        if (tcgetattr(fileno(f), &old_termios) >= 0) {
+                new_termios = old_termios;
+
+                new_termios.c_lflag &= ~ICANON;
+                new_termios.c_cc[VMIN] = 1;
+                new_termios.c_cc[VTIME] = 0;
+
+                if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
+                        size_t k;
+
+                        if (t != USEC_INFINITY) {
+                                if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
+                                        tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+                                        return -ETIMEDOUT;
+                                }
+                        }
+
+                        k = fread(&c, 1, 1, f);
+
+                        tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+
+                        if (k <= 0)
+                                return -EIO;
+
+                        if (need_nl)
+                                *need_nl = c != '\n';
+
+                        *ret = c;
+                        return 0;
+                }
+        }
+
+        if (t != USEC_INFINITY) {
+                if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
+                        return -ETIMEDOUT;
+        }
+
+        errno = 0;
+        if (!fgets(line, sizeof(line), f))
+                return errno ? -errno : -EIO;
+
+        truncate_nl(line);
+
+        if (strlen(line) != 1)
+                return -EBADMSG;
+
+        if (need_nl)
+                *need_nl = false;
+
+        *ret = line[0];
+        return 0;
+}
+
+int ask_char(char *ret, const char *replies, const char *text, ...) {
+        int r;
+
+        assert(ret);
+        assert(replies);
+        assert(text);
+
+        for (;;) {
+                va_list ap;
+                char c;
+                bool need_nl = true;
+
+                if (on_tty())
+                        fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+                va_start(ap, text);
+                vprintf(text, ap);
+                va_end(ap);
+
+                if (on_tty())
+                        fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+                fflush(stdout);
+
+                r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
+                if (r < 0) {
+
+                        if (r == -EBADMSG) {
+                                puts("Bad input, please try again.");
+                                continue;
+                        }
+
+                        putchar('\n');
+                        return r;
+                }
+
+                if (need_nl)
+                        putchar('\n');
+
+                if (strchr(replies, c)) {
+                        *ret = c;
+                        return 0;
+                }
+
+                puts("Read unexpected character, please try again.");
+        }
+}
+
+int ask_string(char **ret, const char *text, ...) {
+        assert(ret);
+        assert(text);
+
+        for (;;) {
+                char line[LINE_MAX];
+                va_list ap;
+
+                if (on_tty())
+                        fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+                va_start(ap, text);
+                vprintf(text, ap);
+                va_end(ap);
+
+                if (on_tty())
+                        fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+                fflush(stdout);
+
+                errno = 0;
+                if (!fgets(line, sizeof(line), stdin))
+                        return errno ? -errno : -EIO;
+
+                if (!endswith(line, "\n"))
+                        putchar('\n');
+                else {
+                        char *s;
+
+                        if (isempty(line))
+                                continue;
+
+                        truncate_nl(line);
+                        s = strdup(line);
+                        if (!s)
+                                return -ENOMEM;
+
+                        *ret = s;
+                        return 0;
+                }
+        }
+}
+
+int reset_terminal_fd(int fd, bool switch_to_text) {
+        struct termios termios;
+        int r = 0;
+
+        /* Set terminal to some sane defaults */
+
+        assert(fd >= 0);
+
+        /* We leave locked terminal attributes untouched, so that
+         * Plymouth may set whatever it wants to set, and we don't
+         * interfere with that. */
+
+        /* Disable exclusive mode, just in case */
+        ioctl(fd, TIOCNXCL);
+
+        /* Switch to text mode */
+        if (switch_to_text)
+                ioctl(fd, KDSETMODE, KD_TEXT);
+
+        /* Enable console unicode mode */
+        ioctl(fd, KDSKBMODE, K_UNICODE);
+
+        if (tcgetattr(fd, &termios) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        /* We only reset the stuff that matters to the software. How
+         * hardware is set up we don't touch assuming that somebody
+         * else will do that for us */
+
+        termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
+        termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
+        termios.c_oflag |= ONLCR;
+        termios.c_cflag |= CREAD;
+        termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
+
+        termios.c_cc[VINTR]    =   03;  /* ^C */
+        termios.c_cc[VQUIT]    =  034;  /* ^\ */
+        termios.c_cc[VERASE]   = 0177;
+        termios.c_cc[VKILL]    =  025;  /* ^X */
+        termios.c_cc[VEOF]     =   04;  /* ^D */
+        termios.c_cc[VSTART]   =  021;  /* ^Q */
+        termios.c_cc[VSTOP]    =  023;  /* ^S */
+        termios.c_cc[VSUSP]    =  032;  /* ^Z */
+        termios.c_cc[VLNEXT]   =  026;  /* ^V */
+        termios.c_cc[VWERASE]  =  027;  /* ^W */
+        termios.c_cc[VREPRINT] =  022;  /* ^R */
+        termios.c_cc[VEOL]     =    0;
+        termios.c_cc[VEOL2]    =    0;
+
+        termios.c_cc[VTIME]  = 0;
+        termios.c_cc[VMIN]   = 1;
+
+        if (tcsetattr(fd, TCSANOW, &termios) < 0)
+                r = -errno;
+
+finish:
+        /* Just in case, flush all crap out */
+        tcflush(fd, TCIOFLUSH);
+
+        return r;
+}
+
+int reset_terminal(const char *name) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        return reset_terminal_fd(fd, true);
+}
+
+int open_terminal(const char *name, int mode) {
+        int fd, r;
+        unsigned c = 0;
+
+        /*
+         * If a TTY is in the process of being closed opening it might
+         * cause EIO. This is horribly awful, but unlikely to be
+         * changed in the kernel. Hence we work around this problem by
+         * retrying a couple of times.
+         *
+         * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
+         */
+
+        assert(!(mode & O_CREAT));
+
+        for (;;) {
+                fd = open(name, mode, 0);
+                if (fd >= 0)
+                        break;
+
+                if (errno != EIO)
+                        return -errno;
+
+                /* Max 1s in total */
+                if (c >= 20)
+                        return -errno;
+
+                usleep(50 * USEC_PER_MSEC);
+                c++;
+        }
+
+        r = isatty(fd);
+        if (r < 0) {
+                safe_close(fd);
+                return -errno;
+        }
+
+        if (!r) {
+                safe_close(fd);
+                return -ENOTTY;
+        }
+
+        return fd;
+}
+
+int flush_fd(int fd) {
+        struct pollfd pollfd = {
+                .fd = fd,
+                .events = POLLIN,
+        };
+
+        for (;;) {
+                char buf[LINE_MAX];
+                ssize_t l;
+                int r;
+
+                r = poll(&pollfd, 1, 0);
+                if (r < 0) {
+                        if (errno == EINTR)
+                                continue;
+
+                        return -errno;
+
+                } else if (r == 0)
+                        return 0;
+
+                l = read(fd, buf, sizeof(buf));
+                if (l < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        if (errno == EAGAIN)
+                                return 0;
+
+                        return -errno;
+                } else if (l == 0)
+                        return 0;
+        }
+}
+
+int acquire_terminal(
+                const char *name,
+                bool fail,
+                bool force,
+                bool ignore_tiocstty_eperm,
+                usec_t timeout) {
+
+        int fd = -1, notify = -1, r = 0, wd = -1;
+        usec_t ts = 0;
+
+        assert(name);
+
+        /* We use inotify to be notified when the tty is closed. We
+         * create the watch before checking if we can actually acquire
+         * it, so that we don't lose any event.
+         *
+         * Note: strictly speaking this actually watches for the
+         * device being closed, it does *not* really watch whether a
+         * tty loses its controlling process. However, unless some
+         * rogue process uses TIOCNOTTY on /dev/tty *after* closing
+         * its tty otherwise this will not become a problem. As long
+         * as the administrator makes sure not configure any service
+         * on the same tty as an untrusted user this should not be a
+         * problem. (Which he probably should not do anyway.) */
+
+        if (timeout != USEC_INFINITY)
+                ts = now(CLOCK_MONOTONIC);
+
+        if (!fail && !force) {
+                notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
+                if (notify < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+
+                wd = inotify_add_watch(notify, name, IN_CLOSE);
+                if (wd < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+        }
+
+        for (;;) {
+                struct sigaction sa_old, sa_new = {
+                        .sa_handler = SIG_IGN,
+                        .sa_flags = SA_RESTART,
+                };
+
+                if (notify >= 0) {
+                        r = flush_fd(notify);
+                        if (r < 0)
+                                goto fail;
+                }
+
+                /* We pass here O_NOCTTY only so that we can check the return
+                 * value TIOCSCTTY and have a reliable way to figure out if we
+                 * successfully became the controlling process of the tty */
+                fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+                if (fd < 0)
+                        return fd;
+
+                /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+                 * if we already own the tty. */
+                assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+                /* First, try to get the tty */
+                if (ioctl(fd, TIOCSCTTY, force) < 0)
+                        r = -errno;
+
+                assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+                /* Sometimes it makes sense to ignore TIOCSCTTY
+                 * returning EPERM, i.e. when very likely we already
+                 * are have this controlling terminal. */
+                if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
+                        r = 0;
+
+                if (r < 0 && (force || fail || r != -EPERM)) {
+                        goto fail;
+                }
+
+                if (r >= 0)
+                        break;
+
+                assert(!fail);
+                assert(!force);
+                assert(notify >= 0);
+
+                for (;;) {
+                        uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
+                        ssize_t l;
+                        struct inotify_event *e;
+
+                        if (timeout != USEC_INFINITY) {
+                                usec_t n;
+
+                                n = now(CLOCK_MONOTONIC);
+                                if (ts + timeout < n) {
+                                        r = -ETIMEDOUT;
+                                        goto fail;
+                                }
+
+                                r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+                                if (r < 0)
+                                        goto fail;
+
+                                if (r == 0) {
+                                        r = -ETIMEDOUT;
+                                        goto fail;
+                                }
+                        }
+
+                        l = read(notify, inotify_buffer, sizeof(inotify_buffer));
+                        if (l < 0) {
+
+                                if (errno == EINTR || errno == EAGAIN)
+                                        continue;
+
+                                r = -errno;
+                                goto fail;
+                        }
+
+                        e = (struct inotify_event*) inotify_buffer;
+
+                        while (l > 0) {
+                                size_t step;
+
+                                if (e->wd != wd || !(e->mask & IN_CLOSE)) {
+                                        r = -EIO;
+                                        goto fail;
+                                }
+
+                                step = sizeof(struct inotify_event) + e->len;
+                                assert(step <= (size_t) l);
+
+                                e = (struct inotify_event*) ((uint8_t*) e + step);
+                                l -= step;
+                        }
+
+                        break;
+                }
+
+                /* We close the tty fd here since if the old session
+                 * ended our handle will be dead. It's important that
+                 * we do this after sleeping, so that we don't enter
+                 * an endless loop. */
+                fd = safe_close(fd);
+        }
+
+        safe_close(notify);
+
+        r = reset_terminal_fd(fd, true);
+        if (r < 0)
+                log_warning("Failed to reset terminal: %s", strerror(-r));
+
+        return fd;
+
+fail:
+        safe_close(fd);
+        safe_close(notify);
+
+        return r;
+}
+
+int release_terminal(void) {
+        static const struct sigaction sa_new = {
+                .sa_handler = SIG_IGN,
+                .sa_flags = SA_RESTART,
+        };
+
+        _cleanup_close_ int fd = -1;
+        struct sigaction sa_old;
+        int r = 0;
+
+        fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+         * by our own TIOCNOTTY */
+        assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+        if (ioctl(fd, TIOCNOTTY) < 0)
+                r = -errno;
+
+        assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+        return r;
+}
+
+int sigaction_many(const struct sigaction *sa, ...) {
+        va_list ap;
+        int r = 0, sig;
+
+        va_start(ap, sa);
+        while ((sig = va_arg(ap, int)) > 0)
+                if (sigaction(sig, sa, NULL) < 0)
+                        r = -errno;
+        va_end(ap);
+
+        return r;
+}
+
+int ignore_signals(int sig, ...) {
+        struct sigaction sa = {
+                .sa_handler = SIG_IGN,
+                .sa_flags = SA_RESTART,
+        };
+        va_list ap;
+        int r = 0;
+
+        if (sigaction(sig, &sa, NULL) < 0)
+                r = -errno;
+
+        va_start(ap, sig);
+        while ((sig = va_arg(ap, int)) > 0)
+                if (sigaction(sig, &sa, NULL) < 0)
+                        r = -errno;
+        va_end(ap);
+
+        return r;
+}
+
+int default_signals(int sig, ...) {
+        struct sigaction sa = {
+                .sa_handler = SIG_DFL,
+                .sa_flags = SA_RESTART,
+        };
+        va_list ap;
+        int r = 0;
+
+        if (sigaction(sig, &sa, NULL) < 0)
+                r = -errno;
+
+        va_start(ap, sig);
+        while ((sig = va_arg(ap, int)) > 0)
+                if (sigaction(sig, &sa, NULL) < 0)
+                        r = -errno;
+        va_end(ap);
+
+        return r;
+}
+
+void safe_close_pair(int p[]) {
+        assert(p);
+
+        if (p[0] == p[1]) {
+                /* Special case pairs which use the same fd in both
+                 * directions... */
+                p[0] = p[1] = safe_close(p[0]);
+                return;
+        }
+
+        p[0] = safe_close(p[0]);
+        p[1] = safe_close(p[1]);
+}
+#endif
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
+        uint8_t *p = buf;
+        ssize_t n = 0;
+
+        assert(fd >= 0);
+        assert(buf);
+
+        while (nbytes > 0) {
+                ssize_t k;
+
+                k = read(fd, p, nbytes);
+                if (k < 0 && errno == EINTR)
+                        continue;
+
+                if (k < 0 && errno == EAGAIN && do_poll) {
+
+                        /* We knowingly ignore any return value here,
+                         * and expect that any error/EOF is reported
+                         * via read() */
+
+                        fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
+                        continue;
+                }
+
+                if (k <= 0)
+                        return n > 0 ? n : (k < 0 ? -errno : 0);
+
+                p += k;
+                nbytes -= k;
+                n += k;
+        }
+
+        return n;
+}
+
+#if 0 /* NM_IGNORED */
+ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
+        const uint8_t *p = buf;
+        ssize_t n = 0;
+
+        assert(fd >= 0);
+        assert(buf);
+
+        while (nbytes > 0) {
+                ssize_t k;
+
+                k = write(fd, p, nbytes);
+                if (k < 0 && errno == EINTR)
+                        continue;
+
+                if (k < 0 && errno == EAGAIN && do_poll) {
+
+                        /* We knowingly ignore any return value here,
+                         * and expect that any error/EOF is reported
+                         * via write() */
+
+                        fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
+                        continue;
+                }
+
+                if (k <= 0)
+                        return n > 0 ? n : (k < 0 ? -errno : 0);
+
+                p += k;
+                nbytes -= k;
+                n += k;
+        }
+
+        return n;
+}
+
+int parse_size(const char *t, off_t base, off_t *size) {
+
+        /* Soo, sometimes we want to parse IEC binary suffxies, and
+         * sometimes SI decimal suffixes. This function can parse
+         * both. Which one is the right way depends on the
+         * context. Wikipedia suggests that SI is customary for
+         * hardrware metrics and network speeds, while IEC is
+         * customary for most data sizes used by software and volatile
+         * (RAM) memory. Hence be careful which one you pick!
+         *
+         * In either case we use just K, M, G as suffix, and not Ki,
+         * Mi, Gi or so (as IEC would suggest). That's because that's
+         * frickin' ugly. But this means you really need to make sure
+         * to document which base you are parsing when you use this
+         * call. */
+
+        struct table {
+                const char *suffix;
+                unsigned long long factor;
+        };
+
+        static const struct table iec[] = {
+                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+                { "G", 1024ULL*1024ULL*1024ULL },
+                { "M", 1024ULL*1024ULL },
+                { "K", 1024ULL },
+                { "B", 1 },
+                { "", 1 },
+        };
+
+        static const struct table si[] = {
+                { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+                { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+                { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
+                { "G", 1000ULL*1000ULL*1000ULL },
+                { "M", 1000ULL*1000ULL },
+                { "K", 1000ULL },
+                { "B", 1 },
+                { "", 1 },
+        };
+
+        const struct table *table;
+        const char *p;
+        unsigned long long r = 0;
+        unsigned n_entries, start_pos = 0;
+
+        assert(t);
+        assert(base == 1000 || base == 1024);
+        assert(size);
+
+        if (base == 1000) {
+                table = si;
+                n_entries = ELEMENTSOF(si);
+        } else {
+                table = iec;
+                n_entries = ELEMENTSOF(iec);
+        }
+
+        p = t;
+        do {
+                long long l;
+                unsigned long long l2;
+                double frac = 0;
+                char *e;
+                unsigned i;
+
+                errno = 0;
+                l = strtoll(p, &e, 10);
+
+                if (errno > 0)
+                        return -errno;
+
+                if (l < 0)
+                        return -ERANGE;
+
+                if (e == p)
+                        return -EINVAL;
+
+                if (*e == '.') {
+                        e++;
+                        if (*e >= '0' && *e <= '9') {
+                                char *e2;
+
+                                /* strotoull itself would accept space/+/- */
+                                l2 = strtoull(e, &e2, 10);
+
+                                if (errno == ERANGE)
+                                        return -errno;
+
+                                /* Ignore failure. E.g. 10.M is valid */
+                                frac = l2;
+                                for (; e < e2; e++)
+                                        frac /= 10;
+                        }
+                }
+
+                e += strspn(e, WHITESPACE);
+
+                for (i = start_pos; i < n_entries; i++)
+                        if (startswith(e, table[i].suffix)) {
+                                unsigned long long tmp;
+                                if ((unsigned long long) l + (frac > 0) > ULLONG_MAX / table[i].factor)
+                                        return -ERANGE;
+                                tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
+                                if (tmp > ULLONG_MAX - r)
+                                        return -ERANGE;
+
+                                r += tmp;
+                                if ((unsigned long long) (off_t) r != r)
+                                        return -ERANGE;
+
+                                p = e + strlen(table[i].suffix);
+
+                                start_pos = i + 1;
+                                break;
+                        }
+
+                if (i >= n_entries)
+                        return -EINVAL;
+
+        } while (*p);
+
+        *size = r;
+
+        return 0;
+}
+
+int make_stdio(int fd) {
+        int r, s, t;
+
+        assert(fd >= 0);
+
+        r = dup3(fd, STDIN_FILENO, 0);
+        s = dup3(fd, STDOUT_FILENO, 0);
+        t = dup3(fd, STDERR_FILENO, 0);
+
+        if (fd >= 3)
+                safe_close(fd);
+
+        if (r < 0 || s < 0 || t < 0)
+                return -errno;
+
+        /* We rely here that the new fd has O_CLOEXEC not set */
+
+        return 0;
+}
+
+int make_null_stdio(void) {
+        int null_fd;
+
+        null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+        if (null_fd < 0)
+                return -errno;
+
+        return make_stdio(null_fd);
+}
+
+bool is_device_path(const char *path) {
+
+        /* Returns true on paths that refer to a device, either in
+         * sysfs or in /dev */
+
+        return
+                path_startswith(path, "/dev/") ||
+                path_startswith(path, "/sys/");
+}
+
+int dir_is_empty(const char *path) {
+        _cleanup_closedir_ DIR *d;
+
+        d = opendir(path);
+        if (!d)
+                return -errno;
+
+        for (;;) {
+                struct dirent *de;
+
+                errno = 0;
+                de = readdir(d);
+                if (!de && errno != 0)
+                        return -errno;
+
+                if (!de)
+                        return 1;
+
+                if (!ignore_file(de->d_name))
+                        return 0;
+        }
+}
+
+char* dirname_malloc(const char *path) {
+        char *d, *dir, *dir2;
+
+        d = strdup(path);
+        if (!d)
+                return NULL;
+        dir = dirname(d);
+        assert(dir);
+
+        if (dir != d) {
+                dir2 = strdup(dir);
+                free(d);
+                return dir2;
+        }
+
+        return dir;
+}
+#endif
+
+int dev_urandom(void *p, size_t n) {
+#if 0 /* NM_IGNORED */
+        static int have_syscall = -1;
+        int r, fd;
+        ssize_t k;
+
+        /* Gathers some randomness from the kernel. This call will
+         * never block, and will always return some data from the
+         * kernel, regardless if the random pool is fully initialized
+         * or not. It thus makes no guarantee for the quality of the
+         * returned entropy, but is good enough for or usual usecases
+         * of seeding the hash functions for hashtable */
+
+        /* Use the getrandom() syscall unless we know we don't have
+         * it, or when the requested size is too large for it. */
+        if (have_syscall != 0 || (size_t) (int) n != n) {
+                r = getrandom(p, n, GRND_NONBLOCK);
+                if (r == (int) n) {
+                        have_syscall = true;
+                        return 0;
+                }
+
+                if (r < 0) {
+                        if (errno == ENOSYS)
+                                /* we lack the syscall, continue with
+                                 * reading from /dev/urandom */
+                                have_syscall = false;
+                        else if (errno == EAGAIN)
+                                /* not enough entropy for now. Let's
+                                 * remember to use the syscall the
+                                 * next time, again, but also read
+                                 * from /dev/urandom for now, which
+                                 * doesn't care about the current
+                                 * amount of entropy.  */
+                                have_syscall = true;
+                        else
+                                return -errno;
+                } else
+                        /* too short read? */
+                        return -EIO;
+        }
+#else /* NM IGNORED */
+        int fd;
+        ssize_t k;
+#endif
+
+        fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return errno == ENOENT ? -ENOSYS : -errno;
+
+        k = loop_read(fd, p, n, true);
+        safe_close(fd);
+
+        if (k < 0)
+                return (int) k;
+        if ((size_t) k != n)
+                return -EIO;
+
+        return 0;
+}
+
+void initialize_srand(void) {
+        static bool srand_called = false;
+        unsigned x;
+#ifdef HAVE_SYS_AUXV_H
+        void *auxv;
+#endif
+
+        if (srand_called)
+                return;
+
+        x = 0;
+
+#ifdef HAVE_SYS_AUXV_H
+        /* The kernel provides us with a bit of entropy in auxv, so
+         * let's try to make use of that to seed the pseudo-random
+         * generator. It's better than nothing... */
+
+        auxv = (void*) getauxval(AT_RANDOM);
+        if (auxv)
+                x ^= *(unsigned*) auxv;
+#endif
+
+        x ^= (unsigned) now(CLOCK_REALTIME);
+        x ^= (unsigned) gettid();
+
+        srand(x);
+        srand_called = true;
+}
+
+void random_bytes(void *p, size_t n) {
+        uint8_t *q;
+        int r;
+
+        r = dev_urandom(p, n);
+        if (r >= 0)
+                return;
+
+        /* If some idiot made /dev/urandom unavailable to us, he'll
+         * get a PRNG instead. */
+
+        initialize_srand();
+
+        for (q = p; q < (uint8_t*) p + n; q ++)
+                *q = rand();
+}
+
+#if 0 /* NM_IGNORED */
+void rename_process(const char name[8]) {
+        assert(name);
+
+        /* This is a like a poor man's setproctitle(). It changes the
+         * comm field, argv[0], and also the glibc's internally used
+         * name of the process. For the first one a limit of 16 chars
+         * applies, to the second one usually one of 10 (i.e. length
+         * of "/sbin/init"), to the third one one of 7 (i.e. length of
+         * "systemd"). If you pass a longer string it will be
+         * truncated */
+
+        prctl(PR_SET_NAME, name);
+
+        if (program_invocation_name)
+                strncpy(program_invocation_name, name, strlen(program_invocation_name));
+
+        if (saved_argc > 0) {
+                int i;
+
+                if (saved_argv[0])
+                        strncpy(saved_argv[0], name, strlen(saved_argv[0]));
+
+                for (i = 1; i < saved_argc; i++) {
+                        if (!saved_argv[i])
+                                break;
+
+                        memzero(saved_argv[i], strlen(saved_argv[i]));
+                }
+        }
+}
+
+void sigset_add_many(sigset_t *ss, ...) {
+        va_list ap;
+        int sig;
+
+        assert(ss);
+
+        va_start(ap, ss);
+        while ((sig = va_arg(ap, int)) > 0)
+                assert_se(sigaddset(ss, sig) == 0);
+        va_end(ap);
+}
+
+int sigprocmask_many(int how, ...) {
+        va_list ap;
+        sigset_t ss;
+        int sig;
+
+        assert_se(sigemptyset(&ss) == 0);
+
+        va_start(ap, how);
+        while ((sig = va_arg(ap, int)) > 0)
+                assert_se(sigaddset(&ss, sig) == 0);
+        va_end(ap);
+
+        if (sigprocmask(how, &ss, NULL) < 0)
+                return -errno;
+
+        return 0;
+}
+
+char* gethostname_malloc(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        if (!isempty(u.nodename) && !streq(u.nodename, "(none)"))
+                return strdup(u.nodename);
+
+        return strdup(u.sysname);
+}
+
+bool hostname_is_set(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        return !isempty(u.nodename) && !streq(u.nodename, "(none)");
+}
+
+char *lookup_uid(uid_t uid) {
+        long bufsize;
+        char *name;
+        _cleanup_free_ char *buf = NULL;
+        struct passwd pwbuf, *pw = NULL;
+
+        /* Shortcut things to avoid NSS lookups */
+        if (uid == 0)
+                return strdup("root");
+
+        bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+        if (bufsize <= 0)
+                bufsize = 4096;
+
+        buf = malloc(bufsize);
+        if (!buf)
+                return NULL;
+
+        if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw)
+                return strdup(pw->pw_name);
+
+        if (asprintf(&name, UID_FMT, uid) < 0)
+                return NULL;
+
+        return name;
+}
+
+char* getlogname_malloc(void) {
+        uid_t uid;
+        struct stat st;
+
+        if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+                uid = st.st_uid;
+        else
+                uid = getuid();
+
+        return lookup_uid(uid);
+}
+
+char *getusername_malloc(void) {
+        const char *e;
+
+        e = getenv("USER");
+        if (e)
+                return strdup(e);
+
+        return lookup_uid(getuid());
+}
+
+int getttyname_malloc(int fd, char **r) {
+        char path[PATH_MAX], *c;
+        int k;
+
+        assert(r);
+
+        k = ttyname_r(fd, path, sizeof(path));
+        if (k > 0)
+                return -k;
+
+        char_array_0(path);
+
+        c = strdup(startswith(path, "/dev/") ? path + 5 : path);
+        if (!c)
+                return -ENOMEM;
+
+        *r = c;
+        return 0;
+}
+
+int getttyname_harder(int fd, char **r) {
+        int k;
+        char *s;
+
+        k = getttyname_malloc(fd, &s);
+        if (k < 0)
+                return k;
+
+        if (streq(s, "tty")) {
+                free(s);
+                return get_ctty(0, NULL, r);
+        }
+
+        *r = s;
+        return 0;
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d) {
+        int r;
+        _cleanup_free_ char *line = NULL;
+        const char *p;
+        unsigned long ttynr;
+
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "stat");
+        r = read_one_line_file(p, &line);
+        if (r < 0)
+                return r;
+
+        p = strrchr(line, ')');
+        if (!p)
+                return -EIO;
+
+        p++;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%*d "  /* ppid */
+                   "%*d "  /* pgrp */
+                   "%*d "  /* session */
+                   "%lu ", /* ttynr */
+                   &ttynr) != 1)
+                return -EIO;
+
+        if (major(ttynr) == 0 && minor(ttynr) == 0)
+                return -ENOENT;
+
+        if (d)
+                *d = (dev_t) ttynr;
+
+        return 0;
+}
+
+int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
+        char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
+        _cleanup_free_ char *s = NULL;
+        const char *p;
+        dev_t devnr;
+        int k;
+
+        assert(r);
+
+        k = get_ctty_devnr(pid, &devnr);
+        if (k < 0)
+                return k;
+
+        sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
+
+        k = readlink_malloc(fn, &s);
+        if (k < 0) {
+
+                if (k != -ENOENT)
+                        return k;
+
+                /* This is an ugly hack */
+                if (major(devnr) == 136) {
+                        asprintf(&b, "pts/%u", minor(devnr));
+                        goto finish;
+                }
+
+                /* Probably something like the ptys which have no
+                 * symlink in /dev/char. Let's return something
+                 * vaguely useful. */
+
+                b = strdup(fn + 5);
+                goto finish;
+        }
+
+        if (startswith(s, "/dev/"))
+                p = s + 5;
+        else if (startswith(s, "../"))
+                p = s + 3;
+        else
+                p = s;
+
+        b = strdup(p);
+
+finish:
+        if (!b)
+                return -ENOMEM;
+
+        *r = b;
+        if (_devnr)
+                *_devnr = devnr;
+
+        return 0;
+}
+
+int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+        _cleanup_closedir_ DIR *d = NULL;
+        int ret = 0;
+
+        assert(fd >= 0);
+
+        /* This returns the first error we run into, but nevertheless
+         * tries to go on. This closes the passed fd. */
+
+        d = fdopendir(fd);
+        if (!d) {
+                safe_close(fd);
+
+                return errno == ENOENT ? 0 : -errno;
+        }
+
+        for (;;) {
+                struct dirent *de;
+                bool is_dir, keep_around;
+                struct stat st;
+                int r;
+
+                errno = 0;
+                de = readdir(d);
+                if (!de) {
+                        if (errno != 0 && ret == 0)
+                                ret = -errno;
+                        return ret;
+                }
+
+                if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+                        continue;
+
+                if (de->d_type == DT_UNKNOWN ||
+                    honour_sticky ||
+                    (de->d_type == DT_DIR && root_dev)) {
+                        if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                                continue;
+                        }
+
+                        is_dir = S_ISDIR(st.st_mode);
+                        keep_around =
+                                honour_sticky &&
+                                (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                (st.st_mode & S_ISVTX);
+                } else {
+                        is_dir = de->d_type == DT_DIR;
+                        keep_around = false;
+                }
+
+                if (is_dir) {
+                        int subdir_fd;
+
+                        /* if root_dev is set, remove subdirectories only, if device is same as dir */
+                        if (root_dev && st.st_dev != root_dev->st_dev)
+                                continue;
+
+                        subdir_fd = openat(fd, de->d_name,
+                                           O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                        if (subdir_fd < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                                continue;
+                        }
+
+                        r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
+                        if (r < 0 && ret == 0)
+                                ret = r;
+
+                        if (!keep_around)
+                                if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+                                        if (ret == 0 && errno != ENOENT)
+                                                ret = -errno;
+                                }
+
+                } else if (!only_dirs && !keep_around) {
+
+                        if (unlinkat(fd, de->d_name, 0) < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                        }
+                }
+        }
+}
+
+_pure_ static int is_temporary_fs(struct statfs *s) {
+        assert(s);
+
+        return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
+               F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
+}
+
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+        struct statfs s;
+
+        assert(fd >= 0);
+
+        if (fstatfs(fd, &s) < 0) {
+                safe_close(fd);
+                return -errno;
+        }
+
+        /* We refuse to clean disk file systems with this call. This
+         * is extra paranoia just to be sure we never ever remove
+         * non-state data */
+        if (!is_temporary_fs(&s)) {
+                log_error("Attempted to remove disk file system, and we can't allow that.");
+                safe_close(fd);
+                return -EPERM;
+        }
+
+        return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
+}
+
+static int file_is_priv_sticky(const char *p) {
+        struct stat st;
+
+        assert(p);
+
+        if (lstat(p, &st) < 0)
+                return -errno;
+
+        return
+                (st.st_uid == 0 || st.st_uid == getuid()) &&
+                (st.st_mode & S_ISVTX);
+}
+
+static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
+        int fd, r;
+        struct statfs s;
+
+        assert(path);
+
+        /* We refuse to clean the root file system with this
+         * call. This is extra paranoia to never cause a really
+         * seriously broken system. */
+        if (path_equal(path, "/")) {
+                log_error("Attempted to remove entire root file system, and we can't allow that.");
+                return -EPERM;
+        }
+
+        fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+        if (fd < 0) {
+
+                if (errno != ENOTDIR)
+                        return -errno;
+
+                if (!dangerous) {
+                        if (statfs(path, &s) < 0)
+                                return -errno;
+
+                        if (!is_temporary_fs(&s)) {
+                                log_error("Attempted to remove disk file system, and we can't allow that.");
+                                return -EPERM;
+                        }
+                }
+
+                if (delete_root && !only_dirs)
+                        if (unlink(path) < 0 && errno != ENOENT)
+                                return -errno;
+
+                return 0;
+        }
+
+        if (!dangerous) {
+                if (fstatfs(fd, &s) < 0) {
+                        safe_close(fd);
+                        return -errno;
+                }
+
+                if (!is_temporary_fs(&s)) {
+                        log_error("Attempted to remove disk file system, and we can't allow that.");
+                        safe_close(fd);
+                        return -EPERM;
+                }
+        }
+
+        r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
+        if (delete_root) {
+
+                if (honour_sticky && file_is_priv_sticky(path) > 0)
+                        return r;
+
+                if (rmdir(path) < 0 && errno != ENOENT) {
+                        if (r == 0)
+                                r = -errno;
+                }
+        }
+
+        return r;
+}
+
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
+}
+
+int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
+}
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+        assert(path);
+
+        /* Under the assumption that we are running privileged we
+         * first change the access mode and only then hand out
+         * ownership to avoid a window where access is too open. */
+
+        if (mode != (mode_t) -1)
+                if (chmod(path, mode) < 0)
+                        return -errno;
+
+        if (uid != (uid_t) -1 || gid != (gid_t) -1)
+                if (chown(path, uid, gid) < 0)
+                        return -errno;
+
+        return 0;
+}
+
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
+        assert(fd >= 0);
+
+        /* Under the assumption that we are running privileged we
+         * first change the access mode and only then hand out
+         * ownership to avoid a window where access is too open. */
+
+        if (mode != (mode_t) -1)
+                if (fchmod(fd, mode) < 0)
+                        return -errno;
+
+        if (uid != (uid_t) -1 || gid != (gid_t) -1)
+                if (fchown(fd, uid, gid) < 0)
+                        return -errno;
+
+        return 0;
+}
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
+        cpu_set_t *r;
+        unsigned n = 1024;
+
+        /* Allocates the cpuset in the right size */
+
+        for (;;) {
+                if (!(r = CPU_ALLOC(n)))
+                        return NULL;
+
+                if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
+                        CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
+
+                        if (ncpus)
+                                *ncpus = n;
+
+                        return r;
+                }
+
+                CPU_FREE(r);
+
+                if (errno != EINVAL)
+                        return NULL;
+
+                n *= 2;
+        }
+}
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
+        static const char status_indent[] = "         "; /* "[" STATUS "] " */
+        _cleanup_free_ char *s = NULL;
+        _cleanup_close_ int fd = -1;
+        struct iovec iovec[6] = {};
+        int n = 0;
+        static bool prev_ephemeral;
+
+        assert(format);
+
+        /* This is independent of logging, as status messages are
+         * optional and go exclusively to the console. */
+
+        if (vasprintf(&s, format, ap) < 0)
+                return log_oom();
+
+        fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        if (ellipse) {
+                char *e;
+                size_t emax, sl;
+                int c;
+
+                c = fd_columns(fd);
+                if (c <= 0)
+                        c = 80;
+
+                sl = status ? sizeof(status_indent)-1 : 0;
+
+                emax = c - sl - 1;
+                if (emax < 3)
+                        emax = 3;
+
+                e = ellipsize(s, emax, 50);
+                if (e) {
+                        free(s);
+                        s = e;
+                }
+        }
+
+        if (prev_ephemeral)
+                IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
+        prev_ephemeral = ephemeral;
+
+        if (status) {
+                if (!isempty(status)) {
+                        IOVEC_SET_STRING(iovec[n++], "[");
+                        IOVEC_SET_STRING(iovec[n++], status);
+                        IOVEC_SET_STRING(iovec[n++], "] ");
+                } else
+                        IOVEC_SET_STRING(iovec[n++], status_indent);
+        }
+
+        IOVEC_SET_STRING(iovec[n++], s);
+        if (!ephemeral)
+                IOVEC_SET_STRING(iovec[n++], "\n");
+
+        if (writev(fd, iovec, n) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
+        va_list ap;
+        int r;
+
+        assert(format);
+
+        va_start(ap, format);
+        r = status_vprintf(status, ellipse, ephemeral, format, ap);
+        va_end(ap);
+
+        return r;
+}
+
+char *replace_env(const char *format, char **env) {
+        enum {
+                WORD,
+                CURLY,
+                VARIABLE
+        } state = WORD;
+
+        const char *e, *word = format;
+        char *r = NULL, *k;
+
+        assert(format);
+
+        for (e = format; *e; e ++) {
+
+                switch (state) {
+
+                case WORD:
+                        if (*e == '$')
+                                state = CURLY;
+                        break;
+
+                case CURLY:
+                        if (*e == '{') {
+                                k = strnappend(r, word, e-word-1);
+                                if (!k)
+                                        goto fail;
+
+                                free(r);
+                                r = k;
+
+                                word = e-1;
+                                state = VARIABLE;
+
+                        } else if (*e == '$') {
+                                k = strnappend(r, word, e-word);
+                                if (!k)
+                                        goto fail;
+
+                                free(r);
+                                r = k;
+
+                                word = e+1;
+                                state = WORD;
+                        } else
+                                state = WORD;
+                        break;
+
+                case VARIABLE:
+                        if (*e == '}') {
+                                const char *t;
+
+                                t = strempty(strv_env_get_n(env, word+2, e-word-2));
+
+                                k = strappend(r, t);
+                                if (!k)
+                                        goto fail;
+
+                                free(r);
+                                r = k;
+
+                                word = e+1;
+                                state = WORD;
+                        }
+                        break;
+                }
+        }
+
+        k = strnappend(r, word, e-word);
+        if (!k)
+                goto fail;
+
+        free(r);
+        return k;
+
+fail:
+        free(r);
+        return NULL;
+}
+
+#if 0 /* NM_IGNORED */
+char **replace_env_argv(char **argv, char **env) {
+        char **ret, **i;
+        unsigned k = 0, l = 0;
+
+        l = strv_length(argv);
+
+        ret = new(char*, l+1);
+        if (!ret)
+                return NULL;
+
+        STRV_FOREACH(i, argv) {
+
+                /* If $FOO appears as single word, replace it by the split up variable */
+                if ((*i)[0] == '$' && (*i)[1] != '{') {
+                        char *e;
+                        char **w, **m;
+                        unsigned q;
+
+                        e = strv_env_get(env, *i+1);
+                        if (e) {
+                                int r;
+
+                                r = strv_split_quoted(&m, e, true);
+                                if (r < 0) {
+                                        ret[k] = NULL;
+                                        strv_free(ret);
+                                        return NULL;
+                                }
+                        } else
+                                m = NULL;
+
+                        q = strv_length(m);
+                        l = l + q - 1;
+
+                        w = realloc(ret, sizeof(char*) * (l+1));
+                        if (!w) {
+                                ret[k] = NULL;
+                                strv_free(ret);
+                                strv_free(m);
+                                return NULL;
+                        }
+
+                        ret = w;
+                        if (m) {
+                                memcpy(ret + k, m, q * sizeof(char*));
+                                free(m);
+                        }
+
+                        k += q;
+                        continue;
+                }
+
+                /* If ${FOO} appears as part of a word, replace it by the variable as-is */
+                ret[k] = replace_env(*i, env);
+                if (!ret[k]) {
+                        strv_free(ret);
+                        return NULL;
+                }
+                k++;
+        }
+
+        ret[k] = NULL;
+        return ret;
+}
+#endif
+
+int fd_columns(int fd) {
+        struct winsize ws = {};
+
+        if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+                return -errno;
+
+        if (ws.ws_col <= 0)
+                return -EIO;
+
+        return ws.ws_col;
+}
+
+unsigned columns(void) {
+        const char *e;
+        int c;
+
+        if (_likely_(cached_columns > 0))
+                return cached_columns;
+
+        c = 0;
+        e = getenv("COLUMNS");
+        if (e)
+                (void) safe_atoi(e, &c);
+
+        if (c <= 0)
+                c = fd_columns(STDOUT_FILENO);
+
+        if (c <= 0)
+                c = 80;
+
+        cached_columns = c;
+        return c;
+}
+
+int fd_lines(int fd) {
+        struct winsize ws = {};
+
+        if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+                return -errno;
+
+        if (ws.ws_row <= 0)
+                return -EIO;
+
+        return ws.ws_row;
+}
+
+unsigned lines(void) {
+        const char *e;
+        unsigned l;
+
+        if (_likely_(cached_lines > 0))
+                return cached_lines;
+
+        l = 0;
+        e = getenv("LINES");
+        if (e)
+                (void) safe_atou(e, &l);
+
+        if (l <= 0)
+                l = fd_lines(STDOUT_FILENO);
+
+        if (l <= 0)
+                l = 24;
+
+        cached_lines = l;
+        return cached_lines;
+}
+
+/* intended to be used as a SIGWINCH sighandler */
+void columns_lines_cache_reset(int signum) {
+        cached_columns = 0;
+        cached_lines = 0;
+}
+
+bool on_tty(void) {
+        static int cached_on_tty = -1;
+
+        if (_unlikely_(cached_on_tty < 0))
+                cached_on_tty = isatty(STDOUT_FILENO) > 0;
+
+        return cached_on_tty;
+}
+
+int files_same(const char *filea, const char *fileb) {
+        struct stat a, b;
+
+        if (stat(filea, &a) < 0)
+                return -errno;
+
+        if (stat(fileb, &b) < 0)
+                return -errno;
+
+        return a.st_dev == b.st_dev &&
+               a.st_ino == b.st_ino;
+}
+
+int running_in_chroot(void) {
+        int ret;
+
+        ret = files_same("/proc/1/root", "/");
+        if (ret < 0)
+                return ret;
+
+        return ret == 0;
+}
+
+static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+        size_t x;
+        char *r;
+
+        assert(s);
+        assert(percent <= 100);
+        assert(new_length >= 3);
+
+        if (old_length <= 3 || old_length <= new_length)
+                return strndup(s, old_length);
+
+        r = new0(char, new_length+1);
+        if (!r)
+                return NULL;
+
+        x = (new_length * percent) / 100;
+
+        if (x > new_length - 3)
+                x = new_length - 3;
+
+        memcpy(r, s, x);
+        r[x] = '.';
+        r[x+1] = '.';
+        r[x+2] = '.';
+        memcpy(r + x + 3,
+               s + old_length - (new_length - x - 3),
+               new_length - x - 3);
+
+        return r;
+}
+
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+        size_t x;
+        char *e;
+        const char *i, *j;
+        unsigned k, len, len2;
+
+        assert(s);
+        assert(percent <= 100);
+        assert(new_length >= 3);
+
+        /* if no multibyte characters use ascii_ellipsize_mem for speed */
+        if (ascii_is_valid(s))
+                return ascii_ellipsize_mem(s, old_length, new_length, percent);
+
+        if (old_length <= 3 || old_length <= new_length)
+                return strndup(s, old_length);
+
+        x = (new_length * percent) / 100;
+
+        if (x > new_length - 3)
+                x = new_length - 3;
+
+        k = 0;
+        for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
+                int c;
+
+                c = utf8_encoded_to_unichar(i);
+                if (c < 0)
+                        return NULL;
+                k += unichar_iswide(c) ? 2 : 1;
+        }
+
+        if (k > x) /* last character was wide and went over quota */
+                x ++;
+
+        for (j = s + old_length; k < new_length && j > i; ) {
+                int c;
+
+                j = utf8_prev_char(j);
+                c = utf8_encoded_to_unichar(j);
+                if (c < 0)
+                        return NULL;
+                k += unichar_iswide(c) ? 2 : 1;
+        }
+        assert(i <= j);
+
+        /* we don't actually need to ellipsize */
+        if (i == j)
+                return memdup(s, old_length + 1);
+
+        /* make space for ellipsis */
+        j = utf8_next_char(j);
+
+        len = i - s;
+        len2 = s + old_length - j;
+        e = new(char, len + 3 + len2 + 1);
+        if (!e)
+                return NULL;
+
+        /*
+        printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+               old_length, new_length, x, len, len2, k);
+        */
+
+        memcpy(e, s, len);
+        e[len]   = 0xe2; /* tri-dot ellipsis: … */
+        e[len + 1] = 0x80;
+        e[len + 2] = 0xa6;
+
+        memcpy(e + len + 3, j, len2 + 1);
+
+        return e;
+}
+
+char *ellipsize(const char *s, size_t length, unsigned percent) {
+        return ellipsize_mem(s, strlen(s), length, percent);
+}
+
+int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
+        _cleanup_close_ int fd = -1;
+        int r;
+
+        assert(path);
+
+        if (parents)
+                mkdir_parents(path, 0755);
+
+        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
+        if (fd < 0)
+                return -errno;
+
+        if (mode > 0) {
+                r = fchmod(fd, mode);
+                if (r < 0)
+                        return -errno;
+        }
+
+        if (uid != (uid_t) -1 || gid != (gid_t) -1) {
+                r = fchown(fd, uid, gid);
+                if (r < 0)
+                        return -errno;
+        }
+
+        if (stamp != USEC_INFINITY) {
+                struct timespec ts[2];
+
+                timespec_store(&ts[0], stamp);
+                ts[1] = ts[0];
+                r = futimens(fd, ts);
+        } else
+                r = futimens(fd, NULL);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
+int touch(const char *path) {
+        return touch_file(path, false, USEC_INFINITY, (uid_t) -1, (gid_t) -1, 0);
+}
+
+char *unquote(const char *s, const char* quotes) {
+        size_t l;
+        assert(s);
+
+        /* This is rather stupid, simply removes the heading and
+         * trailing quotes if there is one. Doesn't care about
+         * escaping or anything. We should make this smarter one
+         * day...*/
+
+        l = strlen(s);
+        if (l < 2)
+                return strdup(s);
+
+        if (strchr(quotes, s[0]) && s[l-1] == s[0])
+                return strndup(s+1, l-2);
+
+        return strdup(s);
+}
+
+char *normalize_env_assignment(const char *s) {
+        _cleanup_free_ char *value = NULL;
+        const char *eq;
+        char *p, *name;
+
+        eq = strchr(s, '=');
+        if (!eq) {
+                char *r, *t;
+
+                r = strdup(s);
+                if (!r)
+                        return NULL;
+
+                t = strstrip(r);
+                if (t != r)
+                        memmove(r, t, strlen(t) + 1);
+
+                return r;
+        }
+
+        name = strndupa(s, eq - s);
+        p = strdupa(eq + 1);
+
+        value = unquote(strstrip(p), QUOTES);
+        if (!value)
+                return NULL;
+
+        return strjoin(strstrip(name), "=", value, NULL);
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *status) {
+        siginfo_t dummy;
+
+        assert(pid >= 1);
+
+        if (!status)
+                status = &dummy;
+
+        for (;;) {
+                zero(*status);
+
+                if (waitid(P_PID, pid, status, WEXITED) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        return -errno;
+                }
+
+                return 0;
+        }
+}
+
+/*
+ * Return values:
+ * < 0 : wait_for_terminate() failed to get the state of the
+ *       process, the process was terminated by a signal, or
+ *       failed for an unknown reason.
+ * >=0 : The process terminated normally, and its exit code is
+ *       returned.
+ *
+ * That is, success is indicated by a return value of zero, and an
+ * error is indicated by a non-zero value.
+ */
+int wait_for_terminate_and_warn(const char *name, pid_t pid) {
+        int r;
+        siginfo_t status;
+
+        assert(name);
+        assert(pid > 1);
+
+        r = wait_for_terminate(pid, &status);
+        if (r < 0) {
+                log_warning("Failed to wait for %s: %s", name, strerror(-r));
+                return r;
+        }
+
+        if (status.si_code == CLD_EXITED) {
+                if (status.si_status != 0) {
+                        log_warning("%s failed with error code %i.", name, status.si_status);
+                        return status.si_status;
+                }
+
+                log_debug("%s succeeded.", name);
+                return 0;
+
+        } else if (status.si_code == CLD_KILLED ||
+                   status.si_code == CLD_DUMPED) {
+
+                log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+                return -EPROTO;
+        }
+
+        log_warning("%s failed due to unknown reason.", name);
+        return -EPROTO;
+}
+
+noreturn void freeze(void) {
+
+        /* Make sure nobody waits for us on a socket anymore */
+        close_all_fds(NULL, 0);
+
+        sync();
+
+        for (;;)
+                pause();
+}
+
+bool null_or_empty(struct stat *st) {
+        assert(st);
+
+        if (S_ISREG(st->st_mode) && st->st_size <= 0)
+                return true;
+
+        if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
+                return true;
+
+        return false;
+}
+
+int null_or_empty_path(const char *fn) {
+        struct stat st;
+
+        assert(fn);
+
+        if (stat(fn, &st) < 0)
+                return -errno;
+
+        return null_or_empty(&st);
+}
+
+int null_or_empty_fd(int fd) {
+        struct stat st;
+
+        assert(fd >= 0);
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        return null_or_empty(&st);
+}
+
+DIR *xopendirat(int fd, const char *name, int flags) {
+        int nfd;
+        DIR *d;
+
+        assert(!(flags & O_CREAT));
+
+        nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
+        if (nfd < 0)
+                return NULL;
+
+        d = fdopendir(nfd);
+        if (!d) {
+                safe_close(nfd);
+                return NULL;
+        }
+
+        return d;
+}
+
+int signal_from_string_try_harder(const char *s) {
+        int signo;
+        assert(s);
+
+        signo = signal_from_string(s);
+        if (signo <= 0)
+                if (startswith(s, "SIG"))
+                        return signal_from_string(s+3);
+
+        return signo;
+}
+
+static char *tag_to_udev_node(const char *tagvalue, const char *by) {
+        _cleanup_free_ char *t = NULL, *u = NULL;
+        size_t enc_len;
+
+        u = unquote(tagvalue, "\"\'");
+        if (!u)
+                return NULL;
+
+        enc_len = strlen(u) * 4 + 1;
+        t = new(char, enc_len);
+        if (!t)
+                return NULL;
+
+        if (encode_devnode_name(u, t, enc_len) < 0)
+                return NULL;
+
+        return strjoin("/dev/disk/by-", by, "/", t, NULL);
+}
+
+char *fstab_node_to_udev_node(const char *p) {
+        assert(p);
+
+        if (startswith(p, "LABEL="))
+                return tag_to_udev_node(p+6, "label");
+
+        if (startswith(p, "UUID="))
+                return tag_to_udev_node(p+5, "uuid");
+
+        if (startswith(p, "PARTUUID="))
+                return tag_to_udev_node(p+9, "partuuid");
+
+        if (startswith(p, "PARTLABEL="))
+                return tag_to_udev_node(p+10, "partlabel");
+
+        return strdup(p);
+}
+
+bool tty_is_vc(const char *tty) {
+        assert(tty);
+
+        return vtnr_from_tty(tty) >= 0;
+}
+
+bool tty_is_console(const char *tty) {
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        return streq(tty, "console");
+}
+
+int vtnr_from_tty(const char *tty) {
+        int i, r;
+
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        if (!startswith(tty, "tty") )
+                return -EINVAL;
+
+        if (tty[3] < '0' || tty[3] > '9')
+                return -EINVAL;
+
+        r = safe_atoi(tty+3, &i);
+        if (r < 0)
+                return r;
+
+        if (i < 0 || i > 63)
+                return -EINVAL;
+
+        return i;
+}
+
+char *resolve_dev_console(char **active) {
+        char *tty;
+
+        /* Resolve where /dev/console is pointing to, if /sys is actually ours
+         * (i.e. not read-only-mounted which is a sign for container setups) */
+
+        if (path_is_read_only_fs("/sys") > 0)
+                return NULL;
+
+        if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
+                return NULL;
+
+        /* If multiple log outputs are configured the last one is what
+         * /dev/console points to */
+        tty = strrchr(*active, ' ');
+        if (tty)
+                tty++;
+        else
+                tty = *active;
+
+        if (streq(tty, "tty0")) {
+                char *tmp;
+
+                /* Get the active VC (e.g. tty1) */
+                if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
+                        free(*active);
+                        tty = *active = tmp;
+                }
+        }
+
+        return tty;
+}
+
+bool tty_is_vc_resolve(const char *tty) {
+        _cleanup_free_ char *active = NULL;
+
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        if (streq(tty, "console")) {
+                tty = resolve_dev_console(&active);
+                if (!tty)
+                        return false;
+        }
+
+        return tty_is_vc(tty);
+}
+
+const char *default_term_for_tty(const char *tty) {
+        assert(tty);
+
+        return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt102";
+}
+
+bool dirent_is_file(const struct dirent *de) {
+        assert(de);
+
+        if (ignore_file(de->d_name))
+                return false;
+
+        if (de->d_type != DT_REG &&
+            de->d_type != DT_LNK &&
+            de->d_type != DT_UNKNOWN)
+                return false;
+
+        return true;
+}
+
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
+        assert(de);
+
+        if (de->d_type != DT_REG &&
+            de->d_type != DT_LNK &&
+            de->d_type != DT_UNKNOWN)
+                return false;
+
+        if (ignore_file_allow_backup(de->d_name))
+                return false;
+
+        return endswith(de->d_name, suffix);
+}
+
+void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv[]) {
+        pid_t executor_pid;
+        int r;
+
+        assert(directory);
+
+        /* Executes all binaries in a directory in parallel and waits
+         * for them to finish. Optionally a timeout is applied. */
+
+        executor_pid = fork();
+        if (executor_pid < 0) {
+                log_error("Failed to fork: %m");
+                return;
+
+        } else if (executor_pid == 0) {
+                _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
+                _cleanup_closedir_ DIR *_d = NULL;
+                struct dirent *de;
+
+                /* We fork this all off from a child process so that
+                 * we can somewhat cleanly make use of SIGALRM to set
+                 * a time limit */
+
+                reset_all_signal_handlers();
+                reset_signal_mask();
+
+                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+                if (!d) {
+                        d = _d = opendir(directory);
+                        if (!d) {
+                                if (errno == ENOENT)
+                                        _exit(EXIT_SUCCESS);
+
+                                log_error("Failed to enumerate directory %s: %m", directory);
+                                _exit(EXIT_FAILURE);
+                        }
+                }
+
+                pids = hashmap_new(NULL);
+                if (!pids) {
+                        log_oom();
+                        _exit(EXIT_FAILURE);
+                }
+
+                FOREACH_DIRENT(de, d, break) {
+                        _cleanup_free_ char *path = NULL;
+                        pid_t pid;
+
+                        if (!dirent_is_file(de))
+                                continue;
+
+                        path = strjoin(directory, "/", de->d_name, NULL);
+                        if (!path) {
+                                log_oom();
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        pid = fork();
+                        if (pid < 0) {
+                                log_error("Failed to fork: %m");
+                                continue;
+                        } else if (pid == 0) {
+                                char *_argv[2];
+
+                                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+                                if (!argv) {
+                                        _argv[0] = path;
+                                        _argv[1] = NULL;
+                                        argv = _argv;
+                                } else
+                                        argv[0] = path;
+
+                                execv(path, argv);
+                                log_error("Failed to execute %s: %m", path);
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        log_debug("Spawned %s as " PID_FMT ".", path, pid);
+
+                        r = hashmap_put(pids, UINT_TO_PTR(pid), path);
+                        if (r < 0) {
+                                log_oom();
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        path = NULL;
+                }
+
+                /* Abort execution of this process after the
+                 * timout. We simply rely on SIGALRM as default action
+                 * terminating the process, and turn on alarm(). */
+
+                if (timeout != USEC_INFINITY)
+                        alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
+
+                while (!hashmap_isempty(pids)) {
+                        _cleanup_free_ char *path = NULL;
+                        pid_t pid;
+
+                        pid = PTR_TO_UINT(hashmap_first_key(pids));
+                        assert(pid > 0);
+
+                        path = hashmap_remove(pids, UINT_TO_PTR(pid));
+                        assert(path);
+
+                        wait_for_terminate_and_warn(path, pid);
+                }
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        wait_for_terminate_and_warn(directory, executor_pid);
+}
+
+int kill_and_sigcont(pid_t pid, int sig) {
+        int r;
+
+        r = kill(pid, sig) < 0 ? -errno : 0;
+
+        if (r >= 0)
+                kill(pid, SIGCONT);
+
+        return r;
+}
+
+bool nulstr_contains(const char*nulstr, const char *needle) {
+        const char *i;
+
+        if (!nulstr)
+                return false;
+
+        NULSTR_FOREACH(i, nulstr)
+                if (streq(i, needle))
+                        return true;
+
+        return false;
+}
+
+bool plymouth_running(void) {
+        return access("/run/plymouth/pid", F_OK) >= 0;
+}
+
+char* strshorten(char *s, size_t l) {
+        assert(s);
+
+        if (l < strlen(s))
+                s[l] = 0;
+
+        return s;
+}
+#endif
+
+static bool hostname_valid_char(char c) {
+        return
+                (c >= 'a' && c <= 'z') ||
+                (c >= 'A' && c <= 'Z') ||
+                (c >= '0' && c <= '9') ||
+                c == '-' ||
+                c == '_' ||
+                c == '.';
+}
+
+bool hostname_is_valid(const char *s) {
+        const char *p;
+        bool dot;
+
+        if (isempty(s))
+                return false;
+
+        for (p = s, dot = true; *p; p++) {
+                if (*p == '.') {
+                        if (dot)
+                                return false;
+
+                        dot = true;
+                } else {
+                        if (!hostname_valid_char(*p))
+                                return false;
+
+                        dot = false;
+                }
+        }
+
+        if (dot)
+                return false;
+
+        if (p-s > HOST_NAME_MAX)
+                return false;
+
+        return true;
+}
+
+#if 0 /* NM_IGNORED */
+char* hostname_cleanup(char *s, bool lowercase) {
+        char *p, *d;
+        bool dot;
+
+        for (p = s, d = s, dot = true; *p; p++) {
+                if (*p == '.') {
+                        if (dot)
+                                continue;
+
+                        *(d++) = '.';
+                        dot = true;
+                } else if (hostname_valid_char(*p)) {
+                        *(d++) = lowercase ? tolower(*p) : *p;
+                        dot = false;
+                }
+
+        }
+
+        if (dot && d > s)
+                d[-1] = 0;
+        else
+                *d = 0;
+
+        strshorten(s, HOST_NAME_MAX);
+
+        return s;
+}
+
+bool machine_name_is_valid(const char *s) {
+
+        if (!hostname_is_valid(s))
+                return false;
+
+        /* Machine names should be useful hostnames, but also be
+         * useful in unit names, hence we enforce a stricter length
+         * limitation. */
+
+        if (strlen(s) > 64)
+                return false;
+
+        return true;
+}
+
+int pipe_eof(int fd) {
+        struct pollfd pollfd = {
+                .fd = fd,
+                .events = POLLIN|POLLHUP,
+        };
+
+        int r;
+
+        r = poll(&pollfd, 1, 0);
+        if (r < 0)
+                return -errno;
+
+        if (r == 0)
+                return 0;
+
+        return pollfd.revents & POLLHUP;
+}
+#endif
+
+int fd_wait_for_event(int fd, int event, usec_t t) {
+
+        struct pollfd pollfd = {
+                .fd = fd,
+                .events = event,
+        };
+
+        struct timespec ts;
+        int r;
+
+        r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
+        if (r < 0)
+                return -errno;
+
+        if (r == 0)
+                return 0;
+
+        return pollfd.revents;
+}
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+        FILE *f;
+        char *t;
+        int fd;
+
+        assert(path);
+        assert(_f);
+        assert(_temp_path);
+
+        t = tempfn_xxxxxx(path);
+        if (!t)
+                return -ENOMEM;
+
+        fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
+        if (fd < 0) {
+                free(t);
+                return -errno;
+        }
+
+        f = fdopen(fd, "we");
+        if (!f) {
+                unlink(t);
+                free(t);
+                return -errno;
+        }
+
+        *_f = f;
+        *_temp_path = t;
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int terminal_vhangup_fd(int fd) {
+        assert(fd >= 0);
+
+        if (ioctl(fd, TIOCVHANGUP) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int terminal_vhangup(const char *name) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        return terminal_vhangup_fd(fd);
+}
+
+int vt_disallocate(const char *name) {
+        int fd, r;
+        unsigned u;
+
+        /* Deallocate the VT if possible. If not possible
+         * (i.e. because it is the active one), at least clear it
+         * entirely (including the scrollback buffer) */
+
+        if (!startswith(name, "/dev/"))
+                return -EINVAL;
+
+        if (!tty_is_vc(name)) {
+                /* So this is not a VT. I guess we cannot deallocate
+                 * it then. But let's at least clear the screen */
+
+                fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+                if (fd < 0)
+                        return fd;
+
+                loop_write(fd,
+                           "\033[r"    /* clear scrolling region */
+                           "\033[H"    /* move home */
+                           "\033[2J",  /* clear screen */
+                           10, false);
+                safe_close(fd);
+
+                return 0;
+        }
+
+        if (!startswith(name, "/dev/tty"))
+                return -EINVAL;
+
+        r = safe_atou(name+8, &u);
+        if (r < 0)
+                return r;
+
+        if (u <= 0)
+                return -EINVAL;
+
+        /* Try to deallocate */
+        fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        r = ioctl(fd, VT_DISALLOCATE, u);
+        safe_close(fd);
+
+        if (r >= 0)
+                return 0;
+
+        if (errno != EBUSY)
+                return -errno;
+
+        /* Couldn't deallocate, so let's clear it fully with
+         * scrollback */
+        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        loop_write(fd,
+                   "\033[r"   /* clear scrolling region */
+                   "\033[H"   /* move home */
+                   "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
+                   10, false);
+        safe_close(fd);
+
+        return 0;
+}
+
+int symlink_atomic(const char *from, const char *to) {
+        _cleanup_free_ char *t = NULL;
+
+        assert(from);
+        assert(to);
+
+        t = tempfn_random(to);
+        if (!t)
+                return -ENOMEM;
+
+        if (symlink(from, t) < 0)
+                return -errno;
+
+        if (rename(t, to) < 0) {
+                unlink_noerrno(t);
+                return -errno;
+        }
+
+        return 0;
+}
+
+int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
+        _cleanup_free_ char *t = NULL;
+
+        assert(path);
+
+        t = tempfn_random(path);
+        if (!t)
+                return -ENOMEM;
+
+        if (mknod(t, mode, dev) < 0)
+                return -errno;
+
+        if (rename(t, path) < 0) {
+                unlink_noerrno(t);
+                return -errno;
+        }
+
+        return 0;
+}
+
+int mkfifo_atomic(const char *path, mode_t mode) {
+        _cleanup_free_ char *t = NULL;
+
+        assert(path);
+
+        t = tempfn_random(path);
+        if (!t)
+                return -ENOMEM;
+
+        if (mkfifo(t, mode) < 0)
+                return -errno;
+
+        if (rename(t, path) < 0) {
+                unlink_noerrno(t);
+                return -errno;
+        }
+
+        return 0;
+}
+
+bool display_is_local(const char *display) {
+        assert(display);
+
+        return
+                display[0] == ':' &&
+                display[1] >= '0' &&
+                display[1] <= '9';
+}
+
+int socket_from_display(const char *display, char **path) {
+        size_t k;
+        char *f, *c;
+
+        assert(display);
+        assert(path);
+
+        if (!display_is_local(display))
+                return -EINVAL;
+
+        k = strspn(display+1, "0123456789");
+
+        f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
+        if (!f)
+                return -ENOMEM;
+
+        c = stpcpy(f, "/tmp/.X11-unix/X");
+        memcpy(c, display+1, k);
+        c[k] = 0;
+
+        *path = f;
+
+        return 0;
+}
+
+int get_user_creds(
+                const char **username,
+                uid_t *uid, gid_t *gid,
+                const char **home,
+                const char **shell) {
+
+        struct passwd *p;
+        uid_t u;
+
+        assert(username);
+        assert(*username);
+
+        /* We enforce some special rules for uid=0: in order to avoid
+         * NSS lookups for root we hardcode its data. */
+
+        if (streq(*username, "root") || streq(*username, "0")) {
+                *username = "root";
+
+                if (uid)
+                        *uid = 0;
+
+                if (gid)
+                        *gid = 0;
+
+                if (home)
+                        *home = "/root";
+
+                if (shell)
+                        *shell = "/bin/sh";
+
+                return 0;
+        }
+
+        if (parse_uid(*username, &u) >= 0) {
+                errno = 0;
+                p = getpwuid(u);
+
+                /* If there are multiple users with the same id, make
+                 * sure to leave $USER to the configured value instead
+                 * of the first occurrence in the database. However if
+                 * the uid was configured by a numeric uid, then let's
+                 * pick the real username from /etc/passwd. */
+                if (p)
+                        *username = p->pw_name;
+        } else {
+                errno = 0;
+                p = getpwnam(*username);
+        }
+
+        if (!p)
+                return errno > 0 ? -errno : -ESRCH;
+
+        if (uid)
+                *uid = p->pw_uid;
+
+        if (gid)
+                *gid = p->pw_gid;
+
+        if (home)
+                *home = p->pw_dir;
+
+        if (shell)
+                *shell = p->pw_shell;
+
+        return 0;
+}
+
+char* uid_to_name(uid_t uid) {
+        struct passwd *p;
+        char *r;
+
+        if (uid == 0)
+                return strdup("root");
+
+        p = getpwuid(uid);
+        if (p)
+                return strdup(p->pw_name);
+
+        if (asprintf(&r, UID_FMT, uid) < 0)
+                return NULL;
+
+        return r;
+}
+
+char* gid_to_name(gid_t gid) {
+        struct group *p;
+        char *r;
+
+        if (gid == 0)
+                return strdup("root");
+
+        p = getgrgid(gid);
+        if (p)
+                return strdup(p->gr_name);
+
+        if (asprintf(&r, GID_FMT, gid) < 0)
+                return NULL;
+
+        return r;
+}
+
+int get_group_creds(const char **groupname, gid_t *gid) {
+        struct group *g;
+        gid_t id;
+
+        assert(groupname);
+
+        /* We enforce some special rules for gid=0: in order to avoid
+         * NSS lookups for root we hardcode its data. */
+
+        if (streq(*groupname, "root") || streq(*groupname, "0")) {
+                *groupname = "root";
+
+                if (gid)
+                        *gid = 0;
+
+                return 0;
+        }
+
+        if (parse_gid(*groupname, &id) >= 0) {
+                errno = 0;
+                g = getgrgid(id);
+
+                if (g)
+                        *groupname = g->gr_name;
+        } else {
+                errno = 0;
+                g = getgrnam(*groupname);
+        }
+
+        if (!g)
+                return errno > 0 ? -errno : -ESRCH;
+
+        if (gid)
+                *gid = g->gr_gid;
+
+        return 0;
+}
+
+int in_gid(gid_t gid) {
+        gid_t *gids;
+        int ngroups_max, r, i;
+
+        if (getgid() == gid)
+                return 1;
+
+        if (getegid() == gid)
+                return 1;
+
+        ngroups_max = sysconf(_SC_NGROUPS_MAX);
+        assert(ngroups_max > 0);
+
+        gids = alloca(sizeof(gid_t) * ngroups_max);
+
+        r = getgroups(ngroups_max, gids);
+        if (r < 0)
+                return -errno;
+
+        for (i = 0; i < r; i++)
+                if (gids[i] == gid)
+                        return 1;
+
+        return 0;
+}
+
+int in_group(const char *name) {
+        int r;
+        gid_t gid;
+
+        r = get_group_creds(&name, &gid);
+        if (r < 0)
+                return r;
+
+        return in_gid(gid);
+}
+
+int glob_exists(const char *path) {
+        _cleanup_globfree_ glob_t g = {};
+        int k;
+
+        assert(path);
+
+        errno = 0;
+        k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+        if (k == GLOB_NOMATCH)
+                return 0;
+        else if (k == GLOB_NOSPACE)
+                return -ENOMEM;
+        else if (k == 0)
+                return !strv_isempty(g.gl_pathv);
+        else
+                return errno ? -errno : -EIO;
+}
+
+int glob_extend(char ***strv, const char *path) {
+        _cleanup_globfree_ glob_t g = {};
+        int k;
+        char **p;
+
+        errno = 0;
+        k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+        if (k == GLOB_NOMATCH)
+                return -ENOENT;
+        else if (k == GLOB_NOSPACE)
+                return -ENOMEM;
+        else if (k != 0 || strv_isempty(g.gl_pathv))
+                return errno ? -errno : -EIO;
+
+        STRV_FOREACH(p, g.gl_pathv) {
+                k = strv_extend(strv, *p);
+                if (k < 0)
+                        break;
+        }
+
+        return k;
+}
+
+int dirent_ensure_type(DIR *d, struct dirent *de) {
+        struct stat st;
+
+        assert(d);
+        assert(de);
+
+        if (de->d_type != DT_UNKNOWN)
+                return 0;
+
+        if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+                return -errno;
+
+        de->d_type =
+                S_ISREG(st.st_mode)  ? DT_REG  :
+                S_ISDIR(st.st_mode)  ? DT_DIR  :
+                S_ISLNK(st.st_mode)  ? DT_LNK  :
+                S_ISFIFO(st.st_mode) ? DT_FIFO :
+                S_ISSOCK(st.st_mode) ? DT_SOCK :
+                S_ISCHR(st.st_mode)  ? DT_CHR  :
+                S_ISBLK(st.st_mode)  ? DT_BLK  :
+                                       DT_UNKNOWN;
+
+        return 0;
+}
+
+int get_files_in_directory(const char *path, char ***list) {
+        _cleanup_closedir_ DIR *d = NULL;
+        size_t bufsize = 0, n = 0;
+        _cleanup_strv_free_ char **l = NULL;
+
+        assert(path);
+
+        /* Returns all files in a directory in *list, and the number
+         * of files as return value. If list is NULL returns only the
+         * number. */
+
+        d = opendir(path);
+        if (!d)
+                return -errno;
+
+        for (;;) {
+                struct dirent *de;
+
+                errno = 0;
+                de = readdir(d);
+                if (!de && errno != 0)
+                        return -errno;
+                if (!de)
+                        break;
+
+                dirent_ensure_type(d, de);
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                if (list) {
+                        /* one extra slot is needed for the terminating NULL */
+                        if (!GREEDY_REALLOC(l, bufsize, n + 2))
+                                return -ENOMEM;
+
+                        l[n] = strdup(de->d_name);
+                        if (!l[n])
+                                return -ENOMEM;
+
+                        l[++n] = NULL;
+                } else
+                        n++;
+        }
+
+        if (list) {
+                *list = l;
+                l = NULL; /* avoid freeing */
+        }
+
+        return n;
+}
+#endif
+
+char *strjoin(const char *x, ...) {
+        va_list ap;
+        size_t l;
+        char *r, *p;
+
+        va_start(ap, x);
+
+        if (x) {
+                l = strlen(x);
+
+                for (;;) {
+                        const char *t;
+                        size_t n;
+
+                        t = va_arg(ap, const char *);
+                        if (!t)
+                                break;
+
+                        n = strlen(t);
+                        if (n > ((size_t) -1) - l) {
+                                va_end(ap);
+                                return NULL;
+                        }
+
+                        l += n;
+                }
+        } else
+                l = 0;
+
+        va_end(ap);
+
+        r = new(char, l+1);
+        if (!r)
+                return NULL;
+
+        if (x) {
+                p = stpcpy(r, x);
+
+                va_start(ap, x);
+
+                for (;;) {
+                        const char *t;
+
+                        t = va_arg(ap, const char *);
+                        if (!t)
+                                break;
+
+                        p = stpcpy(p, t);
+                }
+
+                va_end(ap);
+        } else
+                r[0] = 0;
+
+        return r;
+}
+
+#if 0 /* NM_IGNORED */
+bool is_main_thread(void) {
+        static thread_local int cached = 0;
+
+        if (_unlikely_(cached == 0))
+                cached = getpid() == gettid() ? 1 : -1;
+
+        return cached > 0;
+}
+
+int block_get_whole_disk(dev_t d, dev_t *ret) {
+        char *p, *s;
+        int r;
+        unsigned n, m;
+
+        assert(ret);
+
+        /* If it has a queue this is good enough for us */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r >= 0) {
+                *ret = d;
+                return 0;
+        }
+
+        /* If it is a partition find the originating device */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r < 0)
+                return -ENOENT;
+
+        /* Get parent dev_t */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = read_one_line_file(p, &s);
+        free(p);
+
+        if (r < 0)
+                return r;
+
+        r = sscanf(s, "%u:%u", &m, &n);
+        free(s);
+
+        if (r != 2)
+                return -EINVAL;
+
+        /* Only return this if it is really good enough for us. */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r >= 0) {
+                *ret = makedev(m, n);
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
+static const char *const ioprio_class_table[] = {
+        [IOPRIO_CLASS_NONE] = "none",
+        [IOPRIO_CLASS_RT] = "realtime",
+        [IOPRIO_CLASS_BE] = "best-effort",
+        [IOPRIO_CLASS_IDLE] = "idle"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
+
+static const char *const sigchld_code_table[] = {
+        [CLD_EXITED] = "exited",
+        [CLD_KILLED] = "killed",
+        [CLD_DUMPED] = "dumped",
+        [CLD_TRAPPED] = "trapped",
+        [CLD_STOPPED] = "stopped",
+        [CLD_CONTINUED] = "continued",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
+
+static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
+        [LOG_FAC(LOG_KERN)] = "kern",
+        [LOG_FAC(LOG_USER)] = "user",
+        [LOG_FAC(LOG_MAIL)] = "mail",
+        [LOG_FAC(LOG_DAEMON)] = "daemon",
+        [LOG_FAC(LOG_AUTH)] = "auth",
+        [LOG_FAC(LOG_SYSLOG)] = "syslog",
+        [LOG_FAC(LOG_LPR)] = "lpr",
+        [LOG_FAC(LOG_NEWS)] = "news",
+        [LOG_FAC(LOG_UUCP)] = "uucp",
+        [LOG_FAC(LOG_CRON)] = "cron",
+        [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
+        [LOG_FAC(LOG_FTP)] = "ftp",
+        [LOG_FAC(LOG_LOCAL0)] = "local0",
+        [LOG_FAC(LOG_LOCAL1)] = "local1",
+        [LOG_FAC(LOG_LOCAL2)] = "local2",
+        [LOG_FAC(LOG_LOCAL3)] = "local3",
+        [LOG_FAC(LOG_LOCAL4)] = "local4",
+        [LOG_FAC(LOG_LOCAL5)] = "local5",
+        [LOG_FAC(LOG_LOCAL6)] = "local6",
+        [LOG_FAC(LOG_LOCAL7)] = "local7"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0));
+
+static const char *const log_level_table[] = {
+        [LOG_EMERG] = "emerg",
+        [LOG_ALERT] = "alert",
+        [LOG_CRIT] = "crit",
+        [LOG_ERR] = "err",
+        [LOG_WARNING] = "warning",
+        [LOG_NOTICE] = "notice",
+        [LOG_INFO] = "info",
+        [LOG_DEBUG] = "debug"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
+
+static const char* const sched_policy_table[] = {
+        [SCHED_OTHER] = "other",
+        [SCHED_BATCH] = "batch",
+        [SCHED_IDLE] = "idle",
+        [SCHED_FIFO] = "fifo",
+        [SCHED_RR] = "rr"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
+
+static const char* const rlimit_table[_RLIMIT_MAX] = {
+        [RLIMIT_CPU] = "LimitCPU",
+        [RLIMIT_FSIZE] = "LimitFSIZE",
+        [RLIMIT_DATA] = "LimitDATA",
+        [RLIMIT_STACK] = "LimitSTACK",
+        [RLIMIT_CORE] = "LimitCORE",
+        [RLIMIT_RSS] = "LimitRSS",
+        [RLIMIT_NOFILE] = "LimitNOFILE",
+        [RLIMIT_AS] = "LimitAS",
+        [RLIMIT_NPROC] = "LimitNPROC",
+        [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
+        [RLIMIT_LOCKS] = "LimitLOCKS",
+        [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
+        [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
+        [RLIMIT_NICE] = "LimitNICE",
+        [RLIMIT_RTPRIO] = "LimitRTPRIO",
+        [RLIMIT_RTTIME] = "LimitRTTIME"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
+
+static const char* const ip_tos_table[] = {
+        [IPTOS_LOWDELAY] = "low-delay",
+        [IPTOS_THROUGHPUT] = "throughput",
+        [IPTOS_RELIABILITY] = "reliability",
+        [IPTOS_LOWCOST] = "low-cost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
+
+static const char *const __signal_table[] = {
+        [SIGHUP] = "HUP",
+        [SIGINT] = "INT",
+        [SIGQUIT] = "QUIT",
+        [SIGILL] = "ILL",
+        [SIGTRAP] = "TRAP",
+        [SIGABRT] = "ABRT",
+        [SIGBUS] = "BUS",
+        [SIGFPE] = "FPE",
+        [SIGKILL] = "KILL",
+        [SIGUSR1] = "USR1",
+        [SIGSEGV] = "SEGV",
+        [SIGUSR2] = "USR2",
+        [SIGPIPE] = "PIPE",
+        [SIGALRM] = "ALRM",
+        [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+        [SIGSTKFLT] = "STKFLT",  /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+        [SIGCHLD] = "CHLD",
+        [SIGCONT] = "CONT",
+        [SIGSTOP] = "STOP",
+        [SIGTSTP] = "TSTP",
+        [SIGTTIN] = "TTIN",
+        [SIGTTOU] = "TTOU",
+        [SIGURG] = "URG",
+        [SIGXCPU] = "XCPU",
+        [SIGXFSZ] = "XFSZ",
+        [SIGVTALRM] = "VTALRM",
+        [SIGPROF] = "PROF",
+        [SIGWINCH] = "WINCH",
+        [SIGIO] = "IO",
+        [SIGPWR] = "PWR",
+        [SIGSYS] = "SYS"
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+
+const char *signal_to_string(int signo) {
+        static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
+        const char *name;
+
+        name = __signal_to_string(signo);
+        if (name)
+                return name;
+
+        if (signo >= SIGRTMIN && signo <= SIGRTMAX)
+                snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
+        else
+                snprintf(buf, sizeof(buf), "%d", signo);
+
+        return buf;
+}
+
+int signal_from_string(const char *s) {
+        int signo;
+        int offset = 0;
+        unsigned u;
+
+        signo = __signal_from_string(s);
+        if (signo > 0)
+                return signo;
+
+        if (startswith(s, "RTMIN+")) {
+                s += 6;
+                offset = SIGRTMIN;
+        }
+        if (safe_atou(s, &u) >= 0) {
+                signo = (int) u + offset;
+                if (signo > 0 && signo < _NSIG)
+                        return signo;
+        }
+        return -EINVAL;
+}
+
+bool kexec_loaded(void) {
+       bool loaded = false;
+       char *s;
+
+       if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
+               if (s[0] == '1')
+                       loaded = true;
+               free(s);
+       }
+       return loaded;
+}
+
+int prot_from_flags(int flags) {
+
+        switch (flags & O_ACCMODE) {
+
+        case O_RDONLY:
+                return PROT_READ;
+
+        case O_WRONLY:
+                return PROT_WRITE;
+
+        case O_RDWR:
+                return PROT_READ|PROT_WRITE;
+
+        default:
+                return -EINVAL;
+        }
+}
+
+char *format_bytes(char *buf, size_t l, off_t t) {
+        unsigned i;
+
+        static const struct {
+                const char *suffix;
+                off_t factor;
+        } table[] = {
+                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+                { "G", 1024ULL*1024ULL*1024ULL },
+                { "M", 1024ULL*1024ULL },
+                { "K", 1024ULL },
+        };
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+
+                if (t >= table[i].factor) {
+                        snprintf(buf, l,
+                                 "%llu.%llu%s",
+                                 (unsigned long long) (t / table[i].factor),
+                                 (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL),
+                                 table[i].suffix);
+
+                        goto finish;
+                }
+        }
+
+        snprintf(buf, l, "%lluB", (unsigned long long) t);
+
+finish:
+        buf[l-1] = 0;
+        return buf;
+
+}
+#endif
+
+void* memdup(const void *p, size_t l) {
+        void *r;
+
+        assert(p);
+
+        r = malloc(l);
+        if (!r)
+                return NULL;
+
+        memcpy(r, p, l);
+        return r;
+}
+
+#if 0 /* NM_IGNORED */
+int fd_inc_sndbuf(int fd, size_t n) {
+        int r, value;
+        socklen_t l = sizeof(value);
+
+        r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+        if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+                return 0;
+
+        /* If we have the privileges we will ignore the kernel limit. */
+
+        value = (int) n;
+        if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+                        return -errno;
+
+        return 1;
+}
+
+int fd_inc_rcvbuf(int fd, size_t n) {
+        int r, value;
+        socklen_t l = sizeof(value);
+
+        r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+        if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+                return 0;
+
+        /* If we have the privileges we will ignore the kernel limit. */
+
+        value = (int) n;
+        if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+                        return -errno;
+        return 1;
+}
+
+int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
+        bool stdout_is_tty, stderr_is_tty;
+        pid_t parent_pid, agent_pid;
+        sigset_t ss, saved_ss;
+        unsigned n, i;
+        va_list ap;
+        char **l;
+
+        assert(pid);
+        assert(path);
+
+        /* Spawns a temporary TTY agent, making sure it goes away when
+         * we go away */
+
+        parent_pid = getpid();
+
+        /* First we temporarily block all signals, so that the new
+         * child has them blocked initially. This way, we can be sure
+         * that SIGTERMs are not lost we might send to the agent. */
+        assert_se(sigfillset(&ss) >= 0);
+        assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
+
+        agent_pid = fork();
+        if (agent_pid < 0) {
+                assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
+                return -errno;
+        }
+
+        if (agent_pid != 0) {
+                assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
+                *pid = agent_pid;
+                return 0;
+        }
+
+        /* In the child:
+         *
+         * Make sure the agent goes away when the parent dies */
+        if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+                _exit(EXIT_FAILURE);
+
+        /* Make sure we actually can kill the agent, if we need to, in
+         * case somebody invoked us from a shell script that trapped
+         * SIGTERM or so... */
+        reset_all_signal_handlers();
+        reset_signal_mask();
+
+        /* Check whether our parent died before we were able
+         * to set the death signal and unblock the signals */
+        if (getppid() != parent_pid)
+                _exit(EXIT_SUCCESS);
+
+        /* Don't leak fds to the agent */
+        close_all_fds(except, n_except);
+
+        stdout_is_tty = isatty(STDOUT_FILENO);
+        stderr_is_tty = isatty(STDERR_FILENO);
+
+        if (!stdout_is_tty || !stderr_is_tty) {
+                int fd;
+
+                /* Detach from stdout/stderr. and reopen
+                 * /dev/tty for them. This is important to
+                 * ensure that when systemctl is started via
+                 * popen() or a similar call that expects to
+                 * read EOF we actually do generate EOF and
+                 * not delay this indefinitely by because we
+                 * keep an unused copy of stdin around. */
+                fd = open("/dev/tty", O_WRONLY);
+                if (fd < 0) {
+                        log_error("Failed to open /dev/tty: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (!stdout_is_tty)
+                        dup2(fd, STDOUT_FILENO);
+
+                if (!stderr_is_tty)
+                        dup2(fd, STDERR_FILENO);
+
+                if (fd > 2)
+                        close(fd);
+        }
+
+        /* Count arguments */
+        va_start(ap, path);
+        for (n = 0; va_arg(ap, char*); n++)
+                ;
+        va_end(ap);
+
+        /* Allocate strv */
+        l = alloca(sizeof(char *) * (n + 1));
+
+        /* Fill in arguments */
+        va_start(ap, path);
+        for (i = 0; i <= n; i++)
+                l[i] = va_arg(ap, char*);
+        va_end(ap);
+
+        execv(path, l);
+        _exit(EXIT_FAILURE);
+}
+
+int setrlimit_closest(int resource, const struct rlimit *rlim) {
+        struct rlimit highest, fixed;
+
+        assert(rlim);
+
+        if (setrlimit(resource, rlim) >= 0)
+                return 0;
+
+        if (errno != EPERM)
+                return -errno;
+
+        /* So we failed to set the desired setrlimit, then let's try
+         * to get as close as we can */
+        assert_se(getrlimit(resource, &highest) == 0);
+
+        fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
+        fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
+
+        if (setrlimit(resource, &fixed) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int getenv_for_pid(pid_t pid, const char *field, char **_value) {
+        _cleanup_fclose_ FILE *f = NULL;
+        char *value = NULL;
+        int r;
+        bool done = false;
+        size_t l;
+        const char *path;
+
+        assert(pid >= 0);
+        assert(field);
+        assert(_value);
+
+        path = procfs_file_alloca(pid, "environ");
+
+        f = fopen(path, "re");
+        if (!f)
+                return -errno;
+
+        l = strlen(field);
+        r = 0;
+
+        do {
+                char line[LINE_MAX];
+                unsigned i;
+
+                for (i = 0; i < sizeof(line)-1; i++) {
+                        int c;
+
+                        c = getc(f);
+                        if (_unlikely_(c == EOF)) {
+                                done = true;
+                                break;
+                        } else if (c == 0)
+                                break;
+
+                        line[i] = c;
+                }
+                line[i] = 0;
+
+                if (memcmp(line, field, l) == 0 && line[l] == '=') {
+                        value = strdup(line + l + 1);
+                        if (!value)
+                                return -ENOMEM;
+
+                        r = 1;
+                        break;
+                }
+
+        } while (!done);
+
+        *_value = value;
+        return r;
+}
+
+bool is_valid_documentation_url(const char *url) {
+        assert(url);
+
+        if (startswith(url, "http://") && url[7])
+                return true;
+
+        if (startswith(url, "https://") && url[8])
+                return true;
+
+        if (startswith(url, "file:") && url[5])
+                return true;
+
+        if (startswith(url, "info:") && url[5])
+                return true;
+
+        if (startswith(url, "man:") && url[4])
+                return true;
+
+        return false;
+}
+
+bool in_initrd(void) {
+        static int saved = -1;
+        struct statfs s;
+
+        if (saved >= 0)
+                return saved;
+
+        /* We make two checks here:
+         *
+         * 1. the flag file /etc/initrd-release must exist
+         * 2. the root file system must be a memory file system
+         *
+         * The second check is extra paranoia, since misdetecting an
+         * initrd can have bad bad consequences due the initrd
+         * emptying when transititioning to the main systemd.
+         */
+
+        saved = access("/etc/initrd-release", F_OK) >= 0 &&
+                statfs("/", &s) >= 0 &&
+                is_temporary_fs(&s);
+
+        return saved;
+}
+
+void warn_melody(void) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return;
+
+        /* Yeah, this is synchronous. Kinda sucks. But well... */
+
+        ioctl(fd, KIOCSOUND, (int)(1193180/440));
+        usleep(125*USEC_PER_MSEC);
+
+        ioctl(fd, KIOCSOUND, (int)(1193180/220));
+        usleep(125*USEC_PER_MSEC);
+
+        ioctl(fd, KIOCSOUND, (int)(1193180/220));
+        usleep(125*USEC_PER_MSEC);
+
+        ioctl(fd, KIOCSOUND, 0);
+}
+
+int make_console_stdio(void) {
+        int fd, r;
+
+        /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+
+        fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
+        if (fd < 0) {
+                log_error("Failed to acquire terminal: %s", strerror(-fd));
+                return fd;
+        }
+
+        r = make_stdio(fd);
+        if (r < 0) {
+                log_error("Failed to duplicate terminal fd: %s", strerror(-r));
+                return r;
+        }
+
+        return 0;
+}
+
+int get_home_dir(char **_h) {
+        struct passwd *p;
+        const char *e;
+        char *h;
+        uid_t u;
+
+        assert(_h);
+
+        /* Take the user specified one */
+        e = secure_getenv("HOME");
+        if (e && path_is_absolute(e)) {
+                h = strdup(e);
+                if (!h)
+                        return -ENOMEM;
+
+                *_h = h;
+                return 0;
+        }
+
+        /* Hardcode home directory for root to avoid NSS */
+        u = getuid();
+        if (u == 0) {
+                h = strdup("/root");
+                if (!h)
+                        return -ENOMEM;
+
+                *_h = h;
+                return 0;
+        }
+
+        /* Check the database... */
+        errno = 0;
+        p = getpwuid(u);
+        if (!p)
+                return errno > 0 ? -errno : -ESRCH;
+
+        if (!path_is_absolute(p->pw_dir))
+                return -EINVAL;
+
+        h = strdup(p->pw_dir);
+        if (!h)
+                return -ENOMEM;
+
+        *_h = h;
+        return 0;
+}
+
+int get_shell(char **_s) {
+        struct passwd *p;
+        const char *e;
+        char *s;
+        uid_t u;
+
+        assert(_s);
+
+        /* Take the user specified one */
+        e = getenv("SHELL");
+        if (e) {
+                s = strdup(e);
+                if (!s)
+                        return -ENOMEM;
+
+                *_s = s;
+                return 0;
+        }
+
+        /* Hardcode home directory for root to avoid NSS */
+        u = getuid();
+        if (u == 0) {
+                s = strdup("/bin/sh");
+                if (!s)
+                        return -ENOMEM;
+
+                *_s = s;
+                return 0;
+        }
+
+        /* Check the database... */
+        errno = 0;
+        p = getpwuid(u);
+        if (!p)
+                return errno > 0 ? -errno : -ESRCH;
+
+        if (!path_is_absolute(p->pw_shell))
+                return -EINVAL;
+
+        s = strdup(p->pw_shell);
+        if (!s)
+                return -ENOMEM;
+
+        *_s = s;
+        return 0;
+}
+
+bool filename_is_safe(const char *p) {
+
+        if (isempty(p))
+                return false;
+
+        if (strchr(p, '/'))
+                return false;
+
+        if (streq(p, "."))
+                return false;
+
+        if (streq(p, ".."))
+                return false;
+
+        if (strlen(p) > FILENAME_MAX)
+                return false;
+
+        return true;
+}
+
+bool string_is_safe(const char *p) {
+        const char *t;
+
+        if (!p)
+                return false;
+
+        for (t = p; *t; t++) {
+                if (*t > 0 && *t < ' ')
+                        return false;
+
+                if (strchr("\\\"\'\0x7f", *t))
+                        return false;
+        }
+
+        return true;
+}
+#endif
+
+/**
+ * Check if a string contains control characters. If 'ok' is non-NULL
+ * it may be a string containing additional CCs to be considered OK.
+ */
+bool string_has_cc(const char *p, const char *ok) {
+        const char *t;
+
+        assert(p);
+
+        for (t = p; *t; t++) {
+                if (ok && strchr(ok, *t))
+                        continue;
+
+                if (*t > 0 && *t < ' ')
+                        return true;
+
+                if (*t == 127)
+                        return true;
+        }
+
+        return false;
+}
+
+#if 0 /* NM_IGNORED */
+bool path_is_safe(const char *p) {
+
+        if (isempty(p))
+                return false;
+
+        if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+                return false;
+
+        if (strlen(p) > PATH_MAX)
+                return false;
+
+        /* The following two checks are not really dangerous, but hey, they still are confusing */
+        if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+                return false;
+
+        if (strstr(p, "//"))
+                return false;
+
+        return true;
+}
+
+/* hey glibc, APIs with callbacks without a user pointer are so useless */
+void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
+                 int (*compar) (const void *, const void *, void *), void *arg) {
+        size_t l, u, idx;
+        const void *p;
+        int comparison;
+
+        l = 0;
+        u = nmemb;
+        while (l < u) {
+                idx = (l + u) / 2;
+                p = (void *)(((const char *) base) + (idx * size));
+                comparison = compar(key, p, arg);
+                if (comparison < 0)
+                        u = idx;
+                else if (comparison > 0)
+                        l = idx + 1;
+                else
+                        return (void *)p;
+        }
+        return NULL;
+}
+
+bool is_locale_utf8(void) {
+        const char *set;
+        static int cached_answer = -1;
+
+        if (cached_answer >= 0)
+                goto out;
+
+        if (!setlocale(LC_ALL, "")) {
+                cached_answer = true;
+                goto out;
+        }
+
+        set = nl_langinfo(CODESET);
+        if (!set) {
+                cached_answer = true;
+                goto out;
+        }
+
+        if (streq(set, "UTF-8")) {
+                cached_answer = true;
+                goto out;
+        }
+
+        /* For LC_CTYPE=="C" return true, because CTYPE is effectly
+         * unset and everything can do to UTF-8 nowadays. */
+        set = setlocale(LC_CTYPE, NULL);
+        if (!set) {
+                cached_answer = true;
+                goto out;
+        }
+
+        /* Check result, but ignore the result if C was set
+         * explicitly. */
+        cached_answer =
+                streq(set, "C") &&
+                !getenv("LC_ALL") &&
+                !getenv("LC_CTYPE") &&
+                !getenv("LANG");
+
+out:
+        return (bool) cached_answer;
+}
+
+const char *draw_special_char(DrawSpecialChar ch) {
+        static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
+
+                /* UTF-8 */ {
+                        [DRAW_TREE_VERTICAL]      = "\342\224\202 ",            /* │  */
+                        [DRAW_TREE_BRANCH]        = "\342\224\234\342\224\200", /* ├─ */
+                        [DRAW_TREE_RIGHT]         = "\342\224\224\342\224\200", /* └─ */
+                        [DRAW_TREE_SPACE]         = "  ",                       /*    */
+                        [DRAW_TRIANGULAR_BULLET]  = "\342\200\243",             /* ‣ */
+                        [DRAW_BLACK_CIRCLE]       = "\342\227\217",             /* ● */
+                        [DRAW_ARROW]              = "\342\206\222",             /* → */
+                        [DRAW_DASH]               = "\342\200\223",             /* – */
+                },
+
+                /* ASCII fallback */ {
+                        [DRAW_TREE_VERTICAL]      = "| ",
+                        [DRAW_TREE_BRANCH]        = "|-",
+                        [DRAW_TREE_RIGHT]         = "`-",
+                        [DRAW_TREE_SPACE]         = "  ",
+                        [DRAW_TRIANGULAR_BULLET]  = ">",
+                        [DRAW_BLACK_CIRCLE]       = "*",
+                        [DRAW_ARROW]              = "->",
+                        [DRAW_DASH]               = "-",
+                }
+        };
+
+        return draw_table[!is_locale_utf8()][ch];
+}
+
+char *strreplace(const char *text, const char *old_string, const char *new_string) {
+        const char *f;
+        char *t, *r;
+        size_t l, old_len, new_len;
+
+        assert(text);
+        assert(old_string);
+        assert(new_string);
+
+        old_len = strlen(old_string);
+        new_len = strlen(new_string);
+
+        l = strlen(text);
+        r = new(char, l+1);
+        if (!r)
+                return NULL;
+
+        f = text;
+        t = r;
+        while (*f) {
+                char *a;
+                size_t d, nl;
+
+                if (!startswith(f, old_string)) {
+                        *(t++) = *(f++);
+                        continue;
+                }
+
+                d = t - r;
+                nl = l - old_len + new_len;
+                a = realloc(r, nl + 1);
+                if (!a)
+                        goto oom;
+
+                l = nl;
+                r = a;
+                t = r + d;
+
+                t = stpcpy(t, new_string);
+                f += old_len;
+        }
+
+        *t = 0;
+        return r;
+
+oom:
+        free(r);
+        return NULL;
+}
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+        const char *i, *begin = NULL;
+        enum {
+                STATE_OTHER,
+                STATE_ESCAPE,
+                STATE_BRACKET
+        } state = STATE_OTHER;
+        char *obuf = NULL;
+        size_t osz = 0, isz;
+        FILE *f;
+
+        assert(ibuf);
+        assert(*ibuf);
+
+        /* Strips ANSI color and replaces TABs by 8 spaces */
+
+        isz = _isz ? *_isz : strlen(*ibuf);
+
+        f = open_memstream(&obuf, &osz);
+        if (!f)
+                return NULL;
+
+        for (i = *ibuf; i < *ibuf + isz + 1; i++) {
+
+                switch (state) {
+
+                case STATE_OTHER:
+                        if (i >= *ibuf + isz) /* EOT */
+                                break;
+                        else if (*i == '\x1B')
+                                state = STATE_ESCAPE;
+                        else if (*i == '\t')
+                                fputs("        ", f);
+                        else
+                                fputc(*i, f);
+                        break;
+
+                case STATE_ESCAPE:
+                        if (i >= *ibuf + isz) { /* EOT */
+                                fputc('\x1B', f);
+                                break;
+                        } else if (*i == '[') {
+                                state = STATE_BRACKET;
+                                begin = i + 1;
+                        } else {
+                                fputc('\x1B', f);
+                                fputc(*i, f);
+                                state = STATE_OTHER;
+                        }
+
+                        break;
+
+                case STATE_BRACKET:
+
+                        if (i >= *ibuf + isz || /* EOT */
+                            (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
+                                fputc('\x1B', f);
+                                fputc('[', f);
+                                state = STATE_OTHER;
+                                i = begin-1;
+                        } else if (*i == 'm')
+                                state = STATE_OTHER;
+                        break;
+                }
+        }
+
+        if (ferror(f)) {
+                fclose(f);
+                free(obuf);
+                return NULL;
+        }
+
+        fclose(f);
+
+        free(*ibuf);
+        *ibuf = obuf;
+
+        if (_isz)
+                *_isz = osz;
+
+        return obuf;
+}
+
+int on_ac_power(void) {
+        bool found_offline = false, found_online = false;
+        _cleanup_closedir_ DIR *d = NULL;
+
+        d = opendir("/sys/class/power_supply");
+        if (!d)
+                return -errno;
+
+        for (;;) {
+                struct dirent *de;
+                _cleanup_close_ int fd = -1, device = -1;
+                char contents[6];
+                ssize_t n;
+
+                errno = 0;
+                de = readdir(d);
+                if (!de && errno != 0)
+                        return -errno;
+
+                if (!de)
+                        break;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (device < 0) {
+                        if (errno == ENOENT || errno == ENOTDIR)
+                                continue;
+
+                        return -errno;
+                }
+
+                fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (fd < 0) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                n = read(fd, contents, sizeof(contents));
+                if (n < 0)
+                        return -errno;
+
+                if (n != 6 || memcmp(contents, "Mains\n", 6))
+                        continue;
+
+                safe_close(fd);
+                fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (fd < 0) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                n = read(fd, contents, sizeof(contents));
+                if (n < 0)
+                        return -errno;
+
+                if (n != 2 || contents[1] != '\n')
+                        return -EIO;
+
+                if (contents[0] == '1') {
+                        found_online = true;
+                        break;
+                } else if (contents[0] == '0')
+                        found_offline = true;
+                else
+                        return -EIO;
+        }
+
+        return found_online || !found_offline;
+}
+
+static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
+        char **i;
+
+        assert(path);
+        assert(mode);
+        assert(_f);
+
+        if (!path_strv_resolve_uniq(search, root))
+                return -ENOMEM;
+
+        STRV_FOREACH(i, search) {
+                _cleanup_free_ char *p = NULL;
+                FILE *f;
+
+                if (root)
+                        p = strjoin(root, *i, "/", path, NULL);
+                else
+                        p = strjoin(*i, "/", path, NULL);
+                if (!p)
+                        return -ENOMEM;
+
+                f = fopen(p, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                if (errno != ENOENT)
+                        return -errno;
+        }
+
+        return -ENOENT;
+}
+
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
+        _cleanup_strv_free_ char **copy = NULL;
+
+        assert(path);
+        assert(mode);
+        assert(_f);
+
+        if (path_is_absolute(path)) {
+                FILE *f;
+
+                f = fopen(path, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                return -errno;
+        }
+
+        copy = strv_copy((char**) search);
+        if (!copy)
+                return -ENOMEM;
+
+        return search_and_fopen_internal(path, mode, root, copy, _f);
+}
+
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
+        _cleanup_strv_free_ char **s = NULL;
+
+        if (path_is_absolute(path)) {
+                FILE *f;
+
+                f = fopen(path, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                return -errno;
+        }
+
+        s = strv_split_nulstr(search);
+        if (!s)
+                return -ENOMEM;
+
+        return search_and_fopen_internal(path, mode, root, s, _f);
+}
+
+char *strextend(char **x, ...) {
+        va_list ap;
+        size_t f, l;
+        char *r, *p;
+
+        assert(x);
+
+        l = f = *x ? strlen(*x) : 0;
+
+        va_start(ap, x);
+        for (;;) {
+                const char *t;
+                size_t n;
+
+                t = va_arg(ap, const char *);
+                if (!t)
+                        break;
+
+                n = strlen(t);
+                if (n > ((size_t) -1) - l) {
+                        va_end(ap);
+                        return NULL;
+                }
+
+                l += n;
+        }
+        va_end(ap);
+
+        r = realloc(*x, l+1);
+        if (!r)
+                return NULL;
+
+        p = r + f;
+
+        va_start(ap, x);
+        for (;;) {
+                const char *t;
+
+                t = va_arg(ap, const char *);
+                if (!t)
+                        break;
+
+                p = stpcpy(p, t);
+        }
+        va_end(ap);
+
+        *p = 0;
+        *x = r;
+
+        return r + l;
+}
+
+char *strrep(const char *s, unsigned n) {
+        size_t l;
+        char *r, *p;
+        unsigned i;
+
+        assert(s);
+
+        l = strlen(s);
+        p = r = malloc(l * n + 1);
+        if (!r)
+                return NULL;
+
+        for (i = 0; i < n; i++)
+                p = stpcpy(p, s);
+
+        *p = 0;
+        return r;
+}
+#endif
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
+        size_t a, newalloc;
+        void *q;
+
+        assert(p);
+        assert(allocated);
+
+        if (*allocated >= need)
+                return *p;
+
+        newalloc = MAX(need * 2, 64u / size);
+        a = newalloc * size;
+
+        /* check for overflows */
+        if (a < size * need)
+                return NULL;
+
+        q = realloc(*p, a);
+        if (!q)
+                return NULL;
+
+        *p = q;
+        *allocated = newalloc;
+        return q;
+}
+
+#if 0 /* NM_IGNORED */
+void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
+        size_t prev;
+        uint8_t *q;
+
+        assert(p);
+        assert(allocated);
+
+        prev = *allocated;
+
+        q = greedy_realloc(p, allocated, need, size);
+        if (!q)
+                return NULL;
+
+        if (*allocated > prev)
+                memzero(q + prev * size, (*allocated - prev) * size);
+
+        return q;
+}
+
+bool id128_is_valid(const char *s) {
+        size_t i, l;
+
+        l = strlen(s);
+        if (l == 32) {
+
+                /* Simple formatted 128bit hex string */
+
+                for (i = 0; i < l; i++) {
+                        char c = s[i];
+
+                        if (!(c >= '0' && c <= '9') &&
+                            !(c >= 'a' && c <= 'z') &&
+                            !(c >= 'A' && c <= 'Z'))
+                                return false;
+                }
+
+        } else if (l == 36) {
+
+                /* Formatted UUID */
+
+                for (i = 0; i < l; i++) {
+                        char c = s[i];
+
+                        if ((i == 8 || i == 13 || i == 18 || i == 23)) {
+                                if (c != '-')
+                                        return false;
+                        } else {
+                                if (!(c >= '0' && c <= '9') &&
+                                    !(c >= 'a' && c <= 'z') &&
+                                    !(c >= 'A' && c <= 'Z'))
+                                        return false;
+                        }
+                }
+
+        } else
+                return false;
+
+        return true;
+}
+
+int split_pair(const char *s, const char *sep, char **l, char **r) {
+        char *x, *a, *b;
+
+        assert(s);
+        assert(sep);
+        assert(l);
+        assert(r);
+
+        if (isempty(sep))
+                return -EINVAL;
+
+        x = strstr(s, sep);
+        if (!x)
+                return -EINVAL;
+
+        a = strndup(s, x - s);
+        if (!a)
+                return -ENOMEM;
+
+        b = strdup(x + strlen(sep));
+        if (!b) {
+                free(a);
+                return -ENOMEM;
+        }
+
+        *l = a;
+        *r = b;
+
+        return 0;
+}
+
+int shall_restore_state(void) {
+        _cleanup_free_ char *line = NULL;
+        const char *p;
+        int r;
+
+        r = proc_cmdline(&line);
+        if (r < 0)
+                return r;
+
+        r = 1;
+        p = line;
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+                const char *e;
+                int k;
+
+                k = unquote_first_word(&p, &word, true);
+                if (k < 0)
+                        return k;
+                if (k == 0)
+                        break;
+
+                e = startswith(word, "systemd.restore_state=");
+                if (!e)
+                        continue;
+
+                k = parse_boolean(e);
+                if (k >= 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+int proc_cmdline(char **ret) {
+        assert(ret);
+
+        if (detect_container(NULL) > 0)
+                return get_process_cmdline(1, 0, false, ret);
+        else
+                return read_one_line_file("/proc/cmdline", ret);
+}
+
+int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
+        _cleanup_free_ char *line = NULL;
+        const char *p;
+        int r;
+
+        assert(parse_item);
+
+        r = proc_cmdline(&line);
+        if (r < 0)
+                return r;
+
+        p = line;
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+                char *value = NULL;
+
+                r = unquote_first_word(&p, &word, true);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                /* Filter out arguments that are intended only for the
+                 * initrd */
+                if (!in_initrd() && startswith(word, "rd."))
+                        continue;
+
+                value = strchr(word, '=');
+                if (value)
+                        *(value++) = 0;
+
+                r = parse_item(word, value);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int container_get_leader(const char *machine, pid_t *pid) {
+        _cleanup_free_ char *s = NULL, *class = NULL;
+        const char *p;
+        pid_t leader;
+        int r;
+
+        assert(machine);
+        assert(pid);
+
+        p = strappenda("/run/systemd/machines/", machine);
+        r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
+        if (r == -ENOENT)
+                return -EHOSTDOWN;
+        if (r < 0)
+                return r;
+        if (!s)
+                return -EIO;
+
+        if (!streq_ptr(class, "container"))
+                return -EIO;
+
+        r = parse_pid(s, &leader);
+        if (r < 0)
+                return r;
+        if (leader <= 1)
+                return -EIO;
+
+        *pid = leader;
+        return 0;
+}
+
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd) {
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1;
+        int rfd = -1;
+
+        assert(pid >= 0);
+
+        if (mntns_fd) {
+                const char *mntns;
+
+                mntns = procfs_file_alloca(pid, "ns/mnt");
+                mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                if (mntnsfd < 0)
+                        return -errno;
+        }
+
+        if (pidns_fd) {
+                const char *pidns;
+
+                pidns = procfs_file_alloca(pid, "ns/pid");
+                pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                if (pidnsfd < 0)
+                        return -errno;
+        }
+
+        if (netns_fd) {
+                const char *netns;
+
+                netns = procfs_file_alloca(pid, "ns/net");
+                netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                if (netnsfd < 0)
+                        return -errno;
+        }
+
+        if (root_fd) {
+                const char *root;
+
+                root = procfs_file_alloca(pid, "root");
+                rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+                if (rfd < 0)
+                        return -errno;
+        }
+
+        if (pidns_fd)
+                *pidns_fd = pidnsfd;
+
+        if (mntns_fd)
+                *mntns_fd = mntnsfd;
+
+        if (netns_fd)
+                *netns_fd = netnsfd;
+
+        if (root_fd)
+                *root_fd = rfd;
+
+        pidnsfd = mntnsfd = netnsfd = -1;
+
+        return 0;
+}
+
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
+
+        if (pidns_fd >= 0)
+                if (setns(pidns_fd, CLONE_NEWPID) < 0)
+                        return -errno;
+
+        if (mntns_fd >= 0)
+                if (setns(mntns_fd, CLONE_NEWNS) < 0)
+                        return -errno;
+
+        if (netns_fd >= 0)
+                if (setns(netns_fd, CLONE_NEWNET) < 0)
+                        return -errno;
+
+        if (root_fd >= 0) {
+                if (fchdir(root_fd) < 0)
+                        return -errno;
+
+                if (chroot(".") < 0)
+                        return -errno;
+        }
+
+        if (setresgid(0, 0, 0) < 0)
+                return -errno;
+
+        if (setgroups(0, NULL) < 0)
+                return -errno;
+
+        if (setresuid(0, 0, 0) < 0)
+                return -errno;
+
+        return 0;
+}
+
+bool pid_is_unwaited(pid_t pid) {
+        /* Checks whether a PID is still valid at all, including a zombie */
+
+        if (pid <= 0)
+                return false;
+
+        if (kill(pid, 0) >= 0)
+                return true;
+
+        return errno != ESRCH;
+}
+
+bool pid_is_alive(pid_t pid) {
+        int r;
+
+        /* Checks whether a PID is still valid and not a zombie */
+
+        if (pid <= 0)
+                return false;
+
+        r = get_process_state(pid);
+        if (r == -ENOENT || r == 'Z')
+                return false;
+
+        return true;
+}
+
+int getpeercred(int fd, struct ucred *ucred) {
+        socklen_t n = sizeof(struct ucred);
+        struct ucred u;
+        int r;
+
+        assert(fd >= 0);
+        assert(ucred);
+
+        r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
+        if (r < 0)
+                return -errno;
+
+        if (n != sizeof(struct ucred))
+                return -EIO;
+
+        /* Check if the data is actually useful and not suppressed due
+         * to namespacing issues */
+        if (u.pid <= 0)
+                return -ENODATA;
+
+        *ucred = u;
+        return 0;
+}
+
+int getpeersec(int fd, char **ret) {
+        socklen_t n = 64;
+        char *s;
+        int r;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        s = new0(char, n);
+        if (!s)
+                return -ENOMEM;
+
+        r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+        if (r < 0) {
+                free(s);
+
+                if (errno != ERANGE)
+                        return -errno;
+
+                s = new0(char, n);
+                if (!s)
+                        return -ENOMEM;
+
+                r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+                if (r < 0) {
+                        free(s);
+                        return -errno;
+                }
+        }
+
+        if (isempty(s)) {
+                free(s);
+                return -ENOTSUP;
+        }
+
+        *ret = s;
+        return 0;
+}
+#endif
+
+/* This is much like like mkostemp() but is subject to umask(). */
+int mkostemp_safe(char *pattern, int flags) {
+        _cleanup_umask_ mode_t u;
+        int fd;
+
+        assert(pattern);
+
+        u = umask(077);
+
+        fd = mkostemp(pattern, flags);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
+#if 0 /* NM_IGNORED */
+int open_tmpfile(const char *path, int flags) {
+        char *p;
+        int fd;
+
+        assert(path);
+
+#ifdef O_TMPFILE
+        /* Try O_TMPFILE first, if it is supported */
+        fd = open(path, flags|O_TMPFILE, S_IRUSR|S_IWUSR);
+        if (fd >= 0)
+                return fd;
+#endif
+
+        /* Fall back to unguessable name + unlinking */
+        p = strappenda(path, "/systemd-tmp-XXXXXX");
+
+        fd = mkostemp_safe(p, flags);
+        if (fd < 0)
+                return fd;
+
+        unlink(p);
+        return fd;
+}
+
+int fd_warn_permissions(const char *path, int fd) {
+        struct stat st;
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (st.st_mode & 0111)
+                log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
+
+        if (st.st_mode & 0002)
+                log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
+
+        if (getpid() == 1 && (st.st_mode & 0044) != 0044)
+                log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
+
+        return 0;
+}
+
+unsigned long personality_from_string(const char *p) {
+
+        /* Parse a personality specifier. We introduce our own
+         * identifiers that indicate specific ABIs, rather than just
+         * hints regarding the register size, since we want to keep
+         * things open for multiple locally supported ABIs for the
+         * same register size. We try to reuse the ABI identifiers
+         * used by libseccomp. */
+
+#if defined(__x86_64__)
+
+        if (streq(p, "x86"))
+                return PER_LINUX32;
+
+        if (streq(p, "x86-64"))
+                return PER_LINUX;
+
+#elif defined(__i386__)
+
+        if (streq(p, "x86"))
+                return PER_LINUX;
+#endif
+
+        /* personality(7) documents that 0xffffffffUL is used for
+         * querying the current personality, hence let's use that here
+         * as error indicator. */
+        return 0xffffffffUL;
+}
+
+const char* personality_to_string(unsigned long p) {
+
+#if defined(__x86_64__)
+
+        if (p == PER_LINUX32)
+                return "x86";
+
+        if (p == PER_LINUX)
+                return "x86-64";
+
+#elif defined(__i386__)
+
+        if (p == PER_LINUX)
+                return "x86";
+#endif
+
+        return NULL;
+}
+
+uint64_t physical_memory(void) {
+        long mem;
+
+        /* We return this as uint64_t in case we are running as 32bit
+         * process on a 64bit kernel with huge amounts of memory */
+
+        mem = sysconf(_SC_PHYS_PAGES);
+        assert(mem > 0);
+
+        return (uint64_t) mem * (uint64_t) page_size();
+}
+
+char* mount_test_option(const char *haystack, const char *needle) {
+
+        struct mntent me = {
+                .mnt_opts = (char*) haystack
+        };
+
+        assert(needle);
+
+        /* Like glibc's hasmntopt(), but works on a string, not a
+         * struct mntent */
+
+        if (!haystack)
+                return NULL;
+
+        return hasmntopt(&me, needle);
+}
+
+void hexdump(FILE *f, const void *p, size_t s) {
+        const uint8_t *b = p;
+        unsigned n = 0;
+
+        assert(s == 0 || b);
+
+        while (s > 0) {
+                size_t i;
+
+                fprintf(f, "%04x  ", n);
+
+                for (i = 0; i < 16; i++) {
+
+                        if (i >= s)
+                                fputs("   ", f);
+                        else
+                                fprintf(f, "%02x ", b[i]);
+
+                        if (i == 7)
+                                fputc(' ', f);
+                }
+
+                fputc(' ', f);
+
+                for (i = 0; i < 16; i++) {
+
+                        if (i >= s)
+                                fputc(' ', f);
+                        else
+                                fputc(isprint(b[i]) ? (char) b[i] : '.', f);
+                }
+
+                fputc('\n', f);
+
+                if (s < 16)
+                        break;
+
+                n += 16;
+                b += 16;
+                s -= 16;
+        }
+}
+
+int update_reboot_param_file(const char *param) {
+        int r = 0;
+
+        if (param) {
+
+                r = write_string_file(REBOOT_PARAM_FILE, param);
+                if (r < 0)
+                        log_error("Failed to write reboot param to "
+                                  REBOOT_PARAM_FILE": %s", strerror(-r));
+        } else
+                unlink(REBOOT_PARAM_FILE);
+
+        return r;
+}
+
+int umount_recursive(const char *prefix, int flags) {
+        bool again;
+        int n = 0, r;
+
+        /* Try to umount everything recursively below a
+         * directory. Also, take care of stacked mounts, and keep
+         * unmounting them until they are gone. */
+
+        do {
+                _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+
+                again = false;
+                r = 0;
+
+                proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+                if (!proc_self_mountinfo)
+                        return -errno;
+
+                for (;;) {
+                        _cleanup_free_ char *path = NULL, *p = NULL;
+                        int k;
+
+                        k = fscanf(proc_self_mountinfo,
+                                   "%*s "       /* (1) mount id */
+                                   "%*s "       /* (2) parent id */
+                                   "%*s "       /* (3) major:minor */
+                                   "%*s "       /* (4) root */
+                                   "%ms "       /* (5) mount point */
+                                   "%*s"        /* (6) mount options */
+                                   "%*[^-]"     /* (7) optional fields */
+                                   "- "         /* (8) separator */
+                                   "%*s "       /* (9) file system type */
+                                   "%*s"        /* (10) mount source */
+                                   "%*s"        /* (11) mount options 2 */
+                                   "%*[^\n]",   /* some rubbish at the end */
+                                   &path);
+                        if (k != 1) {
+                                if (k == EOF)
+                                        break;
+
+                                continue;
+                        }
+
+                        p = cunescape(path);
+                        if (!p)
+                                return -ENOMEM;
+
+                        if (!path_startswith(p, prefix))
+                                continue;
+
+                        if (umount2(p, flags) < 0) {
+                                r = -errno;
+                                continue;
+                        }
+
+                        again = true;
+                        n++;
+
+                        break;
+                }
+
+        } while (again);
+
+        return r ? r : n;
+}
+
+int bind_remount_recursive(const char *prefix, bool ro) {
+        _cleanup_set_free_free_ Set *done = NULL;
+        _cleanup_free_ char *cleaned = NULL;
+        int r;
+
+        /* Recursively remount a directory (and all its submounts)
+         * read-only or read-write. If the directory is already
+         * mounted, we reuse the mount and simply mark it
+         * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
+         * operation). If it isn't we first make it one. Afterwards we
+         * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
+         * submounts we can access, too. When mounts are stacked on
+         * the same mount point we only care for each individual
+         * "top-level" mount on each point, as we cannot
+         * influence/access the underlying mounts anyway. We do not
+         * have any effect on future submounts that might get
+         * propagated, they migt be writable. This includes future
+         * submounts that have been triggered via autofs. */
+
+        cleaned = strdup(prefix);
+        if (!cleaned)
+                return -ENOMEM;
+
+        path_kill_slashes(cleaned);
+
+        done = set_new(&string_hash_ops);
+        if (!done)
+                return -ENOMEM;
+
+        for (;;) {
+                _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+                _cleanup_set_free_free_ Set *todo = NULL;
+                bool top_autofs = false;
+                char *x;
+
+                todo = set_new(&string_hash_ops);
+                if (!todo)
+                        return -ENOMEM;
+
+                proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+                if (!proc_self_mountinfo)
+                        return -errno;
+
+                for (;;) {
+                        _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
+                        int k;
+
+                        k = fscanf(proc_self_mountinfo,
+                                   "%*s "       /* (1) mount id */
+                                   "%*s "       /* (2) parent id */
+                                   "%*s "       /* (3) major:minor */
+                                   "%*s "       /* (4) root */
+                                   "%ms "       /* (5) mount point */
+                                   "%*s"        /* (6) mount options (superblock) */
+                                   "%*[^-]"     /* (7) optional fields */
+                                   "- "         /* (8) separator */
+                                   "%ms "       /* (9) file system type */
+                                   "%*s"        /* (10) mount source */
+                                   "%*s"        /* (11) mount options (bind mount) */
+                                   "%*[^\n]",   /* some rubbish at the end */
+                                   &path,
+                                   &type);
+                        if (k != 2) {
+                                if (k == EOF)
+                                        break;
+
+                                continue;
+                        }
+
+                        p = cunescape(path);
+                        if (!p)
+                                return -ENOMEM;
+
+                        /* Let's ignore autofs mounts.  If they aren't
+                         * triggered yet, we want to avoid triggering
+                         * them, as we don't make any guarantees for
+                         * future submounts anyway.  If they are
+                         * already triggered, then we will find
+                         * another entry for this. */
+                        if (streq(type, "autofs")) {
+                                top_autofs = top_autofs || path_equal(cleaned, p);
+                                continue;
+                        }
+
+                        if (path_startswith(p, cleaned) &&
+                            !set_contains(done, p)) {
+
+                                r = set_consume(todo, p);
+                                p = NULL;
+
+                                if (r == -EEXIST)
+                                        continue;
+                                if (r < 0)
+                                        return r;
+                        }
+                }
+
+                /* If we have no submounts to process anymore and if
+                 * the root is either already done, or an autofs, we
+                 * are done */
+                if (set_isempty(todo) &&
+                    (top_autofs || set_contains(done, cleaned)))
+                        return 0;
+
+                if (!set_contains(done, cleaned) &&
+                    !set_contains(todo, cleaned)) {
+                        /* The prefix directory itself is not yet a
+                         * mount, make it one. */
+                        if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
+                                return -errno;
+
+                        if (mount(NULL, prefix, NULL, MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
+                                return -errno;
+
+                        x = strdup(cleaned);
+                        if (!x)
+                                return -ENOMEM;
+
+                        r = set_consume(done, x);
+                        if (r < 0)
+                                return r;
+                }
+
+                while ((x = set_steal_first(todo))) {
+
+                        r = set_consume(done, x);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
+
+                        if (mount(NULL, x, NULL, MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) {
+
+                                /* Deal with mount points that are
+                                 * obstructed by a later mount */
+
+                                if (errno != ENOENT)
+                                        return -errno;
+                        }
+
+                }
+        }
+}
+#endif
+
+int fflush_and_check(FILE *f) {
+        assert(f);
+
+        errno = 0;
+        fflush(f);
+
+        if (ferror(f))
+                return errno ? -errno : -EIO;
+
+        return 0;
+}
+
+char *tempfn_xxxxxx(const char *p) {
+        const char *fn;
+        char *t;
+        size_t k;
+
+        assert(p);
+
+        t = new(char, strlen(p) + 1 + 6 + 1);
+        if (!t)
+                return NULL;
+
+        fn = basename(p);
+        k = fn - p;
+
+        strcpy(stpcpy(stpcpy(mempcpy(t, p, k), "."), fn), "XXXXXX");
+
+        return t;
+}
+
+#if 0 /* NM_IGNORED */
+char *tempfn_random(const char *p) {
+        const char *fn;
+        char *t, *x;
+        uint64_t u;
+        size_t k;
+        unsigned i;
+
+        assert(p);
+
+        t = new(char, strlen(p) + 1 + 16 + 1);
+        if (!t)
+                return NULL;
+
+        fn = basename(p);
+        k = fn - p;
+
+        x = stpcpy(stpcpy(mempcpy(t, p, k), "."), fn);
+
+        u = random_u64();
+        for (i = 0; i < 16; i++) {
+                *(x++) = hexchar(u & 0xF);
+                u >>= 4;
+        }
+
+        *x = 0;
+
+        return t;
+}
+#endif
+
+/* make sure the hostname is not "localhost" */
+bool is_localhost(const char *hostname) {
+        assert(hostname);
+
+        /* This tries to identify local host and domain names
+         * described in RFC6761 plus the redhatism of .localdomain */
+
+        return streq(hostname, "localhost") ||
+               streq(hostname, "localhost.") ||
+               streq(hostname, "localdomain.") ||
+               streq(hostname, "localdomain") ||
+               endswith(hostname, ".localhost") ||
+               endswith(hostname, ".localhost.") ||
+               endswith(hostname, ".localdomain") ||
+               endswith(hostname, ".localdomain.");
+}
+
+#if 0 /* NM_IGNORED */
+int take_password_lock(const char *root) {
+
+        struct flock flock = {
+                .l_type = F_WRLCK,
+                .l_whence = SEEK_SET,
+                .l_start = 0,
+                .l_len = 0,
+        };
+
+        const char *path;
+        int fd, r;
+
+        /* This is roughly the same as lckpwdf(), but not as awful. We
+         * don't want to use alarm() and signals, hence we implement
+         * our own trivial version of this.
+         *
+         * Note that shadow-utils also takes per-database locks in
+         * addition to lckpwdf(). However, we don't given that they
+         * are redundant as they they invoke lckpwdf() first and keep
+         * it during everything they do. The per-database locks are
+         * awfully racy, and thus we just won't do them. */
+
+        if (root)
+                path = strappenda(root, "/etc/.pwd.lock");
+        else
+                path = "/etc/.pwd.lock";
+
+        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+        if (fd < 0)
+                return -errno;
+
+        r = fcntl(fd, F_SETLKW, &flock);
+        if (r < 0) {
+                safe_close(fd);
+                return -errno;
+        }
+
+        return fd;
+}
+
+int is_symlink(const char *path) {
+        struct stat info;
+
+        if (lstat(path, &info) < 0)
+                return -errno;
+
+        return !!S_ISLNK(info.st_mode);
+}
+
+int is_dir(const char* path, bool follow) {
+        struct stat st;
+        int r;
+
+        if (follow)
+                r = stat(path, &st);
+        else
+                r = lstat(path, &st);
+        if (r < 0)
+                return -errno;
+
+        return !!S_ISDIR(st.st_mode);
+}
+
+int unquote_first_word(const char **p, char **ret, bool relax) {
+        _cleanup_free_ char *s = NULL;
+        size_t allocated = 0, sz = 0;
+
+        enum {
+                START,
+                VALUE,
+                VALUE_ESCAPE,
+                SINGLE_QUOTE,
+                SINGLE_QUOTE_ESCAPE,
+                DOUBLE_QUOTE,
+                DOUBLE_QUOTE_ESCAPE,
+                SPACE,
+        } state = START;
+
+        assert(p);
+        assert(*p);
+        assert(ret);
+
+        /* Parses the first word of a string, and returns it in
+         * *ret. Removes all quotes in the process. When parsing fails
+         * (because of an uneven number of quotes or similar), leaves
+         * the pointer *p at the first invalid character. */
+
+        for (;;) {
+                char c = **p;
+
+                switch (state) {
+
+                case START:
+                        if (c == 0)
+                                goto finish;
+                        else if (strchr(WHITESPACE, c))
+                                break;
+
+                        state = VALUE;
+                        /* fallthrough */
+
+                case VALUE:
+                        if (c == 0)
+                                goto finish;
+                        else if (c == '\'')
+                                state = SINGLE_QUOTE;
+                        else if (c == '\\')
+                                state = VALUE_ESCAPE;
+                        else if (c == '\"')
+                                state = DOUBLE_QUOTE;
+                        else if (strchr(WHITESPACE, c))
+                                state = SPACE;
+                        else {
+                                if (!GREEDY_REALLOC(s, allocated, sz+2))
+                                        return -ENOMEM;
+
+                                s[sz++] = c;
+                        }
+
+                        break;
+
+                case VALUE_ESCAPE:
+                        if (c == 0) {
+                                if (relax)
+                                        goto finish;
+                                return -EINVAL;
+                        }
+
+                        if (!GREEDY_REALLOC(s, allocated, sz+2))
+                                return -ENOMEM;
+
+                        s[sz++] = c;
+                        state = VALUE;
+
+                        break;
+
+                case SINGLE_QUOTE:
+                        if (c == 0) {
+                                if (relax)
+                                        goto finish;
+                                return -EINVAL;
+                        } else if (c == '\'')
+                                state = VALUE;
+                        else if (c == '\\')
+                                state = SINGLE_QUOTE_ESCAPE;
+                        else {
+                                if (!GREEDY_REALLOC(s, allocated, sz+2))
+                                        return -ENOMEM;
+
+                                s[sz++] = c;
+                        }
+
+                        break;
+
+                case SINGLE_QUOTE_ESCAPE:
+                        if (c == 0) {
+                                if (relax)
+                                        goto finish;
+                                return -EINVAL;
+                        }
+
+                        if (!GREEDY_REALLOC(s, allocated, sz+2))
+                                return -ENOMEM;
+
+                        s[sz++] = c;
+                        state = SINGLE_QUOTE;
+                        break;
+
+                case DOUBLE_QUOTE:
+                        if (c == 0)
+                                return -EINVAL;
+                        else if (c == '\"')
+                                state = VALUE;
+                        else if (c == '\\')
+                                state = DOUBLE_QUOTE_ESCAPE;
+                        else {
+                                if (!GREEDY_REALLOC(s, allocated, sz+2))
+                                        return -ENOMEM;
+
+                                s[sz++] = c;
+                        }
+
+                        break;
+
+                case DOUBLE_QUOTE_ESCAPE:
+                        if (c == 0) {
+                                if (relax)
+                                        goto finish;
+                                return -EINVAL;
+                        }
+
+                        if (!GREEDY_REALLOC(s, allocated, sz+2))
+                                return -ENOMEM;
+
+                        s[sz++] = c;
+                        state = DOUBLE_QUOTE;
+                        break;
+
+                case SPACE:
+                        if (c == 0)
+                                goto finish;
+                        if (!strchr(WHITESPACE, c))
+                                goto finish;
+
+                        break;
+                }
+
+                (*p) ++;
+        }
+
+finish:
+        if (!s) {
+                *ret = NULL;
+                return 0;
+        }
+
+        s[sz] = 0;
+        *ret = s;
+        s = NULL;
+
+        return 1;
+}
+
+int unquote_many_words(const char **p, ...) {
+        va_list ap;
+        char **l;
+        int n = 0, i, c, r;
+
+        /* Parses a number of words from a string, stripping any
+         * quotes if necessary. */
+
+        assert(p);
+
+        /* Count how many words are expected */
+        va_start(ap, p);
+        for (;;) {
+                if (!va_arg(ap, char **))
+                        break;
+                n++;
+        }
+        va_end(ap);
+
+        if (n <= 0)
+                return 0;
+
+        /* Read all words into a temporary array */
+        l = newa0(char*, n);
+        for (c = 0; c < n; c++) {
+
+                r = unquote_first_word(p, &l[c], false);
+                if (r < 0) {
+                        int j;
+
+                        for (j = 0; j < c; j++)
+                                free(l[j]);
+
+                        return r;
+                }
+
+                if (r == 0)
+                        break;
+        }
+
+        /* If we managed to parse all words, return them in the passed
+         * in parameters */
+        va_start(ap, p);
+        for (i = 0; i < n; i++) {
+                char **v;
+
+                v = va_arg(ap, char **);
+                assert(v);
+
+                *v = l[i];
+        }
+        va_end(ap);
+
+        return c;
+}
+
+int free_and_strdup(char **p, const char *s) {
+        char *t;
+
+        assert(p);
+
+        /* Replaces a string pointer with an strdup()ed new string,
+         * possibly freeing the old one. */
+
+        if (s) {
+                t = strdup(s);
+                if (!t)
+                        return -ENOMEM;
+        } else
+                t = NULL;
+
+        free(*p);
+        *p = t;
+
+        return 0;
+}
+
+int sethostname_idempotent(const char *s) {
+        int r;
+        char buf[HOST_NAME_MAX + 1] = {};
+
+        assert(s);
+
+        r = gethostname(buf, sizeof(buf));
+        if (r < 0)
+                return -errno;
+
+        if (streq(buf, s))
+                return 0;
+
+        r = sethostname(s, strlen(s));
+        if (r < 0)
+                return -errno;
+
+        return 1;
+}
+#endif
+
diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/util.h b/src/dhcp-manager/systemd-dhcp/src/shared/util.h
new file mode 100644
index 00000000..a7690655
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/shared/util.h
@@ -0,0 +1,1038 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <alloca.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sched.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/resource.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <locale.h>
+#include <mntent.h>
+#include <sys/socket.h>
+
+#if 0 /* NM_IGNORED */
+#if SIZEOF_PID_T == 4
+#  define PID_FMT "%" PRIu32
+#elif SIZEOF_PID_T == 2
+#  define PID_FMT "%" PRIu16
+#else
+#  error Unknown pid_t size
+#endif
+
+#if SIZEOF_UID_T == 4
+#  define UID_FMT "%" PRIu32
+#elif SIZEOF_UID_T == 2
+#  define UID_FMT "%" PRIu16
+#else
+#  error Unknown uid_t size
+#endif
+
+#if SIZEOF_GID_T == 4
+#  define GID_FMT "%" PRIu32
+#elif SIZEOF_GID_T == 2
+#  define GID_FMT "%" PRIu16
+#else
+#  error Unknown gid_t size
+#endif
+
+#if SIZEOF_TIME_T == 8
+#  define PRI_TIME PRIu64
+#elif SIZEOF_TIME_T == 4
+#  define PRI_TIME PRIu32
+#else
+#  error Unknown time_t size
+#endif
+
+#if SIZEOF_RLIM_T == 8
+#  define RLIM_FMT "%" PRIu64
+#elif SIZEOF_RLIM_T == 4
+#  define RLIM_FMT "%" PRIu32
+#else
+#  error Unknown rlim_t size
+#endif
+#endif
+
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#endif
+#include "time-util.h"
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE " \t\n\r"
+#define NEWLINE    "\n\r"
+#define QUOTES     "\"\'"
+#define COMMENTS   "#;"
+#define GLOB_CHARS "*?["
+
+/* What characters are special in the shell? */
+/* must be escaped outside and inside double-quotes */
+#define SHELL_NEED_ESCAPE "\"\\`$"
+/* can be escaped or double-quoted */
+#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;"
+
+#define FORMAT_BYTES_MAX 8
+
+#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
+#define ANSI_RED_ON "\x1B[31m"
+#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
+#define ANSI_GREEN_ON "\x1B[32m"
+#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
+#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m"
+#define ANSI_HIGHLIGHT_BLUE_ON "\x1B[1;34m"
+#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
+#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
+
+size_t page_size(void);
+#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
+
+bool streq_ptr(const char *a, const char *b) _pure_;
+
+#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+
+#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n)))
+
+#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+
+#define malloc0(n) (calloc((n), 1))
+
+static inline const char* yes_no(bool b) {
+        return b ? "yes" : "no";
+}
+
+static inline const char* true_false(bool b) {
+        return b ? "true" : "false";
+}
+
+static inline const char* strempty(const char *s) {
+        return s ? s : "";
+}
+
+static inline const char* strnull(const char *s) {
+        return s ? s : "(null)";
+}
+
+static inline const char *strna(const char *s) {
+        return s ? s : "n/a";
+}
+
+static inline bool isempty(const char *p) {
+        return !p || !p[0];
+}
+
+static inline char *startswith(const char *s, const char *prefix) {
+        size_t l;
+
+        l = strlen(prefix);
+        if (strncmp(s, prefix, l) == 0)
+                return (char*) s + l;
+
+        return NULL;
+}
+
+static inline char *startswith_no_case(const char *s, const char *prefix) {
+        size_t l;
+
+        l = strlen(prefix);
+        if (strncasecmp(s, prefix, l) == 0)
+                return (char*) s + l;
+
+        return NULL;
+}
+
+char *endswith(const char *s, const char *postfix) _pure_;
+
+char *first_word(const char *s, const char *word) _pure_;
+
+int close_nointr(int fd);
+int safe_close(int fd);
+void safe_close_pair(int p[]);
+
+void close_many(const int fds[], unsigned n_fd);
+
+int parse_size(const char *t, off_t base, off_t *size);
+
+int parse_boolean(const char *v) _pure_;
+int parse_pid(const char *s, pid_t* ret_pid);
+int parse_uid(const char *s, uid_t* ret_uid);
+#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
+
+int safe_atou(const char *s, unsigned *ret_u);
+int safe_atoi(const char *s, int *ret_i);
+
+int safe_atollu(const char *s, unsigned long long *ret_u);
+int safe_atolli(const char *s, long long int *ret_i);
+
+int safe_atod(const char *s, double *ret_d);
+
+int safe_atou8(const char *s, uint8_t *ret);
+
+#if LONG_MAX == INT_MAX
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+        assert_cc(sizeof(unsigned long) == sizeof(unsigned));
+        return safe_atou(s, (unsigned*) ret_u);
+}
+static inline int safe_atoli(const char *s, long int *ret_u) {
+        assert_cc(sizeof(long int) == sizeof(int));
+        return safe_atoi(s, (int*) ret_u);
+}
+#else
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+        assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
+        return safe_atollu(s, (unsigned long long*) ret_u);
+}
+static inline int safe_atoli(const char *s, long int *ret_u) {
+        assert_cc(sizeof(long int) == sizeof(long long int));
+        return safe_atolli(s, (long long int*) ret_u);
+}
+#endif
+
+static inline int safe_atou32(const char *s, uint32_t *ret_u) {
+        assert_cc(sizeof(uint32_t) == sizeof(unsigned));
+        return safe_atou(s, (unsigned*) ret_u);
+}
+
+static inline int safe_atoi32(const char *s, int32_t *ret_i) {
+        assert_cc(sizeof(int32_t) == sizeof(int));
+        return safe_atoi(s, (int*) ret_i);
+}
+
+static inline int safe_atou64(const char *s, uint64_t *ret_u) {
+        assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
+        return safe_atollu(s, (unsigned long long*) ret_u);
+}
+
+static inline int safe_atoi64(const char *s, int64_t *ret_i) {
+        assert_cc(sizeof(int64_t) == sizeof(long long int));
+        return safe_atolli(s, (long long int*) ret_i);
+}
+
+int safe_atou16(const char *s, uint16_t *ret);
+int safe_atoi16(const char *s, int16_t *ret);
+
+const char* split(const char **state, size_t *l, const char *separator, bool quoted);
+
+#define FOREACH_WORD(word, length, s, state)                            \
+        _FOREACH_WORD(word, length, s, WHITESPACE, false, state)
+
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state)       \
+        _FOREACH_WORD(word, length, s, separator, false, state)
+
+#define FOREACH_WORD_QUOTED(word, length, s, state)                     \
+        _FOREACH_WORD(word, length, s, WHITESPACE, true, state)
+
+#define _FOREACH_WORD(word, length, s, separator, quoted, state)        \
+        for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
+
+pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
+int get_starttime_of_pid(pid_t pid, unsigned long long *st);
+
+char *strappend(const char *s, const char *suffix);
+char *strnappend(const char *s, const char *suffix, size_t length);
+
+char *replace_env(const char *format, char **env);
+char **replace_env_argv(char **argv, char **env);
+
+int readlinkat_malloc(int fd, const char *p, char **ret);
+int readlink_malloc(const char *p, char **r);
+int readlink_value(const char *p, char **ret);
+int readlink_and_make_absolute(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, char **r);
+
+int reset_all_signal_handlers(void);
+int reset_signal_mask(void);
+
+char *strstrip(char *s);
+char *delete_chars(char *s, const char *bad);
+char *truncate_nl(char *s);
+
+char *file_in_same_dir(const char *path, const char *filename);
+
+int rmdir_parents(const char *path, const char *stop);
+
+int get_process_state(pid_t pid);
+int get_process_comm(pid_t pid, char **name);
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_exe(pid_t pid, char **name);
+int get_process_uid(pid_t pid, uid_t *uid);
+int get_process_gid(pid_t pid, gid_t *gid);
+int get_process_capeff(pid_t pid, char **capeff);
+
+char hexchar(int x) _const_;
+int unhexchar(char c) _const_;
+char octchar(int x) _const_;
+int unoctchar(char c) _const_;
+char decchar(int x) _const_;
+int undecchar(char c) _const_;
+
+char *cescape(const char *s);
+char *cunescape(const char *s);
+char *cunescape_length(const char *s, size_t length);
+char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix);
+
+char *xescape(const char *s, const char *bad);
+
+char *ascii_strlower(char *path);
+
+bool dirent_is_file(const struct dirent *de) _pure_;
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
+
+bool ignore_file(const char *filename) _pure_;
+
+bool chars_intersect(const char *a, const char *b) _pure_;
+
+int make_stdio(int fd);
+int make_null_stdio(void);
+int make_console_stdio(void);
+
+int dev_urandom(void *p, size_t n);
+void random_bytes(void *p, size_t n);
+void initialize_srand(void);
+
+static inline uint64_t random_u64(void) {
+        uint64_t u;
+        random_bytes(&u, sizeof(u));
+        return u;
+}
+
+static inline uint32_t random_u32(void) {
+        uint32_t u;
+        random_bytes(&u, sizeof(u));
+        return u;
+}
+
+/* For basic lookup tables with strictly enumerated entries */
+#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope)                   \
+        scope const char *name##_to_string(type i) {                    \
+                if (i < 0 || i >= (type) ELEMENTSOF(name##_table))      \
+                        return NULL;                                    \
+                return name##_table[i];                                 \
+        }                                                               \
+        scope type name##_from_string(const char *s) {                  \
+                type i;                                                 \
+                if (!s)                                                 \
+                        return (type) -1;                               \
+                for (i = 0; i < (type)ELEMENTSOF(name##_table); i++)    \
+                        if (name##_table[i] &&                          \
+                            streq(name##_table[i], s))                  \
+                                return i;                               \
+                return (type) -1;                                       \
+        }                                                               \
+        struct __useless_struct_to_allow_trailing_semicolon__
+
+#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static)
+
+/* For string conversions where numbers are also acceptable */
+#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max)         \
+        int name##_to_string_alloc(type i, char **str) {                \
+                char *s;                                                \
+                int r;                                                  \
+                if (i < 0 || i > max)                                   \
+                        return -ERANGE;                                 \
+                if (i < (type) ELEMENTSOF(name##_table)) {              \
+                        s = strdup(name##_table[i]);                    \
+                        if (!s)                                         \
+                                return log_oom();                       \
+                } else {                                                \
+                        r = asprintf(&s, "%u", i);                      \
+                        if (r < 0)                                      \
+                                return log_oom();                       \
+                }                                                       \
+                *str = s;                                               \
+                return 0;                                               \
+        }                                                               \
+        type name##_from_string(const char *s) {                        \
+                type i;                                                 \
+                unsigned u = 0;                                         \
+                assert(s);                                              \
+                for (i = 0; i < (type)ELEMENTSOF(name##_table); i++)    \
+                        if (name##_table[i] &&                          \
+                            streq(name##_table[i], s))                  \
+                                return i;                               \
+                if (safe_atou(s, &u) >= 0 && u <= max)                  \
+                        return (type) u;                                \
+                return (type) -1;                                       \
+        }                                                               \
+        struct __useless_struct_to_allow_trailing_semicolon__
+
+int fd_nonblock(int fd, bool nonblock);
+int fd_cloexec(int fd, bool cloexec);
+
+int close_all_fds(const int except[], unsigned n_except);
+
+bool fstype_is_network(const char *fstype);
+
+int chvt(int vt);
+
+int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
+int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
+int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
+
+int reset_terminal_fd(int fd, bool switch_to_text);
+int reset_terminal(const char *name);
+
+int open_terminal(const char *name, int mode);
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
+int release_terminal(void);
+
+int flush_fd(int fd);
+
+int ignore_signals(int sig, ...);
+int default_signals(int sig, ...);
+int sigaction_many(const struct sigaction *sa, ...);
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
+ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
+
+bool is_device_path(const char *path);
+
+int dir_is_empty(const char *path);
+char* dirname_malloc(const char *path);
+
+void rename_process(const char name[8]);
+
+void sigset_add_many(sigset_t *ss, ...);
+int sigprocmask_many(int how, ...);
+
+bool hostname_is_set(void);
+
+char* lookup_uid(uid_t uid);
+char* gethostname_malloc(void);
+char* getlogname_malloc(void);
+char* getusername_malloc(void);
+
+int getttyname_malloc(int fd, char **r);
+int getttyname_harder(int fd, char **r);
+
+int get_ctty_devnr(pid_t pid, dev_t *d);
+int get_ctty(pid_t, dev_t *_devnr, char **r);
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
+
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
+int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
+int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
+
+int pipe_eof(int fd);
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus);
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
+
+int fd_columns(int fd);
+unsigned columns(void);
+int fd_lines(int fd);
+unsigned lines(void);
+void columns_lines_cache_reset(int _unused_ signum);
+
+bool on_tty(void);
+
+static inline const char *ansi_highlight(void) {
+        return on_tty() ? ANSI_HIGHLIGHT_ON : "";
+}
+
+static inline const char *ansi_highlight_red(void) {
+        return on_tty() ? ANSI_HIGHLIGHT_RED_ON : "";
+}
+
+static inline const char *ansi_highlight_green(void) {
+        return on_tty() ? ANSI_HIGHLIGHT_GREEN_ON : "";
+}
+
+static inline const char *ansi_highlight_yellow(void) {
+        return on_tty() ? ANSI_HIGHLIGHT_YELLOW_ON : "";
+}
+
+static inline const char *ansi_highlight_blue(void) {
+        return on_tty() ? ANSI_HIGHLIGHT_BLUE_ON : "";
+}
+
+static inline const char *ansi_highlight_off(void) {
+        return on_tty() ? ANSI_HIGHLIGHT_OFF : "";
+}
+
+int files_same(const char *filea, const char *fileb);
+
+int running_in_chroot(void);
+
+char *ellipsize(const char *s, size_t length, unsigned percent);
+                                   /* bytes                 columns */
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent);
+
+int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
+int touch(const char *path);
+
+char *unquote(const char *s, const char *quotes);
+char *normalize_env_assignment(const char *s);
+
+int wait_for_terminate(pid_t pid, siginfo_t *status);
+int wait_for_terminate_and_warn(const char *name, pid_t pid);
+
+noreturn void freeze(void);
+
+bool null_or_empty(struct stat *st) _pure_;
+int null_or_empty_path(const char *fn);
+int null_or_empty_fd(int fd);
+
+DIR *xopendirat(int dirfd, const char *name, int flags);
+
+char *fstab_node_to_udev_node(const char *p);
+
+char *resolve_dev_console(char **active);
+bool tty_is_vc(const char *tty);
+bool tty_is_vc_resolve(const char *tty);
+bool tty_is_console(const char *tty) _pure_;
+int vtnr_from_tty(const char *tty);
+const char *default_term_for_tty(const char *tty);
+
+void execute_directory(const char *directory, DIR *_d, usec_t timeout, char *argv[]);
+
+int kill_and_sigcont(pid_t pid, int sig);
+
+bool nulstr_contains(const char*nulstr, const char *needle);
+
+bool plymouth_running(void);
+
+bool hostname_is_valid(const char *s) _pure_;
+char* hostname_cleanup(char *s, bool lowercase);
+
+bool machine_name_is_valid(const char *s) _pure_;
+
+char* strshorten(char *s, size_t l);
+
+int terminal_vhangup_fd(int fd);
+int terminal_vhangup(const char *name);
+
+int vt_disallocate(const char *name);
+
+int symlink_atomic(const char *from, const char *to);
+int mknod_atomic(const char *path, mode_t mode, dev_t dev);
+int mkfifo_atomic(const char *path, mode_t mode);
+
+int fchmod_umask(int fd, mode_t mode);
+
+bool display_is_local(const char *display) _pure_;
+int socket_from_display(const char *display, char **path);
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
+int get_group_creds(const char **groupname, gid_t *gid);
+
+int in_gid(gid_t gid);
+int in_group(const char *name);
+
+char* uid_to_name(uid_t uid);
+char* gid_to_name(gid_t gid);
+
+int glob_exists(const char *path);
+int glob_extend(char ***strv, const char *path);
+
+int dirent_ensure_type(DIR *d, struct dirent *de);
+
+int get_files_in_directory(const char *path, char ***list);
+
+char *strjoin(const char *x, ...) _sentinel_;
+
+bool is_main_thread(void);
+
+static inline bool _pure_ in_charset(const char *s, const char* charset) {
+        assert(s);
+        assert(charset);
+        return s[strspn(s, charset)] == '\0';
+}
+
+int block_get_whole_disk(dev_t d, dev_t *ret);
+
+#define NULSTR_FOREACH(i, l)                                    \
+        for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
+
+#define NULSTR_FOREACH_PAIR(i, j, l)                             \
+        for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
+
+int ioprio_class_to_string_alloc(int i, char **s);
+int ioprio_class_from_string(const char *s);
+
+const char *sigchld_code_to_string(int i) _const_;
+int sigchld_code_from_string(const char *s) _pure_;
+
+int log_facility_unshifted_to_string_alloc(int i, char **s);
+int log_facility_unshifted_from_string(const char *s);
+
+int log_level_to_string_alloc(int i, char **s);
+int log_level_from_string(const char *s);
+
+int sched_policy_to_string_alloc(int i, char **s);
+int sched_policy_from_string(const char *s);
+
+const char *rlimit_to_string(int i) _const_;
+int rlimit_from_string(const char *s) _pure_;
+
+int ip_tos_to_string_alloc(int i, char **s);
+int ip_tos_from_string(const char *s);
+
+const char *signal_to_string(int i) _const_;
+int signal_from_string(const char *s) _pure_;
+
+int signal_from_string_try_harder(const char *s);
+
+extern int saved_argc;
+extern char **saved_argv;
+
+bool kexec_loaded(void);
+
+int prot_from_flags(int flags) _const_;
+
+char *format_bytes(char *buf, size_t l, off_t t);
+
+int fd_wait_for_event(int fd, int event, usec_t timeout);
+
+void* memdup(const void *p, size_t l) _alloc_(2);
+
+int is_kernel_thread(pid_t pid);
+
+int fd_inc_sndbuf(int fd, size_t n);
+int fd_inc_rcvbuf(int fd, size_t n);
+
+int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...);
+
+int setrlimit_closest(int resource, const struct rlimit *rlim);
+
+int getenv_for_pid(pid_t pid, const char *field, char **_value);
+
+bool is_valid_documentation_url(const char *url) _pure_;
+
+bool in_initrd(void);
+
+void warn_melody(void);
+
+int get_home_dir(char **ret);
+int get_shell(char **_ret);
+
+static inline void freep(void *p) {
+        free(*(void**) p);
+}
+
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                 \
+        static inline void func##p(type *p) {                   \
+                if (*p)                                         \
+                        func(*p);                               \
+        }                                                       \
+        struct __useless_struct_to_allow_trailing_semicolon__
+
+static inline void closep(int *fd) {
+        safe_close(*fd);
+}
+
+static inline void umaskp(mode_t *u) {
+        umask(*u);
+}
+
+static inline void close_pairp(int (*p)[2]) {
+        safe_close_pair(*p);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, fclose);
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
+
+#define _cleanup_free_ _cleanup_(freep)
+#define _cleanup_close_ _cleanup_(closep)
+#define _cleanup_umask_ _cleanup_(umaskp)
+#define _cleanup_globfree_ _cleanup_(globfree)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_pclose_ _cleanup_(pclosep)
+#define _cleanup_closedir_ _cleanup_(closedirp)
+#define _cleanup_endmntent_ _cleanup_(endmntentp)
+#define _cleanup_close_pair_ _cleanup_(close_pairp)
+
+_malloc_  _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
+        if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+                return NULL;
+
+        return malloc(a * b);
+}
+
+_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) {
+        if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+                return NULL;
+
+        return realloc(p, a * b);
+}
+
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
+        if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+                return NULL;
+
+        return memdup(p, a * b);
+}
+
+bool filename_is_safe(const char *p) _pure_;
+bool path_is_safe(const char *p) _pure_;
+bool string_is_safe(const char *p) _pure_;
+bool string_has_cc(const char *p, const char *ok) _pure_;
+
+/**
+ * Check if a string contains any glob patterns.
+ */
+_pure_ static inline bool string_is_glob(const char *p) {
+        return !!strpbrk(p, GLOB_CHARS);
+}
+
+void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
+                 int (*compar) (const void *, const void *, void *),
+                 void *arg);
+
+bool is_locale_utf8(void);
+
+typedef enum DrawSpecialChar {
+        DRAW_TREE_VERTICAL,
+        DRAW_TREE_BRANCH,
+        DRAW_TREE_RIGHT,
+        DRAW_TREE_SPACE,
+        DRAW_TRIANGULAR_BULLET,
+        DRAW_BLACK_CIRCLE,
+        DRAW_ARROW,
+        DRAW_DASH,
+        _DRAW_SPECIAL_CHAR_MAX
+} DrawSpecialChar;
+
+const char *draw_special_char(DrawSpecialChar ch);
+
+char *strreplace(const char *text, const char *old_string, const char *new_string);
+
+char *strip_tab_ansi(char **p, size_t *l);
+
+int on_ac_power(void);
+
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
+
+#define FOREACH_LINE(line, f, on_error)                         \
+        for (;;)                                                \
+                if (!fgets(line, sizeof(line), f)) {            \
+                        if (ferror(f)) {                        \
+                                on_error;                       \
+                        }                                       \
+                        break;                                  \
+                } else
+
+#define FOREACH_DIRENT(de, d, on_error)                                 \
+        for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d))   \
+                if (!de) {                                              \
+                        if (errno > 0) {                                \
+                                on_error;                               \
+                        }                                               \
+                        break;                                          \
+                } else if (ignore_file((de)->d_name))                   \
+                        continue;                                       \
+                else
+
+static inline void *mempset(void *s, int c, size_t n) {
+        memset(s, c, n);
+        return (uint8_t*)s + n;
+}
+
+char *hexmem(const void *p, size_t l);
+void *unhexmem(const char *p, size_t l);
+
+char *strextend(char **x, ...) _sentinel_;
+char *strrep(const char *s, unsigned n);
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
+void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
+#define GREEDY_REALLOC(array, allocated, need)                          \
+        greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
+
+#define GREEDY_REALLOC0(array, allocated, need)                         \
+        greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
+
+static inline void _reset_errno_(int *saved_errno) {
+        errno = *saved_errno;
+}
+
+#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno
+
+static inline int negative_errno(void) {
+        /* This helper should be used to shut up gcc if you know 'errno' is
+         * negative. Instead of "return -errno;", use "return negative_errno();"
+         * It will suppress bogus gcc warnings in case it assumes 'errno' might
+         * be 0 and thus the caller's error-handling might not be triggered. */
+        assert_return(errno > 0, -EINVAL);
+        return -errno;
+}
+
+struct _umask_struct_ {
+        mode_t mask;
+        bool quit;
+};
+
+static inline void _reset_umask_(struct _umask_struct_ *s) {
+        umask(s->mask);
+};
+
+#define RUN_WITH_UMASK(mask)                                            \
+        for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \
+             !_saved_umask_.quit ;                                      \
+             _saved_umask_.quit = true)
+
+static inline unsigned u64log2(uint64_t n) {
+#if __SIZEOF_LONG_LONG__ == 8
+        return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0;
+#else
+#error "Wut?"
+#endif
+}
+
+static inline unsigned u32ctz(uint32_t n) {
+#if __SIZEOF_INT__ == 4
+        return __builtin_ctz(n);
+#else
+#error "Wut?"
+#endif
+}
+
+static inline int log2i(int x) {
+        assert(x > 0);
+
+        return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1;
+}
+
+static inline unsigned log2u(unsigned x) {
+        assert(x > 0);
+
+        return sizeof(unsigned) * 8 - __builtin_clz(x) - 1;
+}
+
+static inline unsigned log2u_round_up(unsigned x) {
+        assert(x > 0);
+
+        if (x == 1)
+                return 0;
+
+        return log2u(x - 1) + 1;
+}
+
+static inline bool logind_running(void) {
+        return access("/run/systemd/seats/", F_OK) >= 0;
+}
+
+#define DECIMAL_STR_WIDTH(x)                            \
+        ({                                              \
+                typeof(x) _x_ = (x);                    \
+                unsigned ans = 1;                       \
+                while (_x_ /= 10)                       \
+                        ans++;                          \
+                ans;                                    \
+        })
+
+int unlink_noerrno(const char *path);
+
+#define alloca0(n)                                      \
+        ({                                              \
+                char *_new_;                            \
+                size_t _len_ = n;                       \
+                _new_ = alloca(_len_);                  \
+                (void *) memset(_new_, 0, _len_);       \
+        })
+
+#define alloca_align(size, align)                                       \
+        ({                                                              \
+                void *_ptr_;                                            \
+                size_t _mask_ = (align) - 1;                            \
+                _ptr_ = alloca((size) + _mask_);                        \
+                (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_);         \
+        })
+
+#define alloca0_align(size, align)                                      \
+        ({                                                              \
+                void *_new_;                                            \
+                size_t _size_ = (size);                                 \
+                _new_ = alloca_align(_size_, (align));                  \
+                (void*)memset(_new_, 0, _size_);                        \
+        })
+
+#define strappenda(a, ...)                                       \
+        ({                                                       \
+                int _len = strlen(a);                            \
+                unsigned _i;                                     \
+                char *_d_, *_p_;                                 \
+                const char *_appendees_[] = { __VA_ARGS__ };     \
+                for (_i = 0; _i < ELEMENTSOF(_appendees_); _i++) \
+                        _len += strlen(_appendees_[_i]);         \
+                _d_ = alloca(_len + 1);                          \
+                _p_ = stpcpy(_d_, a);                            \
+                for (_i = 0; _i < ELEMENTSOF(_appendees_); _i++) \
+                        _p_ = stpcpy(_p_, _appendees_[_i]);      \
+                _d_;                                             \
+        })
+
+#define procfs_file_alloca(pid, field)                                  \
+        ({                                                              \
+                pid_t _pid_ = (pid);                                    \
+                const char *_r_;                                        \
+                if (_pid_ == 0) {                                       \
+                        _r_ = ("/proc/self/" field);                    \
+                } else {                                                \
+                        _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
+                        sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_);                       \
+                }                                                       \
+                _r_;                                                    \
+        })
+
+struct _locale_struct_ {
+        locale_t saved_locale;
+        locale_t new_locale;
+        bool quit;
+};
+
+static inline void _reset_locale_(struct _locale_struct_ *s) {
+        PROTECT_ERRNO;
+        if (s->saved_locale != (locale_t) 0)
+                uselocale(s->saved_locale);
+        if (s->new_locale != (locale_t) 0)
+                freelocale(s->new_locale);
+}
+
+#define RUN_WITH_LOCALE(mask, loc) \
+        for (_cleanup_(_reset_locale_) struct _locale_struct_ _saved_locale_ = { (locale_t) 0, (locale_t) 0, false }; \
+             ({                                                         \
+                     if (!_saved_locale_.quit) {                        \
+                             PROTECT_ERRNO;                             \
+                             _saved_locale_.new_locale = newlocale((mask), (loc), (locale_t) 0); \
+                             if (_saved_locale_.new_locale != (locale_t) 0)     \
+                                     _saved_locale_.saved_locale = uselocale(_saved_locale_.new_locale); \
+                     }                                                  \
+                     !_saved_locale_.quit; }) ;                         \
+             _saved_locale_.quit = true)
+
+bool id128_is_valid(const char *s) _pure_;
+
+int split_pair(const char *s, const char *sep, char **l, char **r);
+
+int shall_restore_state(void);
+
+/**
+ * Normal qsort requires base to be nonnull. Here were require
+ * that only if nmemb > 0.
+ */
+static inline void qsort_safe(void *base, size_t nmemb, size_t size,
+                              int (*compar)(const void *, const void *)) {
+        if (nmemb) {
+                assert(base);
+                qsort(base, nmemb, size, compar);
+        }
+}
+
+int proc_cmdline(char **ret);
+int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value));
+
+int container_get_leader(const char *machine, pid_t *pid);
+
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd);
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd);
+
+bool pid_is_alive(pid_t pid);
+bool pid_is_unwaited(pid_t pid);
+
+#if 0 /* NM_IGNORED */
+int getpeercred(int fd, struct ucred *ucred);
+int getpeersec(int fd, char **ret);
+#endif
+
+int writev_safe(int fd, const struct iovec *w, int j);
+
+int mkostemp_safe(char *pattern, int flags);
+int open_tmpfile(const char *path, int flags);
+
+int fd_warn_permissions(const char *path, int fd);
+
+unsigned long personality_from_string(const char *p);
+const char *personality_to_string(unsigned long);
+
+uint64_t physical_memory(void);
+
+char* mount_test_option(const char *haystack, const char *needle);
+
+void hexdump(FILE *f, const void *p, size_t s);
+
+#if 0 /* NM_IGNORED */
+union file_handle_union {
+        struct file_handle handle;
+        char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ];
+};
+#endif
+
+int update_reboot_param_file(const char *param);
+
+int umount_recursive(const char *target, int flags);
+
+int bind_remount_recursive(const char *prefix, bool ro);
+
+int fflush_and_check(FILE *f);
+
+char *tempfn_xxxxxx(const char *p);
+char *tempfn_random(const char *p);
+
+bool is_localhost(const char *hostname);
+
+int take_password_lock(const char *root);
+
+int is_symlink(const char *path);
+int is_dir(const char *path, bool follow);
+
+int unquote_first_word(const char **p, char **ret, bool relax);
+int unquote_many_words(const char **p, ...) _sentinel_;
+
+int free_and_strdup(char **p, const char *s);
+
+int sethostname_idempotent(const char *s);
diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/_sd-common.h b/src/dhcp-manager/systemd-dhcp/src/systemd/_sd-common.h
new file mode 100644
index 00000000..896a027e
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/systemd/_sd-common.h
@@ -0,0 +1,78 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdcommonhfoo
+#define foosdcommonhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This is a private header; never even think of including this directly! */
+
+#if __INCLUDE_LEVEL__ <= 1
+#error "Do not include _sd-common.h directly; it is a private header."
+#endif
+
+#ifndef _sd_printf_
+#  if __GNUC__ >= 4
+#    define _sd_printf_(a,b) __attribute__ ((format (printf, a, b)))
+#  else
+#    define _sd_printf_(a,b)
+#  endif
+#endif
+
+#ifndef _sd_sentinel_
+#  define _sd_sentinel_ __attribute__((sentinel))
+#endif
+
+#ifndef _sd_packed_
+#  define _sd_packed_ __attribute__((packed))
+#endif
+
+#ifndef _sd_pure_
+#  define _sd_pure_ __attribute__((pure))
+#endif
+
+#ifndef _SD_STRINGIFY
+#  define _SD_XSTRINGIFY(x) #x
+#  define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x)
+#endif
+
+#ifndef _SD_BEGIN_DECLARATIONS
+#  ifdef __cplusplus
+#    define _SD_BEGIN_DECLARATIONS                              \
+        extern "C" {                                            \
+        struct __useless_struct_to_allow_trailing_semicolon__
+#  else
+#    define _SD_BEGIN_DECLARATIONS                              \
+        struct __useless_struct_to_allow_trailing_semicolon__
+#  endif
+#endif
+
+#ifndef _SD_END_DECLARATIONS
+#  ifdef __cplusplus
+#    define _SD_END_DECLARATIONS                                \
+        }                                                       \
+        struct __useless_struct_to_allow_trailing_semicolon__
+#  else
+#    define _SD_END_DECLARATIONS                                \
+        struct __useless_struct_to_allow_trailing_semicolon__
+#  endif
+#endif
+
+#endif
diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-client.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-client.h
new file mode 100644
index 00000000..951662e5
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-client.h
@@ -0,0 +1,75 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddhcpclienthfoo
+#define foosddhcpclienthfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+#include "sd-dhcp-lease.h"
+
+enum {
+        DHCP_EVENT_STOP                         = 0,
+        DHCP_EVENT_IP_ACQUIRE                   = 1,
+        DHCP_EVENT_IP_CHANGE                    = 2,
+        DHCP_EVENT_EXPIRED                      = 3,
+        DHCP_EVENT_RENEW                        = 4,
+};
+
+typedef struct sd_dhcp_client sd_dhcp_client;
+
+typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event,
+                                    void *userdata);
+int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
+                                void *userdata);
+
+
+int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
+int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
+                                       const struct in_addr *last_address);
+int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast);
+int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index);
+int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
+                           size_t addr_len, uint16_t arp_type);
+int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
+                                 const uint8_t *data, size_t data_len);
+int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
+                                 const uint8_t **data, size_t *data_len);
+int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu);
+int sd_dhcp_client_set_hostname(sd_dhcp_client *client, const char *hostname);
+int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client, const char *vci);
+int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret);
+
+int sd_dhcp_client_stop(sd_dhcp_client *client);
+int sd_dhcp_client_start(sd_dhcp_client *client);
+
+sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client);
+sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
+
+int sd_dhcp_client_new(sd_dhcp_client **ret);
+
+int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority);
+int sd_dhcp_client_detach_event(sd_dhcp_client *client);
+sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client);
+
+#endif
diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h
new file mode 100644
index 00000000..4296b91d
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h
@@ -0,0 +1,54 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddhcpleasehfoo
+#define foosddhcpleasehfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+  Copyright (C) 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+typedef struct sd_dhcp_lease sd_dhcp_lease;
+struct sd_dhcp_route;
+
+sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
+sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
+
+int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
+int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
+int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
+int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
+int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routesgn);
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
+                                size_t *client_id_len);
+
+int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
+int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
+
+#endif
diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-client.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-client.h
new file mode 100644
index 00000000..38c1c3fb
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-client.h
@@ -0,0 +1,66 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddhcp6clienthfoo
+#define foosddhcp6clienthfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+#include "sd-dhcp6-lease.h"
+
+enum {
+        DHCP6_EVENT_STOP                        = 0,
+        DHCP6_EVENT_RESEND_EXPIRE               = 10,
+        DHCP6_EVENT_RETRANS_MAX                 = 11,
+        DHCP6_EVENT_IP_ACQUIRE                  = 12,
+};
+
+typedef struct sd_dhcp6_client sd_dhcp6_client;
+
+typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event,
+                                     void *userdata);
+int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
+                                 sd_dhcp6_client_cb_t cb, void *userdata);
+
+int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
+int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
+                            size_t addr_len, uint16_t arp_type);
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
+                             size_t duid_len);
+int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname);
+int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
+                                       uint16_t option);
+
+int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret);
+
+int sd_dhcp6_client_stop(sd_dhcp6_client *client);
+int sd_dhcp6_client_start(sd_dhcp6_client *client);
+int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
+                                 int priority);
+int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);
+sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client);
+sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client);
+sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client);
+int sd_dhcp6_client_new(sd_dhcp6_client **ret);
+
+#endif
diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-lease.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-lease.h
new file mode 100644
index 00000000..1126f1ac
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-lease.h
@@ -0,0 +1,42 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddhcp6leasehfoo
+#define foosddhcp6leasehfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+
+typedef struct sd_dhcp6_lease sd_dhcp6_lease;
+
+int sd_dhcp6_lease_get_first_address(sd_dhcp6_lease *lease,
+                                     struct in6_addr *addr,
+                                     uint32_t *lifetime_preferred,
+                                     uint32_t *lifetime_valid);
+int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease,
+                                    struct in6_addr *addr,
+                                    uint32_t *lifetime_preferred,
+                                    uint32_t *lifetime_valid);
+
+sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
+sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
+
+#endif
diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-event.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-event.h
new file mode 100644
index 00000000..25a10f99
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-event.h
@@ -0,0 +1,135 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdeventhfoo
+#define foosdeventhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <sys/signalfd.h>
+#include <sys/epoll.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "_sd-common.h"
+
+/*
+  Why is this better than pure epoll?
+
+  - Supports event source prioritization
+  - Scales better with a large number of time events because it does not require one timerfd each
+  - Automatically tries to coalesce timer events system-wide
+  - Handles signals and child PIDs
+*/
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_event sd_event;
+typedef struct sd_event_source sd_event_source;
+
+enum {
+        SD_EVENT_OFF = 0,
+        SD_EVENT_ON = 1,
+        SD_EVENT_ONESHOT = -1
+};
+
+enum {
+        SD_EVENT_PASSIVE,
+        SD_EVENT_PREPARED,
+        SD_EVENT_PENDING,
+        SD_EVENT_RUNNING,
+        SD_EVENT_EXITING,
+        SD_EVENT_FINISHED
+};
+
+enum {
+        /* And everything in-between and outside is good too */
+        SD_EVENT_PRIORITY_IMPORTANT = -100,
+        SD_EVENT_PRIORITY_NORMAL = 0,
+        SD_EVENT_PRIORITY_IDLE = 100
+};
+
+typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata);
+typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata);
+typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata);
+typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata);
+typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata);
+
+int sd_event_default(sd_event **e);
+
+int sd_event_new(sd_event **e);
+sd_event* sd_event_ref(sd_event *e);
+sd_event* sd_event_unref(sd_event *e);
+
+int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata);
+int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata);
+int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata);
+int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata);
+int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
+int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
+int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
+
+int sd_event_prepare(sd_event *e);
+int sd_event_wait(sd_event *e, uint64_t timeout);
+int sd_event_dispatch(sd_event *e);
+int sd_event_run(sd_event *e, uint64_t timeout);
+int sd_event_loop(sd_event *e);
+int sd_event_exit(sd_event *e, int code);
+
+int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec);
+
+int sd_event_get_fd(sd_event *e);
+int sd_event_get_state(sd_event *e);
+int sd_event_get_tid(sd_event *e, pid_t *tid);
+int sd_event_get_exit_code(sd_event *e, int *code);
+int sd_event_set_watchdog(sd_event *e, int b);
+int sd_event_get_watchdog(sd_event *e);
+
+sd_event_source* sd_event_source_ref(sd_event_source *s);
+sd_event_source* sd_event_source_unref(sd_event_source *s);
+
+sd_event *sd_event_source_get_event(sd_event_source *s);
+void* sd_event_source_get_userdata(sd_event_source *s);
+void* sd_event_source_set_userdata(sd_event_source *s, void *userdata);
+
+int sd_event_source_set_description(sd_event_source *s, const char *description);
+int sd_event_source_get_description(sd_event_source *s, const char **description);
+int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback);
+int sd_event_source_get_pending(sd_event_source *s);
+int sd_event_source_get_priority(sd_event_source *s, int64_t *priority);
+int sd_event_source_set_priority(sd_event_source *s, int64_t priority);
+int sd_event_source_get_enabled(sd_event_source *s, int *enabled);
+int sd_event_source_set_enabled(sd_event_source *s, int enabled);
+int sd_event_source_get_io_fd(sd_event_source *s);
+int sd_event_source_set_io_fd(sd_event_source *s, int fd);
+int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events);
+int sd_event_source_set_io_events(sd_event_source *s, uint32_t events);
+int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents);
+int sd_event_source_get_time(sd_event_source *s, uint64_t *usec);
+int sd_event_source_set_time(sd_event_source *s, uint64_t usec);
+int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec);
+int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec);
+int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock);
+int sd_event_source_get_signal(sd_event_source *s);
+int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-id128.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-id128.h
new file mode 100644
index 00000000..4fdbf3ab
--- /dev/null
+++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-id128.h
@@ -0,0 +1,113 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdid128hfoo
+#define foosdid128hfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <string.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+/* 128-bit ID APIs. See sd-id128(3) for more information. */
+
+typedef union sd_id128 sd_id128_t;
+
+union sd_id128 {
+        uint8_t bytes[16];
+        uint64_t qwords[2];
+};
+
+#define SD_ID128_STRING_MAX 33
+
+char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]);
+
+int sd_id128_from_string(const char *s, sd_id128_t *ret);
+
+int sd_id128_randomize(sd_id128_t *ret);
+
+int sd_id128_get_machine(sd_id128_t *ret);
+
+int sd_id128_get_boot(sd_id128_t *ret);
+
+#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
+        ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
+                                   0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }})
+
+#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
+        { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
+                     0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}
+
+/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16
+ * times. It is hence not a good idea to call this macro with an
+ * expensive function as paramater or an expression with side
+ * effects */
+
+#define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+#define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15]
+
+#define SD_ID128_CONST_STR(x)                                           \
+        ((const char[SD_ID128_STRING_MAX]) {                            \
+                ((x).bytes[0] >> 4) >= 10 ? 'a' + ((x).bytes[0] >> 4) - 10 : '0' + ((x).bytes[0] >> 4), \
+                ((x).bytes[0] & 15) >= 10 ? 'a' + ((x).bytes[0] & 15) - 10 : '0' + ((x).bytes[0] & 15), \
+                ((x).bytes[1] >> 4) >= 10 ? 'a' + ((x).bytes[1] >> 4) - 10 : '0' + ((x).bytes[1] >> 4), \
+                ((x).bytes[1] & 15) >= 10 ? 'a' + ((x).bytes[1] & 15) - 10 : '0' + ((x).bytes[1] & 15), \
+                ((x).bytes[2] >> 4) >= 10 ? 'a' + ((x).bytes[2] >> 4) - 10 : '0' + ((x).bytes[2] >> 4), \
+                ((x).bytes[2] & 15) >= 10 ? 'a' + ((x).bytes[2] & 15) - 10 : '0' + ((x).bytes[2] & 15), \
+                ((x).bytes[3] >> 4) >= 10 ? 'a' + ((x).bytes[3] >> 4) - 10 : '0' + ((x).bytes[3] >> 4), \
+                ((x).bytes[3] & 15) >= 10 ? 'a' + ((x).bytes[3] & 15) - 10 : '0' + ((x).bytes[3] & 15), \
+                ((x).bytes[4] >> 4) >= 10 ? 'a' + ((x).bytes[4] >> 4) - 10 : '0' + ((x).bytes[4] >> 4), \
+                ((x).bytes[4] & 15) >= 10 ? 'a' + ((x).bytes[4] & 15) - 10 : '0' + ((x).bytes[4] & 15), \
+                ((x).bytes[5] >> 4) >= 10 ? 'a' + ((x).bytes[5] >> 4) - 10 : '0' + ((x).bytes[5] >> 4), \
+                ((x).bytes[5] & 15) >= 10 ? 'a' + ((x).bytes[5] & 15) - 10 : '0' + ((x).bytes[5] & 15), \
+                ((x).bytes[6] >> 4) >= 10 ? 'a' + ((x).bytes[6] >> 4) - 10 : '0' + ((x).bytes[6] >> 4), \
+                ((x).bytes[6] & 15) >= 10 ? 'a' + ((x).bytes[6] & 15) - 10 : '0' + ((x).bytes[6] & 15), \
+                ((x).bytes[7] >> 4) >= 10 ? 'a' + ((x).bytes[7] >> 4) - 10 : '0' + ((x).bytes[7] >> 4), \
+                ((x).bytes[7] & 15) >= 10 ? 'a' + ((x).bytes[7] & 15) - 10 : '0' + ((x).bytes[7] & 15), \
+                ((x).bytes[8] >> 4) >= 10 ? 'a' + ((x).bytes[8] >> 4) - 10 : '0' + ((x).bytes[8] >> 4), \
+                ((x).bytes[8] & 15) >= 10 ? 'a' + ((x).bytes[8] & 15) - 10 : '0' + ((x).bytes[8] & 15), \
+                ((x).bytes[9] >> 4) >= 10 ? 'a' + ((x).bytes[9] >> 4) - 10 : '0' + ((x).bytes[9] >> 4), \
+                ((x).bytes[9] & 15) >= 10 ? 'a' + ((x).bytes[9] & 15) - 10 : '0' + ((x).bytes[9] & 15), \
+                ((x).bytes[10] >> 4) >= 10 ? 'a' + ((x).bytes[10] >> 4) - 10 : '0' + ((x).bytes[10] >> 4), \
+                ((x).bytes[10] & 15) >= 10 ? 'a' + ((x).bytes[10] & 15) - 10 : '0' + ((x).bytes[10] & 15), \
+                ((x).bytes[11] >> 4) >= 10 ? 'a' + ((x).bytes[11] >> 4) - 10 : '0' + ((x).bytes[11] >> 4), \
+                ((x).bytes[11] & 15) >= 10 ? 'a' + ((x).bytes[11] & 15) - 10 : '0' + ((x).bytes[11] & 15), \
+                ((x).bytes[12] >> 4) >= 10 ? 'a' + ((x).bytes[12] >> 4) - 10 : '0' + ((x).bytes[12] >> 4), \
+                ((x).bytes[12] & 15) >= 10 ? 'a' + ((x).bytes[12] & 15) - 10 : '0' + ((x).bytes[12] & 15), \
+                ((x).bytes[13] >> 4) >= 10 ? 'a' + ((x).bytes[13] >> 4) - 10 : '0' + ((x).bytes[13] >> 4), \
+                ((x).bytes[13] & 15) >= 10 ? 'a' + ((x).bytes[13] & 15) - 10 : '0' + ((x).bytes[13] & 15), \
+                ((x).bytes[14] >> 4) >= 10 ? 'a' + ((x).bytes[14] >> 4) - 10 : '0' + ((x).bytes[14] >> 4), \
+                ((x).bytes[14] & 15) >= 10 ? 'a' + ((x).bytes[14] & 15) - 10 : '0' + ((x).bytes[14] & 15), \
+                ((x).bytes[15] >> 4) >= 10 ? 'a' + ((x).bytes[15] >> 4) - 10 : '0' + ((x).bytes[15] >> 4), \
+                ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \
+                0 })
+
+_sd_pure_ static inline int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
+        return memcmp(&a, &b, 16) == 0;
+}
+
+#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am
index 6ef828d5..9dc5dcb1 100644
--- a/src/dhcp-manager/tests/Makefile.am
+++ b/src/dhcp-manager/tests/Makefile.am
@@ -1,27 +1,43 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/include \
-	-I$(top_builddir)/include \
-	-I${top_srcdir}/libnm-util \
-	-I${top_builddir}/libnm-util \
+	-I${top_srcdir}/libnm-core \
+	-I${top_builddir}/libnm-core \
 	-I$(top_srcdir)/src/dhcp-manager \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/platform \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(GLIB_CFLAGS) \
 	-DTESTDIR="\"$(abs_srcdir)\""
 
-noinst_PROGRAMS = test-dhcp-dhclient
+noinst_PROGRAMS = \
+	test-dhcp-dhclient \
+	test-dhcp-utils
 
-####### policy /etc/hosts test #######
+####### dhclient leases test #######
 
 test_dhcp_dhclient_SOURCES = \
+	$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.h \
+	$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c \
+	$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.h \
+	$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c \
 	test-dhcp-dhclient.c
 
 test_dhcp_dhclient_LDADD = \
 	$(top_builddir)/src/libNetworkManager.la
 
-TESTS = test-dhcp-dhclient
+####### DHCP utils test #######
+
+test_dhcp_utils_SOURCES = \
+	test-dhcp-utils.c
+
+test_dhcp_utils_LDADD = \
+	$(top_builddir)/src/libNetworkManager.la
+
+#################################
+
+TESTS = test-dhcp-dhclient test-dhcp-utils
 
 EXTRA_DIST = \
 	test-dhclient-duid.leases \
diff --git a/src/dhcp-manager/tests/Makefile.in b/src/dhcp-manager/tests/Makefile.in
index d445838f..c0324e30 100644
--- a/src/dhcp-manager/tests/Makefile.in
+++ b/src/dhcp-manager/tests/Makefile.in
@@ -78,8 +78,8 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
-noinst_PROGRAMS = test-dhcp-dhclient$(EXEEXT)
-TESTS = test-dhcp-dhclient$(EXEEXT)
+noinst_PROGRAMS = test-dhcp-dhclient$(EXEEXT) test-dhcp-utils$(EXEEXT)
+TESTS = test-dhcp-dhclient$(EXEEXT) test-dhcp-utils$(EXEEXT)
 subdir = src/dhcp-manager/tests
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
 	$(top_srcdir)/build-aux/depcomp
@@ -104,7 +104,8 @@ CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 PROGRAMS = $(noinst_PROGRAMS)
-am_test_dhcp_dhclient_OBJECTS = test-dhcp-dhclient.$(OBJEXT)
+am_test_dhcp_dhclient_OBJECTS = nm-dhcp-dhclient-utils.$(OBJEXT) \
+	nm-dhcp-utils.$(OBJEXT) test-dhcp-dhclient.$(OBJEXT)
 test_dhcp_dhclient_OBJECTS = $(am_test_dhcp_dhclient_OBJECTS)
 test_dhcp_dhclient_DEPENDENCIES =  \
 	$(top_builddir)/src/libNetworkManager.la
@@ -112,6 +113,10 @@ AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
 am__v_lt_1 = 
+am_test_dhcp_utils_OBJECTS = test-dhcp-utils.$(OBJEXT)
+test_dhcp_utils_OBJECTS = $(am_test_dhcp_utils_OBJECTS)
+test_dhcp_utils_DEPENDENCIES =  \
+	$(top_builddir)/src/libNetworkManager.la
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -146,8 +151,9 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(test_dhcp_dhclient_SOURCES)
-DIST_SOURCES = $(test_dhcp_dhclient_SOURCES)
+SOURCES = $(test_dhcp_dhclient_SOURCES) $(test_dhcp_utils_SOURCES)
+DIST_SOURCES = $(test_dhcp_dhclient_SOURCES) \
+	$(test_dhcp_utils_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -204,6 +210,8 @@ AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
@@ -327,6 +335,7 @@ NEWT_CFLAGS = @NEWT_CFLAGS@
 NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -346,6 +355,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -372,7 +382,7 @@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
+UDEV_DIR = @UDEV_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
@@ -453,25 +463,37 @@ with_resolvconf = @with_resolvconf@
 with_valgrind = @with_valgrind@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/include \
-	-I$(top_builddir)/include \
-	-I${top_srcdir}/libnm-util \
-	-I${top_builddir}/libnm-util \
+	-I${top_srcdir}/libnm-core \
+	-I${top_builddir}/libnm-core \
 	-I$(top_srcdir)/src/dhcp-manager \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/platform \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(GLIB_CFLAGS) \
 	-DTESTDIR="\"$(abs_srcdir)\""
 
 
-####### policy /etc/hosts test #######
+####### dhclient leases test #######
 test_dhcp_dhclient_SOURCES = \
+	$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.h \
+	$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c \
+	$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.h \
+	$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c \
 	test-dhcp-dhclient.c
 
 test_dhcp_dhclient_LDADD = \
 	$(top_builddir)/src/libNetworkManager.la
 
+
+####### DHCP utils test #######
+test_dhcp_utils_SOURCES = \
+	test-dhcp-utils.c
+
+test_dhcp_utils_LDADD = \
+	$(top_builddir)/src/libNetworkManager.la
+
 EXTRA_DIST = \
 	test-dhclient-duid.leases \
 	test-dhclient-commented-duid.leases \
@@ -528,13 +550,20 @@ test-dhcp-dhclient$(EXEEXT): $(test_dhcp_dhclient_OBJECTS) $(test_dhcp_dhclient_
 	@rm -f test-dhcp-dhclient$(EXEEXT)
 	$(AM_V_CCLD)$(LINK) $(test_dhcp_dhclient_OBJECTS) $(test_dhcp_dhclient_LDADD) $(LIBS)
 
+test-dhcp-utils$(EXEEXT): $(test_dhcp_utils_OBJECTS) $(test_dhcp_utils_DEPENDENCIES) $(EXTRA_test_dhcp_utils_DEPENDENCIES) 
+	@rm -f test-dhcp-utils$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_dhcp_utils_OBJECTS) $(test_dhcp_utils_LDADD) $(LIBS)
+
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
 
 distclean-compile:
 	-rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dhcp-dhclient-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dhcp-utils.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-dhcp-dhclient.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-dhcp-utils.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -557,6 +586,34 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
+nm-dhcp-dhclient-utils.o: $(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-dhcp-dhclient-utils.o -MD -MP -MF $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo -c -o nm-dhcp-dhclient-utils.o `test -f '$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c' || echo '$(srcdir)/'`$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo $(DEPDIR)/nm-dhcp-dhclient-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c' object='nm-dhcp-dhclient-utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dhcp-dhclient-utils.o `test -f '$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c' || echo '$(srcdir)/'`$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+
+nm-dhcp-dhclient-utils.obj: $(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-dhcp-dhclient-utils.obj -MD -MP -MF $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo -c -o nm-dhcp-dhclient-utils.obj `if test -f '$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c'; then $(CYGPATH_W) '$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo $(DEPDIR)/nm-dhcp-dhclient-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c' object='nm-dhcp-dhclient-utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dhcp-dhclient-utils.obj `if test -f '$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c'; then $(CYGPATH_W) '$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c'; fi`
+
+nm-dhcp-utils.o: $(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-dhcp-utils.o -MD -MP -MF $(DEPDIR)/nm-dhcp-utils.Tpo -c -o nm-dhcp-utils.o `test -f '$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c' || echo '$(srcdir)/'`$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-dhcp-utils.Tpo $(DEPDIR)/nm-dhcp-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c' object='nm-dhcp-utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dhcp-utils.o `test -f '$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c' || echo '$(srcdir)/'`$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c
+
+nm-dhcp-utils.obj: $(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-dhcp-utils.obj -MD -MP -MF $(DEPDIR)/nm-dhcp-utils.Tpo -c -o nm-dhcp-utils.obj `if test -f '$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c'; then $(CYGPATH_W) '$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-dhcp-utils.Tpo $(DEPDIR)/nm-dhcp-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c' object='nm-dhcp-utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dhcp-utils.obj `if test -f '$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c'; then $(CYGPATH_W) '$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c'; fi`
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c
index 1ab182a7..4f7e96af 100644
--- a/src/dhcp-manager/tests/test-dhcp-dhclient.c
+++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c
@@ -18,33 +18,50 @@
  *
  */
 
+#include "config.h"
+
 #include <glib.h>
 #include <string.h>
 #include <unistd.h>
+#include <arpa/inet.h>
 
+#include "gsystem-local-alloc.h"
+#include "NetworkManagerUtils.h"
 #include "nm-dhcp-dhclient-utils.h"
+#include "nm-dhcp-utils.h"
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
+#include "nm-platform.h"
+#include "nm-utils-internal.h"
 
-#define DEBUG 0
+#define DEBUG 1
 
 static void
 test_config (const char *orig,
              const char *expected,
              const char *hostname,
              const char *dhcp_client_id,
+             GBytes *expected_new_client_id,
              const char *iface,
-             GByteArray *anycast_addr)
+             const char *anycast_addr)
 {
-	char *new;
+	gs_free char *new = NULL;
+	gs_unref_bytes GBytes *client_id = NULL;
+	gs_unref_bytes GBytes *new_client_id = NULL;
+
+	if (dhcp_client_id) {
+		client_id = nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id);
+		g_assert (client_id);
+	}
 
 	new = nm_dhcp_dhclient_create_config (iface,
 	                                      FALSE,
-	                                      dhcp_client_id,
+	                                      client_id,
 	                                      anycast_addr,
 	                                      hostname,
 	                                      "/path/to/dhclient.conf",
-	                                      orig);
+	                                      orig,
+	                                      &new_client_id);
 	g_assert (new != NULL);
 
 #if DEBUG
@@ -58,9 +75,13 @@ test_config (const char *orig,
 		           new, expected);
 	}
 #endif
-	g_assert (strlen (new) == strlen (expected));
-	g_assert (strcmp (new, expected) == 0);
-	g_free (new);
+	g_assert_cmpstr (new, ==, expected);
+
+	if (expected_new_client_id) {
+		g_assert (new_client_id);
+		g_assert (g_bytes_equal (new_client_id, expected_new_client_id));
+	 } else
+		g_assert (new_client_id == NULL);
 }
 
 /*******************************************/
@@ -82,11 +103,7 @@ static const char *orig_missing_expected = \
 static void
 test_orig_missing (void)
 {
-	test_config (NULL, orig_missing_expected,
-	             NULL,
-	             NULL,
-	             "eth0",
-	             NULL);
+	test_config (NULL, orig_missing_expected, NULL, NULL, NULL, "eth0", NULL);
 }
 
 /*******************************************/
@@ -117,6 +134,7 @@ test_override_client_id (void)
 	test_config (override_client_id_orig, override_client_id_expected,
 	             NULL,
 	             "11:22:33:44:55:66",
+	             NULL,
 	             "eth0",
 	             NULL);
 }
@@ -145,6 +163,7 @@ test_quote_client_id (void)
 	test_config (NULL, quote_client_id_expected,
 	             NULL,
 	             "1234",
+	             NULL,
 	             "eth0",
 	             NULL);
 }
@@ -173,6 +192,113 @@ test_ascii_client_id (void)
 	test_config (NULL, ascii_client_id_expected,
 	             NULL,
 	             "qb:cd:ef:12:34:56",
+	             NULL,
+	             "eth0",
+	             NULL);
+}
+
+/*******************************************/
+
+static const char *hex_single_client_id_expected = \
+	"# Created by NetworkManager\n"
+	"\n"
+	"send dhcp-client-identifier ab:cd:0e:12:34:56; # added by NetworkManager\n"
+	"\n"
+	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+	"option wpad code 252 = string;\n"
+	"\n"
+	"also request rfc3442-classless-static-routes;\n"
+	"also request ms-classless-static-routes;\n"
+	"also request static-routes;\n"
+	"also request wpad;\n"
+	"also request ntp-servers;\n"
+	"\n";
+
+static void
+test_hex_single_client_id (void)
+{
+	test_config (NULL, hex_single_client_id_expected,
+	             NULL,
+	             "ab:cd:e:12:34:56",
+	             NULL,
+	             "eth0",
+	             NULL);
+}
+
+/*******************************************/
+
+static const char *existing_hex_client_id_orig = \
+	"send dhcp-client-identifier 00:30:04:20:7A:08;\n";
+
+static const char *existing_hex_client_id_expected = \
+	"# Created by NetworkManager\n"
+	"# Merged from /path/to/dhclient.conf\n"
+	"\n"
+	"send dhcp-client-identifier 00:30:04:20:7A:08;\n"
+	"\n"
+	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+	"option wpad code 252 = string;\n"
+	"\n"
+	"also request rfc3442-classless-static-routes;\n"
+	"also request ms-classless-static-routes;\n"
+	"also request static-routes;\n"
+	"also request wpad;\n"
+	"also request ntp-servers;\n"
+	"\n";
+
+static void
+test_existing_hex_client_id (void)
+{
+	gs_unref_bytes GBytes *new_client_id = NULL;
+	const guint8 bytes[] = { 0x00, 0x30, 0x04,0x20, 0x7A, 0x08 };
+
+	new_client_id = g_bytes_new (bytes, sizeof (bytes));
+	test_config (existing_hex_client_id_orig, existing_hex_client_id_expected,
+	             NULL,
+	             NULL,
+	             new_client_id,
+	             "eth0",
+	             NULL);
+}
+
+/*******************************************/
+
+#define EACID "qb:cd:ef:12:34:56"
+
+static const char *existing_ascii_client_id_orig = \
+	"send dhcp-client-identifier \"" EACID "\";\n";
+
+static const char *existing_ascii_client_id_expected = \
+	"# Created by NetworkManager\n"
+	"# Merged from /path/to/dhclient.conf\n"
+	"\n"
+	"send dhcp-client-identifier \"" EACID "\";\n"
+	"\n"
+	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+	"option wpad code 252 = string;\n"
+	"\n"
+	"also request rfc3442-classless-static-routes;\n"
+	"also request ms-classless-static-routes;\n"
+	"also request static-routes;\n"
+	"also request wpad;\n"
+	"also request ntp-servers;\n"
+	"\n";
+
+static void
+test_existing_ascii_client_id (void)
+{
+	gs_unref_bytes GBytes *new_client_id = NULL;
+	char buf[STRLEN (EACID) + 1] = { 0 };
+
+	memcpy (buf + 1, EACID, STRLEN (EACID));
+	new_client_id = g_bytes_new (buf, sizeof (buf));
+	test_config (existing_ascii_client_id_orig, existing_ascii_client_id_expected,
+	             NULL,
+	             NULL,
+	             new_client_id,
 	             "eth0",
 	             NULL);
 }
@@ -205,6 +331,7 @@ test_override_hostname (void)
 	test_config (override_hostname_orig, override_hostname_expected,
 	             "blahblah",
 	             NULL,
+	             NULL,
 	             "eth0",
 	             NULL);
 }
@@ -239,6 +366,7 @@ test_existing_alsoreq (void)
 	test_config (existing_alsoreq_orig, existing_alsoreq_expected,
 	             NULL,
 	             NULL,
+	             NULL,
 	             "eth0",
 	             NULL);
 }
@@ -277,6 +405,7 @@ test_existing_multiline_alsoreq (void)
 	test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected,
 	             NULL,
 	             NULL,
+	             NULL,
 	             "eth0",
 	             NULL);
 }
@@ -585,6 +714,9 @@ main (int argc, char **argv)
 	g_test_add_func ("/dhcp/dhclient/override_client_id", test_override_client_id);
 	g_test_add_func ("/dhcp/dhclient/quote_client_id", test_quote_client_id);
 	g_test_add_func ("/dhcp/dhclient/ascii_client_id", test_ascii_client_id);
+	g_test_add_func ("/dhcp/dhclient/hex_single_client_id", test_hex_single_client_id);
+	g_test_add_func ("/dhcp/dhclient/existing-hex-client-id", test_existing_hex_client_id);
+	g_test_add_func ("/dhcp/dhclient/existing-ascii-client-id", test_existing_ascii_client_id);
 	g_test_add_func ("/dhcp/dhclient/override_hostname", test_override_hostname);
 	g_test_add_func ("/dhcp/dhclient/existing_alsoreq", test_existing_alsoreq);
 	g_test_add_func ("/dhcp/dhclient/existing_multiline_alsoreq", test_existing_multiline_alsoreq);
diff --git a/src/dhcp-manager/tests/test-dhcp-utils.c b/src/dhcp-manager/tests/test-dhcp-utils.c
new file mode 100644
index 00000000..4ff8caad
--- /dev/null
+++ b/src/dhcp-manager/tests/test-dhcp-utils.c
@@ -0,0 +1,723 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2014 Red Hat, Inc.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <nm-utils.h>
+
+#include "nm-dhcp-utils.h"
+#include "nm-logging.h"
+#include "nm-platform.h"
+
+#include "nm-test-utils.h"
+
+typedef struct {
+	const char *name;
+	const char *value;
+} Option;
+
+static GHashTable *
+fill_table (const Option *test_options, GHashTable *table)
+{
+	const Option *opt;
+
+	if (!table)
+		table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+	for (opt = test_options; opt->name; opt++)
+		g_hash_table_insert (table, (gpointer) opt->name, (gpointer) opt->value);
+	return table;
+}
+
+static const Option generic_options[] = {
+	{ "subnet_mask",            "255.255.255.0" },
+	{ "ip_address",             "192.168.1.106" },
+	{ "network_number",         "192.168.1.0" },
+	{ "expiry",                 "1232324877" },
+	{ "dhcp_lease_time",        "3600" },
+	{ "dhcp_server_identifier", "192.168.1.1" },
+	{ "routers",                "192.168.1.1" },
+	{ "domain_name_servers",    "216.254.95.2 216.231.41.2" },
+	{ "dhcp_message_type",      "5" },
+	{ "broadcast_address",      "192.168.1.255" },
+	{ "domain_search",          "foobar.com blah.foobar.com" },
+	{ "host_name",              "nmreallywhipsthe" },
+	{ "domain_name",            "lamasass.com" },
+	{ "interface_mtu",          "987" },
+	{ "static_routes",          "10.1.1.5 10.1.1.1 100.99.88.56 10.1.1.1" },
+	{ NULL, NULL }
+};
+
+static void
+test_generic_options (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const NMPlatformIP4Address *address;
+	const NMPlatformIP4Route *route;
+	guint32 tmp;
+	const char *expected_addr = "192.168.1.106";
+	const char *expected_gw = "192.168.1.1";
+	const char *expected_dns1 = "216.254.95.2";
+	const char *expected_dns2 = "216.231.41.2";
+	const char *expected_search1 = "foobar.com";
+	const char *expected_search2 = "blah.foobar.com";
+	const char *expected_route1_dest = "10.1.1.5";
+	const char *expected_route1_gw = "10.1.1.1";
+	const char *expected_route2_dest = "100.99.88.56";
+	const char *expected_route2_gw = "10.1.1.1";
+
+	options = fill_table (generic_options, NULL);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	/* IP4 address */
+	g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+	address = nm_ip4_config_get_address (ip4_config, 0);
+	g_assert (inet_pton (AF_INET, expected_addr, &tmp) > 0);
+	g_assert (address->address == tmp);
+	g_assert (address->peer_address == 0);
+	g_assert_cmpint (address->plen, ==, 24);
+
+	/* Gateway */
+	g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0);
+	g_assert (nm_ip4_config_get_gateway (ip4_config) == tmp);
+
+	g_assert_cmpint (nm_ip4_config_get_num_wins (ip4_config), ==, 0);
+
+	g_assert_cmpint (nm_ip4_config_get_mtu (ip4_config), ==, 987);
+
+	/* Domain searches */
+	g_assert_cmpint (nm_ip4_config_get_num_searches (ip4_config), ==, 2);
+	g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 0), ==, expected_search1);
+	g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 1), ==, expected_search2);
+
+	/* DNS servers */
+	g_assert_cmpint (nm_ip4_config_get_num_nameservers (ip4_config), ==, 2);
+	g_assert (inet_pton (AF_INET, expected_dns1, &tmp) > 0);
+	g_assert (nm_ip4_config_get_nameserver (ip4_config, 0) == tmp);
+	g_assert (inet_pton (AF_INET, expected_dns2, &tmp) > 0);
+	g_assert (nm_ip4_config_get_nameserver (ip4_config, 1) == tmp);
+
+	/* Routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+
+	/* Route #1 */
+	route = nm_ip4_config_get_route (ip4_config, 0);
+	g_assert (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0);
+	g_assert (route->network == tmp);
+	g_assert (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0);
+	g_assert (route->gateway == tmp);
+	g_assert_cmpint (route->plen, ==, 32);
+	g_assert_cmpint (route->metric, ==, 0);
+
+	/* Route #2 */
+	route = nm_ip4_config_get_route (ip4_config, 1);
+	g_assert (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0);
+	g_assert (route->network == tmp);
+	g_assert (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0);
+	g_assert (route->gateway == tmp);
+	g_assert_cmpint (route->plen, ==, 32);
+	g_assert_cmpint (route->metric, ==, 0);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_wins_options (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const NMPlatformIP4Address *address;
+	guint32 tmp;
+	const char *expected_wins1 = "63.12.199.5";
+	const char *expected_wins2 = "150.4.88.120";
+	static const Option data[] = {
+		{ "netbios_name_servers", "63.12.199.5 150.4.88.120" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	/* IP4 address */
+	g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+	address = nm_ip4_config_get_address (ip4_config, 0);
+	g_assert (address);
+	g_assert_cmpint (nm_ip4_config_get_num_wins (ip4_config), ==, 2);
+	g_assert (inet_pton (AF_INET, expected_wins1, &tmp) > 0);
+	g_assert (nm_ip4_config_get_wins (ip4_config, 0) == tmp);
+	g_assert (inet_pton (AF_INET, expected_wins2, &tmp) > 0);
+	g_assert (nm_ip4_config_get_wins (ip4_config, 1) == tmp);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+ip4_test_route (NMIP4Config *ip4_config,
+                guint route_num,
+                const char *expected_dest,
+                const char *expected_gw,
+                guint expected_prefix)
+{
+	const NMPlatformIP4Route *route;
+	guint32 tmp;
+
+	route = nm_ip4_config_get_route (ip4_config, route_num);
+	g_assert (inet_pton (AF_INET, expected_dest, &tmp) > 0);
+	g_assert (route->network == tmp);
+	g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0);
+	g_assert (route->gateway == tmp);
+	g_assert_cmpint (route->plen, ==, expected_prefix);
+	g_assert_cmpint (route->metric, ==, 0);
+}
+
+static void
+ip4_test_gateway (NMIP4Config *ip4_config, const char *expected_gw)
+{
+	guint32 tmp;
+
+	g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+	g_assert (inet_pton (AF_INET, expected_gw, &tmp) > 0);
+	g_assert (nm_ip4_config_get_gateway (ip4_config) == tmp);
+}
+
+static void
+test_classless_static_routes_1 (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	const char *expected_route2_dest = "10.0.0.0";
+	const char *expected_route2_gw = "10.17.66.41";
+	static const Option data[] = {
+		/* dhclient custom format */
+		{ "rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	/* IP4 routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+	ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 8);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_classless_static_routes_2 (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	const char *expected_route2_dest = "10.0.0.0";
+	const char *expected_route2_gw = "10.17.66.41";
+	static const Option data[] = {
+		/* dhcpcd format */
+		{ "classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	/* IP4 routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+	ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 8);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_fedora_dhclient_classless_static_routes (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "129.210.177.128";
+	const char *expected_route1_gw = "192.168.0.113";
+	const char *expected_route2_dest = "2.0.0.0";
+	const char *expected_route2_gw = "10.34.255.6";
+	const char *expected_gateway = "192.168.0.113";
+	static const Option data[] = {
+		/* Fedora dhclient format */
+		{ "classless_static_routes", "0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	/* IP4 routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 25);
+	ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 7);
+
+	/* Gateway */
+	ip4_test_gateway (ip4_config, expected_gateway);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_dhclient_invalid_classless_routes_1 (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	static const Option data[] = {
+		/* dhclient format */
+		{ "rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*ignoring invalid classless static routes*");
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_test_assert_expected_messages ();
+
+	/* IP4 routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_dhcpcd_invalid_classless_routes_1 (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "10.1.1.5";
+	const char *expected_route1_gw = "10.1.1.1";
+	const char *expected_route2_dest = "100.99.88.56";
+	const char *expected_route2_gw = "10.1.1.1";
+	static const Option data[] = {
+		/* dhcpcd format */
+		{ "classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*ignoring invalid classless static routes*");
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_test_assert_expected_messages ();
+
+	/* Test falling back to old-style static routes if the classless static
+	 * routes are invalid.
+	 */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32);
+	ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_dhclient_invalid_classless_routes_2 (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "10.1.1.5";
+	const char *expected_route1_gw = "10.1.1.1";
+	const char *expected_route2_dest = "100.99.88.56";
+	const char *expected_route2_gw = "10.1.1.1";
+	static const Option data[] = {
+		{ "rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*ignoring invalid classless static routes*");
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_test_assert_expected_messages ();
+
+	/* Test falling back to old-style static routes if the classless static
+	 * routes are invalid.
+	 */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32);
+	ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_dhcpcd_invalid_classless_routes_2 (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "10.1.1.5";
+	const char *expected_route1_gw = "10.1.1.1";
+	const char *expected_route2_dest = "100.99.88.56";
+	const char *expected_route2_gw = "10.1.1.1";
+	static const Option data[] = {
+		{ "classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*ignoring invalid classless static routes*");
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_test_assert_expected_messages ();
+
+	/* Test falling back to old-style static routes if the classless static
+	 * routes are invalid.
+	 */
+
+	/* Routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 2);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 32);
+	ip4_test_route (ip4_config, 1, expected_route2_dest, expected_route2_gw, 32);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_dhclient_invalid_classless_routes_3 (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	static const Option data[] = {
+		{ "rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*ignoring invalid classless static routes*");
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_test_assert_expected_messages ();
+
+	/* IP4 routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_dhcpcd_invalid_classless_routes_3 (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	static Option data[] = {
+		{ "classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*DHCP provided invalid classless static route*");
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_test_assert_expected_messages ();
+
+	/* IP4 routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_dhclient_gw_in_classless_routes (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	const char *expected_gateway = "192.2.3.4";
+	static Option data[] = {
+		{ "rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	/* IP4 routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+	/* Gateway */
+	ip4_test_gateway (ip4_config, expected_gateway);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_dhcpcd_gw_in_classless_routes (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	const char *expected_gateway = "192.2.3.4";
+	static Option data[] = {
+		{ "classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	/* IP4 routes */
+	g_assert_cmpint (nm_ip4_config_get_num_routes (ip4_config), ==, 1);
+	ip4_test_route (ip4_config, 0, expected_route1_dest, expected_route1_gw, 24);
+
+	/* Gateway */
+	ip4_test_gateway (ip4_config, expected_gateway);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_escaped_domain_searches (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_search0 = "host1";
+	const char *expected_search1 = "host2";
+	const char *expected_search2 = "host3";
+	static const Option data[] = {
+		{ "domain_search", "host1\\032host2\\032host3" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	/* domain searches */
+	g_assert_cmpint (nm_ip4_config_get_num_searches (ip4_config), ==, 3);
+	g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 0), ==, expected_search0);
+	g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 1), ==, expected_search1);
+	g_assert_cmpstr (nm_ip4_config_get_search (ip4_config, 2), ==, expected_search2);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_invalid_escaped_domain_searches (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	static const Option data[] = {
+		{ "domain_search", "host1\\aahost2\\032host3" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*invalid domain search*");
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_test_assert_expected_messages ();
+
+	/* domain searches */
+	g_assert_cmpint (nm_ip4_config_get_num_searches (ip4_config), ==, 0);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_ip4_missing_prefix (const char *ip, guint32 expected_prefix)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const NMPlatformIP4Address *address;
+
+	options = fill_table (generic_options, NULL);
+	g_hash_table_insert (options, "ip_address", (gpointer) ip);
+	g_hash_table_remove (options, "subnet_mask");
+
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+	address = nm_ip4_config_get_address (ip4_config, 0);
+	g_assert (address);
+	g_assert_cmpint (address->plen, ==, expected_prefix);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_ip4_missing_prefix_24 (void)
+{
+	test_ip4_missing_prefix ("192.168.1.10", 24);
+}
+
+static void
+test_ip4_missing_prefix_16 (void)
+{
+	test_ip4_missing_prefix ("172.16.54.50", 16);
+}
+
+static void
+test_ip4_missing_prefix_8 (void)
+{
+	test_ip4_missing_prefix ("10.1.2.3", 8);
+}
+
+static void
+test_ip4_prefix_classless (void)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const NMPlatformIP4Address *address;
+
+	/* Ensure that the missing-subnet-mask handler doesn't mangle classless
+	 * subnet masks at all.  The handler should trigger only if the server
+	 * doesn't send the subnet mask.
+	 */
+
+	options = fill_table (generic_options, NULL);
+	g_hash_table_insert (options, "ip_address", "172.16.54.22");
+	g_hash_table_insert (options, "subnet_mask", "255.255.252.0");
+
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+
+	g_assert_cmpint (nm_ip4_config_get_num_addresses (ip4_config), ==, 1);
+	address = nm_ip4_config_get_address (ip4_config, 0);
+	g_assert (address);
+	g_assert_cmpint (address->plen, ==, 22);
+
+	g_hash_table_destroy (options);
+}
+
+#define COMPARE_ID(src, is_str, expected, expected_len) \
+G_STMT_START { \
+	gs_unref_bytes GBytes *b = NULL; \
+	const char *p; \
+	gsize l; \
+ \
+	b = nm_dhcp_utils_client_id_string_to_bytes (src); \
+	g_assert (b); \
+	p = g_bytes_get_data (b, &l); \
+	if (is_str) { \
+		g_assert_cmpint (l, ==, expected_len + 1); \
+		g_assert_cmpint (((const char *) p)[0], ==, 0); \
+		g_assert (memcmp (p + 1, expected, expected_len) == 0); \
+	} else { \
+		g_assert_cmpint (l, ==, expected_len); \
+		g_assert (memcmp (p, expected, expected_len) == 0); \
+	} \
+} G_STMT_END
+
+static void
+test_client_id_from_string (void)
+{
+	const char *nothex = "asdfasdfasdfasdfasdfasdfasdf";
+	const char *allhex = "00:11:22:33:4:55:66:77:88";
+	const guint8 allhex_bin[] = { 0x00, 0x11, 0x22, 0x33, 0x04, 0x55, 0x66, 0x77, 0x88 };
+	const char *somehex = "00:11:22:33:44:55:asdfasdfasdf:99:10";
+	const char *nocolons = "0011223344559910";
+	const char *endcolon = "00:11:22:33:44:55:";
+
+	COMPARE_ID (nothex, TRUE, nothex, strlen (nothex));
+	COMPARE_ID (allhex, FALSE, allhex_bin, sizeof (allhex_bin));
+	COMPARE_ID (somehex, TRUE, somehex, strlen (somehex));
+	COMPARE_ID (nocolons, TRUE, nocolons, strlen (nocolons));
+	COMPARE_ID (endcolon, TRUE, endcolon, strlen (endcolon));
+}
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+	nmtst_init_assert_logging (&argc, &argv);
+	nm_logging_setup ("WARN", "DEFAULT", NULL, NULL);
+
+	g_test_add_func ("/dhcp/generic-options", test_generic_options);
+	g_test_add_func ("/dhcp/wins-options", test_wins_options);
+	g_test_add_func ("/dhcp/classless-static-routes-1", test_classless_static_routes_1);
+	g_test_add_func ("/dhcp/classless-static-routes-2", test_classless_static_routes_2);
+	g_test_add_func ("/dhcp/fedora-dhclient-classless-static-routes", test_fedora_dhclient_classless_static_routes);
+	g_test_add_func ("/dhcp/dhclient-invalid-classless-routes-1", test_dhclient_invalid_classless_routes_1);
+	g_test_add_func ("/dhcp/dhcpcd-invalid-classless-routes-1", test_dhcpcd_invalid_classless_routes_1);
+	g_test_add_func ("/dhcp/dhclient-invalid-classless-routes-2", test_dhclient_invalid_classless_routes_2);
+	g_test_add_func ("/dhcp/dhcpcd-invalid-classless-routes-2", test_dhcpcd_invalid_classless_routes_2);
+	g_test_add_func ("/dhcp/dhclient-invalid-classless-routes-3", test_dhclient_invalid_classless_routes_3);
+	g_test_add_func ("/dhcp/dhcpcd-invalid-classless-routes-3", test_dhcpcd_invalid_classless_routes_3);
+	g_test_add_func ("/dhcp/dhclient-gw-in-classless-routes", test_dhclient_gw_in_classless_routes);
+	g_test_add_func ("/dhcp/dhcpcd-gw-in-classless-routes", test_dhcpcd_gw_in_classless_routes);
+	g_test_add_func ("/dhcp/escaped-domain-searches", test_escaped_domain_searches);
+	g_test_add_func ("/dhcp/invalid-escaped-domain-searches", test_invalid_escaped_domain_searches);
+	g_test_add_func ("/dhcp/ip4-missing-prefix-24", test_ip4_missing_prefix_24);
+	g_test_add_func ("/dhcp/ip4-missing-prefix-16", test_ip4_missing_prefix_16);
+	g_test_add_func ("/dhcp/ip4-missing-prefix-8", test_ip4_missing_prefix_8);
+	g_test_add_func ("/dhcp/ip4-prefix-classless", test_ip4_prefix_classless);
+	g_test_add_func ("/dhcp/client-id-from-string", test_client_id_from_string);
+
+	return g_test_run ();
+}
+