summary refs log tree commit diff
path: root/src/systemd
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
committerMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
commit494f296a3baab08522617b24b1f126d8f9a17502 (patch)
treec8ef32fb0dd1c4ff35a0b38e787abb58692de0cd /src/systemd
parent54f6333410ffd570e62717d9e77c5c987175e397 (diff)
Imported Upstream version 1.1.90 upstream/1.1.90
Diffstat (limited to 'src/systemd')
-rw-r--r--src/systemd/nm-sd-adapt.c237
-rw-r--r--src/systemd/nm-sd-adapt.h152
-rw-r--r--src/systemd/src/basic/alloc-util.c87
-rw-r--r--src/systemd/src/basic/alloc-util.h111
-rw-r--r--src/systemd/src/basic/async.h29
-rw-r--r--src/systemd/src/basic/escape.c493
-rw-r--r--src/systemd/src/basic/escape.h55
-rw-r--r--src/systemd/src/basic/fd-util.c366
-rw-r--r--src/systemd/src/basic/fd-util.h77
-rw-r--r--src/systemd/src/basic/fileio.c1261
-rw-r--r--src/systemd/src/basic/fileio.h86
-rw-r--r--src/systemd/src/basic/fs-util.c526
-rw-r--r--src/systemd/src/basic/fs-util.h79
-rw-r--r--src/systemd/src/basic/hashmap.c1871
-rw-r--r--src/systemd/src/basic/hashmap.h417
-rw-r--r--src/systemd/src/basic/hexdecoct.c702
-rw-r--r--src/systemd/src/basic/hexdecoct.h57
-rw-r--r--src/systemd/src/basic/hostname-util.c230
-rw-r--r--src/systemd/src/basic/hostname-util.h44
-rw-r--r--src/systemd/src/basic/in-addr-util.c360
-rw-r--r--src/systemd/src/basic/in-addr-util.h58
-rw-r--r--src/systemd/src/basic/io-util.c268
-rw-r--r--src/systemd/src/basic/io-util.h81
-rw-r--r--src/systemd/src/basic/list.h186
-rw-r--r--src/systemd/src/basic/log.h246
-rw-r--r--src/systemd/src/basic/macro.h379
-rw-r--r--src/systemd/src/basic/mempool.c108
-rw-r--r--src/systemd/src/basic/mempool.h51
-rw-r--r--src/systemd/src/basic/parse-util.c542
-rw-r--r--src/systemd/src/basic/parse-util.h99
-rw-r--r--src/systemd/src/basic/path-util.c813
-rw-r--r--src/systemd/src/basic/path-util.h115
-rw-r--r--src/systemd/src/basic/prioq.c324
-rw-r--r--src/systemd/src/basic/prioq.h47
-rw-r--r--src/systemd/src/basic/random-util.c140
-rw-r--r--src/systemd/src/basic/random-util.h41
-rw-r--r--src/systemd/src/basic/refcnt.h38
-rw-r--r--src/systemd/src/basic/set.h137
-rw-r--r--src/systemd/src/basic/siphash24.c193
-rw-r--r--src/systemd/src/basic/siphash24.h23
-rw-r--r--src/systemd/src/basic/socket-util.h141
-rw-r--r--src/systemd/src/basic/sparse-endian.h90
-rw-r--r--src/systemd/src/basic/string-table.c38
-rw-r--r--src/systemd/src/basic/string-table.h91
-rw-r--r--src/systemd/src/basic/string-util.c813
-rw-r--r--src/systemd/src/basic/string-util.h188
-rw-r--r--src/systemd/src/basic/strv.c879
-rw-r--r--src/systemd/src/basic/strv.h177
-rw-r--r--src/systemd/src/basic/time-util.c1154
-rw-r--r--src/systemd/src/basic/time-util.h131
-rw-r--r--src/systemd/src/basic/umask-util.h50
-rw-r--r--src/systemd/src/basic/unaligned.h115
-rw-r--r--src/systemd/src/basic/utf8.c405
-rw-r--r--src/systemd/src/basic/utf8.h61
-rw-r--r--src/systemd/src/basic/util.c813
-rw-r--r--src/systemd/src/basic/util.h186
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.c156
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.h36
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.c116
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.h67
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-internal.h72
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-lease-internal.h106
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-network.c237
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-option.c272
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-packet.c193
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-protocol.h159
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h84
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-lease-internal.h78
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-network.c93
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c416
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-protocol.h152
-rw-r--r--src/systemd/src/libsystemd-network/lldp-internal.c364
-rw-r--r--src/systemd/src/libsystemd-network/lldp-internal.h95
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.c87
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.h29
-rw-r--r--src/systemd/src/libsystemd-network/lldp-port.c120
-rw-r--r--src/systemd/src/libsystemd-network/lldp-port.h73
-rw-r--r--src/systemd/src/libsystemd-network/lldp-tlv.c642
-rw-r--r--src/systemd/src/libsystemd-network/lldp-tlv.h98
-rw-r--r--src/systemd/src/libsystemd-network/lldp.h130
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c560
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.h85
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c1781
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c1145
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c1309
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-lease.c414
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4acd.c529
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c366
-rw-r--r--src/systemd/src/libsystemd-network/sd-lldp.c760
-rw-r--r--src/systemd/src/libsystemd/sd-id128/sd-id128.c233
-rw-r--r--src/systemd/src/shared/dns-domain.c1249
-rw-r--r--src/systemd/src/shared/dns-domain.h107
-rw-r--r--src/systemd/src/systemd/_sd-common.h87
-rw-r--r--src/systemd/src/systemd/sd-dhcp-client.h86
-rw-r--r--src/systemd/src/systemd/sd-dhcp-lease.h67
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h81
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-lease.h56
-rw-r--r--src/systemd/src/systemd/sd-event.h142
-rw-r--r--src/systemd/src/systemd/sd-id128.h119
-rw-r--r--src/systemd/src/systemd/sd-ipv4acd.h64
-rw-r--r--src/systemd/src/systemd/sd-ipv4ll.h64
-rw-r--r--src/systemd/src/systemd/sd-lldp.h91
-rw-r--r--src/systemd/src/systemd/sd-ndisc.h88
103 files changed, 29819 insertions, 0 deletions
diff --git a/src/systemd/nm-sd-adapt.c b/src/systemd/nm-sd-adapt.c
new file mode 100644
index 00000000..8b29aed5
--- /dev/null
+++ b/src/systemd/nm-sd-adapt.c
@@ -0,0 +1,237 @@
+/* -*- 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 "fd-util.h"
+#include "time-util.h"
+
+struct sd_event_source {
+	guint refcount;
+	guint id;
+	gpointer user_data;
+
+	GIOChannel *channel;
+
+	union {
+		struct {
+			sd_event_io_handler_t cb;
+		} io;
+		struct {
+			sd_event_time_handler_t cb;
+			uint64_t usec;
+		} time;
+	};
+};
+
+static struct sd_event_source *
+source_new (void)
+{
+	struct sd_event_source *source;
+
+	source = g_slice_new0 (struct sd_event_source);
+	source->refcount = 1;
+	return source;
+}
+
+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_slice_free (struct sd_event_source, 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;
+	gboolean result;
+
+	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;
+
+	source->refcount++;
+
+	r = source->io.cb (source, g_io_channel_unix_get_fd (channel), revents, source->user_data);
+	if (r < 0 || source->refcount <= 1) {
+		source->id = 0;
+		result = G_SOURCE_REMOVE;
+	} else
+		result = G_SOURCE_CONTINUE;
+
+	sd_event_source_unref (source);
+
+	return result;
+}
+
+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;
+
+	/* systemd supports floating sd_event_source by omitting the @s argument.
+	 * We don't have such users and don't implement floating references. */
+	g_return_val_if_fail (s, -EINVAL);
+
+	channel = g_io_channel_unix_new (fd);
+	if (!channel)
+		return -EINVAL;
+
+	source = source_new ();
+	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)
+{
+	source->refcount++;
+
+	source->time.cb (source, source->time.usec, source->user_data);
+	source->id = 0;
+
+	sd_event_source_unref (source);
+
+	return G_SOURCE_REMOVE;
+}
+
+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);
+
+	/* systemd supports floating sd_event_source by omitting the @s argument.
+	 * We don't have such users and don't implement floating references. */
+	g_return_val_if_fail (s, -EINVAL);
+
+	source = source_new ();
+	source->time.cb = callback;
+	source->user_data = userdata;
+	source->time.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/systemd/nm-sd-adapt.h b/src/systemd/nm-sd-adapt.h
new file mode 100644
index 00000000..967cb48f
--- /dev/null
+++ b/src/systemd/nm-sd-adapt.h
@@ -0,0 +1,152 @@
+/* -*- 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 - 2015 Red Hat, Inc.
+ */
+
+#ifndef NM_SD_ADAPT_H
+#define NM_SD_ADAPT_H
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <syslog.h>
+#include <sys/resource.h>
+#include <time.h>
+
+#include "nm-default.h"
+
+#define noreturn G_GNUC_NORETURN
+
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+
+/*****************************************************************************/
+
+static inline NMLogLevel
+_slog_level_to_nm (int slevel)
+{
+    switch (slevel) {
+    case LOG_DEBUG:   return LOGL_DEBUG;
+	case LOG_WARNING: return LOGL_WARN;
+	case LOG_CRIT:
+	case LOG_ERR:     return LOGL_ERR;
+	case LOG_INFO:
+	case LOG_NOTICE:
+	default:          return LOGL_INFO;
+	}
+}
+
+#define log_internal(level, error, file, line, func, format, ...) \
+({ \
+	const int _nm_e = (error); \
+	const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \
+	\
+	if (nm_logging_enabled (_nm_l, LOGD_DHCP)) { \
+		const char *_nm_location = strrchr ((""file), '/'); \
+		\
+		_nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \
+	} \
+	(_nm_e > 0 ? -_nm_e : _nm_e); \
+})
+
+#define log_full_errno(level, error, ...) \
+({ \
+	log_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__); \
+})
+
+#define log_assert_failed(text, file, line, func) \
+G_STMT_START { \
+	log_internal (LOG_CRIT, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", text, file, line, func); \
+	g_assert_not_reached (); \
+} G_STMT_END
+
+#define log_assert_failed_unreachable(text, file, line, func) \
+G_STMT_START { \
+	log_internal (LOG_CRIT, 0, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", text, file, line, func); \
+	g_assert_not_reached (); \
+} G_STMT_END
+
+#define log_assert_failed_return(text, file, line, func) \
+({ \
+	log_internal (LOG_DEBUG, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", text, file, line, func); \
+	g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, text); \
+	(void) 0; \
+})
+
+/*****************************************************************************
+ * The remainder of the header is only enabled when building the systemd code
+ * itself.
+ *****************************************************************************/
+
+#if (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD
+
+#include <netinet/in.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 <net/if_arp.h>
+
+/* Missing in Linux 3.2.0, in Ubuntu 12.04 */
+#ifndef BPF_XOR
+#define BPF_XOR 0xa0
+#endif
+
+/*****************************************************************************/
+
+/* 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
+
+
+/*
+ * Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export
+ * CLOCK_BOOTTIME even though the kernel supports it, so provide a
+ * local definition
+ */
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+
+#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);
+}
+
+static inline bool is_main_thread(void) {
+        return TRUE;
+}
+
+#endif /* (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD */
+
+#endif /* NM_SD_ADAPT_H */
+
diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c
new file mode 100644
index 00000000..a849e139
--- /dev/null
+++ b/src/systemd/src/basic/alloc-util.c
@@ -0,0 +1,87 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "macro.h"
+#include "util.h"
+
+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;
+}
+
+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;
+}
+
+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;
+}
diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h
new file mode 100644
index 00000000..f9be5e85
--- /dev/null
+++ b/src/systemd/src/basic/alloc-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 "nm-sd-adapt.h"
+
+#include <alloca.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macro.h"
+
+#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(1, (n)))
+
+static inline void *mfree(void *memory) {
+        free(memory);
+        return NULL;
+}
+
+void* memdup(const void *p, size_t l) _alloc_(2);
+
+static inline void freep(void *p) {
+        free(*(void**) p);
+}
+
+#define _cleanup_free_ _cleanup_(freep)
+
+_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);
+}
+
+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]))
+
+#define alloca0(n)                                      \
+        ({                                              \
+                char *_new_;                            \
+                size_t _len_ = n;                       \
+                _new_ = alloca(_len_);                  \
+                (void *) memset(_new_, 0, _len_);       \
+        })
+
+/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
+#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_);                        \
+        })
diff --git a/src/systemd/src/basic/async.h b/src/systemd/src/basic/async.h
new file mode 100644
index 00000000..44b886a6
--- /dev/null
+++ b/src/systemd/src/basic/async.h
@@ -0,0 +1,29 @@
+/*-*- 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/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+int asynchronous_job(void* (*func)(void *p), void *arg);
+
+int asynchronous_sync(void);
+int asynchronous_close(int fd);
diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c
new file mode 100644
index 00000000..fe1737e5
--- /dev/null
+++ b/src/systemd/src/basic/escape.c
@@ -0,0 +1,493 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "hexdecoct.h"
+#include "macro.h"
+#include "utf8.h"
+
+size_t cescape_char(char c, char *buf) {
+        char * buf_old = buf;
+
+        switch (c) {
+
+                case '\a':
+                        *(buf++) = '\\';
+                        *(buf++) = 'a';
+                        break;
+                case '\b':
+                        *(buf++) = '\\';
+                        *(buf++) = 'b';
+                        break;
+                case '\f':
+                        *(buf++) = '\\';
+                        *(buf++) = 'f';
+                        break;
+                case '\n':
+                        *(buf++) = '\\';
+                        *(buf++) = 'n';
+                        break;
+                case '\r':
+                        *(buf++) = '\\';
+                        *(buf++) = 'r';
+                        break;
+                case '\t':
+                        *(buf++) = '\\';
+                        *(buf++) = 't';
+                        break;
+                case '\v':
+                        *(buf++) = '\\';
+                        *(buf++) = 'v';
+                        break;
+                case '\\':
+                        *(buf++) = '\\';
+                        *(buf++) = '\\';
+                        break;
+                case '"':
+                        *(buf++) = '\\';
+                        *(buf++) = '"';
+                        break;
+                case '\'':
+                        *(buf++) = '\\';
+                        *(buf++) = '\'';
+                        break;
+
+                default:
+                        /* For special chars we prefer octal over
+                         * hexadecimal encoding, simply because glib's
+                         * g_strescape() does the same */
+                        if ((c < ' ') || (c >= 127)) {
+                                *(buf++) = '\\';
+                                *(buf++) = octchar((unsigned char) c >> 6);
+                                *(buf++) = octchar((unsigned char) c >> 3);
+                                *(buf++) = octchar((unsigned char) c);
+                        } else
+                                *(buf++) = c;
+                        break;
+        }
+
+        return buf - buf_old;
+}
+
+char *cescape_length(const char *s, size_t n) {
+        const char *f;
+        char *r, *t;
+
+        assert(s || n == 0);
+
+        /* Does C style string escaping. May be reversed with
+         * cunescape(). */
+
+        r = new(char, n*4 + 1);
+        if (!r)
+                return NULL;
+
+        for (f = s, t = r; f < s + n; f++)
+                t += cescape_char(*f, t);
+
+        *t = 0;
+
+        return r;
+}
+
+char *cescape(const char *s) {
+        assert(s);
+
+        return cescape_length(s, strlen(s));
+}
+
+int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
+        int r = 1;
+
+        assert(p);
+        assert(*p);
+        assert(ret);
+
+        /* Unescapes C style. Returns the unescaped character in ret,
+         * unless we encountered a \u sequence in which case the full
+         * unicode character is returned in ret_unicode, instead. */
+
+        if (length != (size_t) -1 && length < 1)
+                return -EINVAL;
+
+        switch (p[0]) {
+
+        case 'a':
+                *ret = '\a';
+                break;
+        case 'b':
+                *ret = '\b';
+                break;
+        case 'f':
+                *ret = '\f';
+                break;
+        case 'n':
+                *ret = '\n';
+                break;
+        case 'r':
+                *ret = '\r';
+                break;
+        case 't':
+                *ret = '\t';
+                break;
+        case 'v':
+                *ret = '\v';
+                break;
+        case '\\':
+                *ret = '\\';
+                break;
+        case '"':
+                *ret = '"';
+                break;
+        case '\'':
+                *ret = '\'';
+                break;
+
+        case 's':
+                /* This is an extension of the XDG syntax files */
+                *ret = ' ';
+                break;
+
+        case 'x': {
+                /* hexadecimal encoding */
+                int a, b;
+
+                if (length != (size_t) -1 && length < 3)
+                        return -EINVAL;
+
+                a = unhexchar(p[1]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unhexchar(p[2]);
+                if (b < 0)
+                        return -EINVAL;
+
+                /* Don't allow NUL bytes */
+                if (a == 0 && b == 0)
+                        return -EINVAL;
+
+                *ret = (char) ((a << 4U) | b);
+                r = 3;
+                break;
+        }
+
+        case 'u': {
+                /* C++11 style 16bit unicode */
+
+                int a[4];
+                unsigned i;
+                uint32_t c;
+
+                if (length != (size_t) -1 && length < 5)
+                        return -EINVAL;
+
+                for (i = 0; i < 4; i++) {
+                        a[i] = unhexchar(p[1 + i]);
+                        if (a[i] < 0)
+                                return a[i];
+                }
+
+                c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
+
+                /* Don't allow 0 chars */
+                if (c == 0)
+                        return -EINVAL;
+
+                if (c < 128)
+                        *ret = c;
+                else {
+                        if (!ret_unicode)
+                                return -EINVAL;
+
+                        *ret = 0;
+                        *ret_unicode = c;
+                }
+
+                r = 5;
+                break;
+        }
+
+        case 'U': {
+                /* C++11 style 32bit unicode */
+
+                int a[8];
+                unsigned i;
+                uint32_t c;
+
+                if (length != (size_t) -1 && length < 9)
+                        return -EINVAL;
+
+                for (i = 0; i < 8; i++) {
+                        a[i] = unhexchar(p[1 + i]);
+                        if (a[i] < 0)
+                                return a[i];
+                }
+
+                c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
+                    ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] <<  8U) | ((uint32_t) a[6] <<  4U) |  (uint32_t) a[7];
+
+                /* Don't allow 0 chars */
+                if (c == 0)
+                        return -EINVAL;
+
+                /* Don't allow invalid code points */
+                if (!unichar_is_valid(c))
+                        return -EINVAL;
+
+                if (c < 128)
+                        *ret = c;
+                else {
+                        if (!ret_unicode)
+                                return -EINVAL;
+
+                        *ret = 0;
+                        *ret_unicode = c;
+                }
+
+                r = 9;
+                break;
+        }
+
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7': {
+                /* octal encoding */
+                int a, b, c;
+                uint32_t m;
+
+                if (length != (size_t) -1 && length < 3)
+                        return -EINVAL;
+
+                a = unoctchar(p[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unoctchar(p[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unoctchar(p[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                /* don't allow NUL bytes */
+                if (a == 0 && b == 0 && c == 0)
+                        return -EINVAL;
+
+                /* Don't allow bytes above 255 */
+                m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
+                if (m > 255)
+                        return -EINVAL;
+
+                *ret = m;
+                r = 3;
+                break;
+        }
+
+        default:
+                return -EINVAL;
+        }
+
+        return r;
+}
+
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
+        char *r, *t;
+        const char *f;
+        size_t pl;
+
+        assert(s);
+        assert(ret);
+
+        /* Undoes C style string escaping, and optionally prefixes it. */
+
+        pl = prefix ? strlen(prefix) : 0;
+
+        r = new(char, pl+length+1);
+        if (!r)
+                return -ENOMEM;
+
+        if (prefix)
+                memcpy(r, prefix, pl);
+
+        for (f = s, t = r + pl; f < s + length; f++) {
+                size_t remaining;
+                uint32_t u;
+                char c;
+                int k;
+
+                remaining = s + length - f;
+                assert(remaining > 0);
+
+                if (*f != '\\') {
+                        /* A literal literal, copy verbatim */
+                        *(t++) = *f;
+                        continue;
+                }
+
+                if (remaining == 1) {
+                        if (flags & UNESCAPE_RELAX) {
+                                /* A trailing backslash, copy verbatim */
+                                *(t++) = *f;
+                                continue;
+                        }
+
+                        free(r);
+                        return -EINVAL;
+                }
+
+                k = cunescape_one(f + 1, remaining - 1, &c, &u);
+                if (k < 0) {
+                        if (flags & UNESCAPE_RELAX) {
+                                /* Invalid escape code, let's take it literal then */
+                                *(t++) = '\\';
+                                continue;
+                        }
+
+                        free(r);
+                        return k;
+                }
+
+                if (c != 0)
+                        /* Non-Unicode? Let's encode this directly */
+                        *(t++) = c;
+                else
+                        /* Unicode? Then let's encode this in UTF-8 */
+                        t += utf8_encode_unichar(t, u);
+
+                f += k;
+        }
+
+        *t = 0;
+
+        *ret = r;
+        return t - r;
+}
+
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+        return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
+
+int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+        return cunescape_length(s, strlen(s), flags, ret);
+}
+
+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;
+}
+
+static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
+        assert(bad);
+
+        for (; *s; s++) {
+                if (*s == '\\' || strchr(bad, *s))
+                        *(t++) = '\\';
+
+                *(t++) = *s;
+        }
+
+        return t;
+}
+
+char *shell_escape(const char *s, const char *bad) {
+        char *r, *t;
+
+        r = new(char, strlen(s)*2+1);
+        if (!r)
+                return NULL;
+
+        t = strcpy_backslash_escaped(r, s, bad);
+        *t = 0;
+
+        return r;
+}
+
+char *shell_maybe_quote(const char *s) {
+        const char *p;
+        char *r, *t;
+
+        assert(s);
+
+        /* Encloses a string in double quotes if necessary to make it
+         * OK as shell string. */
+
+        for (p = s; *p; p++)
+                if (*p <= ' ' ||
+                    *p >= 127 ||
+                    strchr(SHELL_NEED_QUOTES, *p))
+                        break;
+
+        if (!*p)
+                return strdup(s);
+
+        r = new(char, 1+strlen(s)*2+1+1);
+        if (!r)
+                return NULL;
+
+        t = r;
+        *(t++) = '"';
+        t = mempcpy(t, s, p - s);
+
+        t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
+
+        *(t++)= '"';
+        *t = 0;
+
+        return r;
+}
diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h
new file mode 100644
index 00000000..da8f7143
--- /dev/null
+++ b/src/systemd/src/basic/escape.h
@@ -0,0 +1,55 @@
+/*-*- 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 <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "string-util.h"
+
+/* 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 "'()<>|&;"
+
+typedef enum UnescapeFlags {
+        UNESCAPE_RELAX = 1,
+} UnescapeFlags;
+
+char *cescape(const char *s);
+char *cescape_length(const char *s, size_t n);
+size_t cescape_char(char c, char *buf);
+
+int cunescape(const char *s, UnescapeFlags flags, char **ret);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
+int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode);
+
+char *xescape(const char *s, const char *bad);
+
+char *shell_escape(const char *s, const char *bad);
+char *shell_maybe_quote(const char *s);
diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c
new file mode 100644
index 00000000..3828148c
--- /dev/null
+++ b/src/systemd/src/basic/fd-util.c
@@ -0,0 +1,366 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#endif /* NM_IGNORED */
+#include "parse-util.h"
+#include "path-util.h"
+#include "socket-util.h"
+#include "util.h"
+
+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;
+}
+
+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]);
+}
+
+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 fclose_nointr(FILE *f) {
+        assert(f);
+
+        /* Same as close_nointr(), but for fclose() */
+
+        if (fclose(f) == 0)
+                return 0;
+
+        if (errno == EINTR)
+                return 0;
+
+        return -errno;
+}
+
+FILE* safe_fclose(FILE *f) {
+
+        /* Same as safe_close(), but for fclose() */
+
+        if (f) {
+                PROTECT_ERRNO;
+
+                assert_se(fclose_nointr(f) != EBADF);
+        }
+
+        return NULL;
+}
+
+DIR* safe_closedir(DIR *d) {
+
+        if (d) {
+                PROTECT_ERRNO;
+
+                assert_se(closedir(d) >= 0 || errno != EBADF);
+        }
+
+        return NULL;
+}
+
+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;
+}
+
+#if 0 /* NM_IGNORED */
+_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 (hidden_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;
+}
+
+int same_fd(int a, int b) {
+        struct stat sta, stb;
+        pid_t pid;
+        int r, fa, fb;
+
+        assert(a >= 0);
+        assert(b >= 0);
+
+        /* Compares two file descriptors. Note that semantics are
+         * quite different depending on whether we have kcmp() or we
+         * don't. If we have kcmp() this will only return true for
+         * dup()ed file descriptors, but not otherwise. If we don't
+         * have kcmp() this will also return true for two fds of the same
+         * file, created by separate open() calls. Since we use this
+         * call mostly for filtering out duplicates in the fd store
+         * this difference hopefully doesn't matter too much. */
+
+        if (a == b)
+                return true;
+
+        /* Try to use kcmp() if we have it. */
+        pid = getpid();
+        r = kcmp(pid, pid, KCMP_FILE, a, b);
+        if (r == 0)
+                return true;
+        if (r > 0)
+                return false;
+        if (errno != ENOSYS)
+                return -errno;
+
+        /* We don't have kcmp(), use fstat() instead. */
+        if (fstat(a, &sta) < 0)
+                return -errno;
+
+        if (fstat(b, &stb) < 0)
+                return -errno;
+
+        if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
+                return false;
+
+        /* We consider all device fds different, since two device fds
+         * might refer to quite different device contexts even though
+         * they share the same inode and backing dev_t. */
+
+        if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
+                return false;
+
+        if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
+                return false;
+
+        /* The fds refer to the same inode on disk, let's also check
+         * if they have the same fd flags. This is useful to
+         * distinguish the read and write side of a pipe created with
+         * pipe(). */
+        fa = fcntl(a, F_GETFL);
+        if (fa < 0)
+                return -errno;
+
+        fb = fcntl(b, F_GETFL);
+        if (fb < 0)
+                return -errno;
+
+        return fa == fb;
+}
+#endif /* NM_IGNORED */
+
+void cmsg_close_all(struct msghdr *mh) {
+        struct cmsghdr *cmsg;
+
+        assert(mh);
+
+        CMSG_FOREACH(cmsg, mh)
+                if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+                        close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+}
+
+bool fdname_is_valid(const char *s) {
+        const char *p;
+
+        /* Validates a name for $LISTEN_FDNAMES. We basically allow
+         * everything ASCII that's not a control character. Also, as
+         * special exception the ":" character is not allowed, as we
+         * use that as field separator in $LISTEN_FDNAMES.
+         *
+         * Note that the empty string is explicitly allowed
+         * here. However, we limit the length of the names to 255
+         * characters. */
+
+        if (!s)
+                return false;
+
+        for (p = s; *p; p++) {
+                if (*p < ' ')
+                        return false;
+                if (*p >= 127)
+                        return false;
+                if (*p == ':')
+                        return false;
+        }
+
+        return p - s < 256;
+}
diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h
new file mode 100644
index 00000000..3614dc78
--- /dev/null
+++ b/src/systemd/src/basic/fd-util.h
@@ -0,0 +1,77 @@
+/*-*- 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 <dirent.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/socket.h>
+
+#include "macro.h"
+
+/* Make sure we can distinguish fd 0 and NULL */
+#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
+#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
+
+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 fclose_nointr(FILE *f);
+FILE* safe_fclose(FILE *f);
+DIR* safe_closedir(DIR *f);
+
+static inline void closep(int *fd) {
+        safe_close(*fd);
+}
+
+static inline void close_pairp(int (*p)[2]) {
+        safe_close_pair(*p);
+}
+
+static inline void fclosep(FILE **f) {
+        safe_fclose(*f);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
+
+#define _cleanup_close_ _cleanup_(closep)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_pclose_ _cleanup_(pclosep)
+#define _cleanup_closedir_ _cleanup_(closedirp)
+#define _cleanup_close_pair_ _cleanup_(close_pairp)
+
+int fd_nonblock(int fd, bool nonblock);
+int fd_cloexec(int fd, bool cloexec);
+
+int close_all_fds(const int except[], unsigned n_except);
+
+int same_fd(int a, int b);
+
+void cmsg_close_all(struct msghdr *mh);
+
+bool fdname_is_valid(const char *s);
diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c
new file mode 100644
index 00000000..913c6293
--- /dev/null
+++ b/src/systemd/src/basic/fileio.c
@@ -0,0 +1,1261 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "ctype.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hexdecoct.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "random-util.h"
+#if 0 /* NM_IGNORED */
+#include "stdio-util.h"
+#endif /* NM_IGNORED */
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "umask-util.h"
+#include "utf8.h"
+
+int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
+
+        assert(f);
+        assert(line);
+
+        fputs(line, f);
+        if (enforce_newline && !endswith(line, "\n"))
+                fputc('\n', f);
+
+        return fflush_and_check(f);
+}
+
+static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
+        _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;
+
+        (void) fchmod_umask(fileno(f), 0644);
+
+        r = write_string_stream(f, line, enforce_newline);
+        if (r >= 0) {
+                if (rename(p, fn) < 0)
+                        r = -errno;
+        }
+
+        if (r < 0)
+                (void) unlink(p);
+
+        return r;
+}
+
+int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
+        _cleanup_fclose_ FILE *f = NULL;
+        int q, r;
+
+        assert(fn);
+        assert(line);
+
+        if (flags & WRITE_STRING_FILE_ATOMIC) {
+                assert(flags & WRITE_STRING_FILE_CREATE);
+
+                r = write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+                if (r < 0)
+                        goto fail;
+
+                return r;
+        }
+
+        if (flags & WRITE_STRING_FILE_CREATE) {
+                f = fopen(fn, "we");
+                if (!f) {
+                        r = -errno;
+                        goto fail;
+                }
+        } else {
+                int fd;
+
+                /* 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) {
+                        r = -errno;
+                        goto fail;
+                }
+
+                f = fdopen(fd, "we");
+                if (!f) {
+                        r = -errno;
+                        safe_close(fd);
+                        goto fail;
+                }
+        }
+
+        r = write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+        if (r < 0)
+                goto fail;
+
+        return 0;
+
+fail:
+        if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE))
+                return r;
+
+        f = safe_fclose(f);
+
+        /* OK, the operation failed, but let's see if the right
+         * contents in place already. If so, eat up the error. */
+
+        q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+        if (q <= 0)
+                return r;
+
+        return 0;
+}
+
+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 verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *buf = NULL;
+        size_t l, k;
+
+        assert(fn);
+        assert(blob);
+
+        l = strlen(blob);
+
+        if (accept_extra_nl && endswith(blob, "\n"))
+                accept_extra_nl = false;
+
+        buf = malloc(l + accept_extra_nl + 1);
+        if (!buf)
+                return -ENOMEM;
+
+        f = fopen(fn, "re");
+        if (!f)
+                return -errno;
+
+        /* We try to read one byte more than we need, so that we know whether we hit eof */
+        errno = 0;
+        k = fread(buf, 1, l + accept_extra_nl + 1, f);
+        if (ferror(f))
+                return errno > 0 ? -errno : -EIO;
+
+        if (k != l && k != l + accept_extra_nl)
+                return 0;
+        if (memcmp(buf, blob, l) != 0)
+                return 0;
+        if (k > l && buf[l] != '\n')
+                return 0;
+
+        return 1;
+}
+
+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 = NULL;
+
+                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 = NULL;
+
+                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 not include whitespace or the delimiter (':'). pattern matches only
+ * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
+ * zeros after the ':' will be skipped. field must be freed afterwards.
+ * terminator specifies the terminating characters of the field value (not
+ * included in the value).
+ */
+int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
+        _cleanup_free_ char *status = NULL;
+        char *t, *f;
+        size_t len;
+        int r;
+
+        assert(terminator);
+        assert(filename);
+        assert(pattern);
+        assert(field);
+
+        r = read_full_file(filename, &status, NULL);
+        if (r < 0)
+                return r;
+
+        t = status;
+
+        do {
+                bool pattern_ok;
+
+                do {
+                        t = strstr(t, pattern);
+                        if (!t)
+                                return -ENOENT;
+
+                        /* Check that pattern occurs in beginning of line. */
+                        pattern_ok = (t == status || t[-1] == '\n');
+
+                        t += strlen(pattern);
+
+                } while (!pattern_ok);
+
+                t += strspn(t, " \t");
+                if (!*t)
+                        return -ENOENT;
+
+        } while (*t != ':');
+
+        t++;
+
+        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, terminator);
+
+        f = strndup(t, len);
+        if (!f)
+                return -ENOMEM;
+
+        *field = f;
+        return 0;
+}
+
+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;
+}
+
+#if 0 /* NM_IGNORED */
+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);
+}
+#endif /* NM_IGNORED */
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+        FILE *f;
+        char *t;
+        int r, fd;
+
+        assert(path);
+        assert(_f);
+        assert(_temp_path);
+
+        r = tempfn_xxxxxx(path, NULL, &t);
+        if (r < 0)
+                return r;
+
+        fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
+        if (fd < 0) {
+                free(t);
+                return -errno;
+        }
+
+        f = fdopen(fd, "we");
+        if (!f) {
+                unlink_noerrno(t);
+                free(t);
+                safe_close(fd);
+                return -errno;
+        }
+
+        *_f = f;
+        *_temp_path = t;
+
+        return 0;
+}
+
+int fflush_and_check(FILE *f) {
+        assert(f);
+
+        errno = 0;
+        fflush(f);
+
+        if (ferror(f))
+                return errno ? -errno : -EIO;
+
+        return 0;
+}
+
+/* This is much like like mkostemp() but is subject to umask(). */
+int mkostemp_safe(char *pattern, int flags) {
+        _cleanup_umask_ mode_t u = 0;
+        int fd;
+
+        assert(pattern);
+
+        u = umask(077);
+
+        fd = mkostemp(pattern, flags);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
+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|O_EXCL, S_IRUSR|S_IWUSR);
+        if (fd >= 0)
+                return fd;
+#endif
+
+        /* Fall back to unguessable name + unlinking */
+        p = strjoina(path, "/systemd-tmp-XXXXXX");
+
+        fd = mkostemp_safe(p, flags);
+        if (fd < 0)
+                return fd;
+
+        unlink(p);
+        return fd;
+}
+
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
+        const char *fn;
+        char *t;
+
+        assert(p);
+        assert(ret);
+
+        /*
+         * Turns this:
+         *         /foo/bar/waldo
+         *
+         * Into this:
+         *         /foo/bar/.#<extra>waldoXXXXXX
+         */
+
+        fn = basename(p);
+        if (!filename_is_valid(fn))
+                return -EINVAL;
+
+        if (extra == NULL)
+                extra = "";
+
+        t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
+        if (!t)
+                return -ENOMEM;
+
+        strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
+
+        *ret = path_kill_slashes(t);
+        return 0;
+}
+
+int tempfn_random(const char *p, const char *extra, char **ret) {
+        const char *fn;
+        char *t, *x;
+        uint64_t u;
+        unsigned i;
+
+        assert(p);
+        assert(ret);
+
+        /*
+         * Turns this:
+         *         /foo/bar/waldo
+         *
+         * Into this:
+         *         /foo/bar/.#<extra>waldobaa2a261115984a9
+         */
+
+        fn = basename(p);
+        if (!filename_is_valid(fn))
+                return -EINVAL;
+
+        if (!extra)
+                extra = "";
+
+        t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
+        if (!t)
+                return -ENOMEM;
+
+        x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
+
+        u = random_u64();
+        for (i = 0; i < 16; i++) {
+                *(x++) = hexchar(u & 0xF);
+                u >>= 4;
+        }
+
+        *x = 0;
+
+        *ret = path_kill_slashes(t);
+        return 0;
+}
+
+int tempfn_random_child(const char *p, const char *extra, char **ret) {
+        char *t, *x;
+        uint64_t u;
+        unsigned i;
+
+        assert(p);
+        assert(ret);
+
+        /* Turns this:
+         *         /foo/bar/waldo
+         * Into this:
+         *         /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
+         */
+
+        if (!extra)
+                extra = "";
+
+        t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
+        if (!t)
+                return -ENOMEM;
+
+        x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
+
+        u = random_u64();
+        for (i = 0; i < 16; i++) {
+                *(x++) = hexchar(u & 0xF);
+                u >>= 4;
+        }
+
+        *x = 0;
+
+        *ret = path_kill_slashes(t);
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int write_timestamp_file_atomic(const char *fn, usec_t n) {
+        char ln[DECIMAL_STR_MAX(n)+2];
+
+        /* Creates a "timestamp" file, that contains nothing but a
+         * usec_t timestamp, formatted in ASCII. */
+
+        if (n <= 0 || n >= USEC_INFINITY)
+                return -ERANGE;
+
+        xsprintf(ln, USEC_FMT "\n", n);
+
+        return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+}
+#endif /* NM_IGNORED */
+
+int read_timestamp_file(const char *fn, usec_t *ret) {
+        _cleanup_free_ char *ln = NULL;
+        uint64_t t;
+        int r;
+
+        r = read_one_line_file(fn, &ln);
+        if (r < 0)
+                return r;
+
+        r = safe_atou64(ln, &t);
+        if (r < 0)
+                return r;
+
+        if (t <= 0 || t >= (uint64_t) USEC_INFINITY)
+                return -ERANGE;
+
+        *ret = (usec_t) t;
+        return 0;
+}
diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h
new file mode 100644
index 00000000..daaa7802
--- /dev/null
+++ b/src/systemd/src/basic/fileio.h
@@ -0,0 +1,86 @@
+/*-*- 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 <dirent.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+typedef enum {
+        WRITE_STRING_FILE_CREATE = 1,
+        WRITE_STRING_FILE_ATOMIC = 2,
+        WRITE_STRING_FILE_AVOID_NEWLINE = 4,
+        WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8,
+} WriteStringFileFlags;
+
+int write_string_stream(FILE *f, const char *line, bool enforce_newline);
+int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags);
+
+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 verify_file(const char *fn, const char *blob, bool accept_extra_nl);
+
+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_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
+
+DIR *xopendirat(int dirfd, const char *name, int flags);
+
+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
+
+int fflush_and_check(FILE *f);
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
+int mkostemp_safe(char *pattern, int flags);
+int open_tmpfile(const char *path, int flags);
+
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
+int tempfn_random(const char *p, const char *extra, char **ret);
+int tempfn_random_child(const char *p, const char *extra, char **ret);
+
+int write_timestamp_file_atomic(const char *fn, usec_t n);
+int read_timestamp_file(const char *fn, usec_t *ret);
diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c
new file mode 100644
index 00000000..741bd31f
--- /dev/null
+++ b/src/systemd/src/basic/fs-util.c
@@ -0,0 +1,526 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#if 0 /* NM_IGNORED */
+#include "dirent-util.h"
+#endif /* NM_IGNORED */
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#include "mkdir.h"
+#endif /* NM_IGNORED */
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+#if 0 /* NM_IGNORED */
+#include "user-util.h"
+#endif /* NM_IGNORED */
+#include "util.h"
+
+int unlink_noerrno(const char *path) {
+        PROTECT_ERRNO;
+        int r;
+
+        r = unlink(path);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+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;
+
+                t = strndup(path, l);
+                if (!t)
+                        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;
+}
+
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+        struct stat buf;
+        int ret;
+
+        ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
+        if (ret >= 0)
+                return 0;
+
+        /* renameat2() exists since Linux 3.15, btrfs added support for it later.
+         * If it is not implemented, fallback to another method. */
+        if (!IN_SET(errno, EINVAL, ENOSYS))
+                return -errno;
+
+        /* The link()/unlink() fallback does not work on directories. But
+         * renameat() without RENAME_NOREPLACE gives the same semantics on
+         * directories, except when newpath is an *empty* directory. This is
+         * good enough. */
+        ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
+        if (ret >= 0 && S_ISDIR(buf.st_mode)) {
+                ret = renameat(olddirfd, oldpath, newdirfd, newpath);
+                return ret >= 0 ? 0 : -errno;
+        }
+
+        /* If it is not a directory, use the link()/unlink() fallback. */
+        ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
+        if (ret < 0)
+                return -errno;
+
+        ret = unlinkat(olddirfd, oldpath, 0);
+        if (ret < 0) {
+                /* backup errno before the following unlinkat() alters it */
+                ret = errno;
+                (void) unlinkat(newdirfd, newpath, 0);
+                errno = ret;
+                return -errno;
+        }
+
+        return 0;
+}
+
+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 readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
+        _cleanup_free_ char *target = NULL, *t = NULL;
+        const char *full;
+        int r;
+
+        full = prefix_roota(root, path);
+        r = readlink_malloc(full, &target);
+        if (r < 0)
+                return r;
+
+        t = file_in_same_dir(path, target);
+        if (!t)
+                return -ENOMEM;
+
+        *ret = t;
+        t = NULL;
+
+        return 0;
+}
+
+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_INVALID)
+                if (chmod(path, mode) < 0)
+                        return -errno;
+
+        if (uid != UID_INVALID || gid != GID_INVALID)
+                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_INVALID)
+                if (fchmod(fd, mode) < 0)
+                        return -errno;
+
+        if (uid != UID_INVALID || gid != GID_INVALID)
+                if (fchown(fd, uid, gid) < 0)
+                        return -errno;
+
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+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;
+}
+
+#if 0 /* NM_IGNORED */
+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;
+}
+
+int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
+        _cleanup_close_ int fd;
+        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 != MODE_INVALID) {
+                r = fchmod(fd, mode);
+                if (r < 0)
+                        return -errno;
+        }
+
+        if (uid != UID_INVALID || gid != GID_INVALID) {
+                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_INVALID, GID_INVALID, MODE_INVALID);
+}
+
+int symlink_idempotent(const char *from, const char *to) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        assert(from);
+        assert(to);
+
+        if (symlink(from, to) < 0) {
+                if (errno != EEXIST)
+                        return -errno;
+
+                r = readlink_malloc(to, &p);
+                if (r < 0)
+                        return r;
+
+                if (!streq(p, from))
+                        return -EINVAL;
+        }
+
+        return 0;
+}
+
+int symlink_atomic(const char *from, const char *to) {
+        _cleanup_free_ char *t = NULL;
+        int r;
+
+        assert(from);
+        assert(to);
+
+        r = tempfn_random(to, NULL, &t);
+        if (r < 0)
+                return r;
+
+        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;
+        int r;
+
+        assert(path);
+
+        r = tempfn_random(path, NULL, &t);
+        if (r < 0)
+                return r;
+
+        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;
+        int r;
+
+        assert(path);
+
+        r = tempfn_random(path, NULL, &t);
+        if (r < 0)
+                return r;
+
+        if (mkfifo(t, mode) < 0)
+                return -errno;
+
+        if (rename(t, path) < 0) {
+                unlink_noerrno(t);
+                return -errno;
+        }
+
+        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 /* NM_IGNORED */
diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h
new file mode 100644
index 00000000..d13c0194
--- /dev/null
+++ b/src/systemd/src/basic/fs-util.h
@@ -0,0 +1,79 @@
+/*-*- 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 <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/inotify.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "time-util.h"
+
+int unlink_noerrno(const char *path);
+
+int rmdir_parents(const char *path, const char *stop);
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
+
+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 readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
+
+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 fchmod_umask(int fd, mode_t mode);
+
+int fd_warn_permissions(const char *path, int fd);
+
+#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
+
+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);
+
+int symlink_idempotent(const char *from, const char *to);
+
+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 get_files_in_directory(const char *path, char ***list);
+
+#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
+
+#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
+        for ((e) = &buffer.ev;                                \
+             (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
+             (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
+
+union inotify_event_buffer {
+        struct inotify_event ev;
+        uint8_t raw[INOTIFY_EVENT_MAX];
+};
diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c
new file mode 100644
index 00000000..4ebb73b5
--- /dev/null
+++ b/src/systemd/src/basic/hashmap.c
@@ -0,0 +1,1871 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2014 Michal Schmidt
+
+  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 <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "mempool.h"
+#if 0 /* NM_IGNORED */
+#include "process-util.h"
+#endif /* NM_IGNORED */
+#include "random-util.h"
+#include "set.h"
+#include "siphash24.h"
+#include "strv.h"
+#include "util.h"
+
+#ifdef ENABLE_DEBUG_HASHMAP
+#include <pthread.h>
+#include "list.h"
+#endif
+
+/*
+ * Implementation of hashmaps.
+ * Addressing: open
+ *   - uses less RAM compared to closed addressing (chaining), because
+ *     our entries are small (especially in Sets, which tend to contain
+ *     the majority of entries in systemd).
+ * Collision resolution: Robin Hood
+ *   - tends to equalize displacement of entries from their optimal buckets.
+ * Probe sequence: linear
+ *   - though theoretically worse than random probing/uniform hashing/double
+ *     hashing, it is good for cache locality.
+ *
+ * References:
+ * Celis, P. 1986. Robin Hood Hashing.
+ * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada.
+ * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
+ * - The results are derived for random probing. Suggests deletion with
+ *   tombstones and two mean-centered search methods. None of that works
+ *   well for linear probing.
+ *
+ * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies.
+ * ACM Trans. Algorithms 1, 2 (October 2005), 177-213.
+ * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964
+ * http://www.math.uu.se/~svante/papers/sj157.pdf
+ * - Applies to Robin Hood with linear probing. Contains remarks on
+ *   the unsuitability of mean-centered search with linear probing.
+ *
+ * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing.
+ * ACM Trans. Algorithms 1, 2 (October 2005), 214-242.
+ * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965
+ * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes
+ *   in a successful search), and Janson writes about displacement. C = d + 1.
+ *
+ * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion.
+ * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
+ * - Explanation of backward shift deletion with pictures.
+ *
+ * Khuong, P. 2013. The Other Robin Hood Hashing.
+ * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/
+ * - Short summary of random vs. linear probing, and tombstones vs. backward shift.
+ */
+
+/*
+ * XXX Ideas for improvement:
+ * For unordered hashmaps, randomize iteration order, similarly to Perl:
+ * http://blog.booking.com/hardening-perls-hash-function.html
+ */
+
+/* INV_KEEP_FREE = 1 / (1 - max_load_factor)
+ * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */
+#define INV_KEEP_FREE            5U
+
+/* Fields common to entries of all hashmap/set types */
+struct hashmap_base_entry {
+        const void *key;
+};
+
+/* Entry types for specific hashmap/set types
+ * hashmap_base_entry must be at the beginning of each entry struct. */
+
+struct plain_hashmap_entry {
+        struct hashmap_base_entry b;
+        void *value;
+};
+
+struct ordered_hashmap_entry {
+        struct plain_hashmap_entry p;
+        unsigned iterate_next, iterate_previous;
+};
+
+struct set_entry {
+        struct hashmap_base_entry b;
+};
+
+/* In several functions it is advantageous to have the hash table extended
+ * virtually by a couple of additional buckets. We reserve special index values
+ * for these "swap" buckets. */
+#define _IDX_SWAP_BEGIN     (UINT_MAX - 3)
+#define IDX_PUT             (_IDX_SWAP_BEGIN + 0)
+#define IDX_TMP             (_IDX_SWAP_BEGIN + 1)
+#define _IDX_SWAP_END       (_IDX_SWAP_BEGIN + 2)
+
+#define IDX_FIRST           (UINT_MAX - 1) /* special index for freshly initialized iterators */
+#define IDX_NIL             UINT_MAX       /* special index value meaning "none" or "end" */
+
+assert_cc(IDX_FIRST == _IDX_SWAP_END);
+assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
+
+/* Storage space for the "swap" buckets.
+ * All entry types can fit into a ordered_hashmap_entry. */
+struct swap_entries {
+        struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
+};
+
+/* Distance from Initial Bucket */
+typedef uint8_t dib_raw_t;
+#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU)   /* indicates DIB value is greater than representable */
+#define DIB_RAW_REHASH   ((dib_raw_t)0xfeU)   /* entry yet to be rehashed during in-place resize */
+#define DIB_RAW_FREE     ((dib_raw_t)0xffU)   /* a free bucket */
+#define DIB_RAW_INIT     ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
+
+#define DIB_FREE UINT_MAX
+
+#ifdef ENABLE_DEBUG_HASHMAP
+struct hashmap_debug_info {
+        LIST_FIELDS(struct hashmap_debug_info, debug_list);
+        unsigned max_entries;  /* high watermark of n_entries */
+
+        /* who allocated this hashmap */
+        int line;
+        const char *file;
+        const char *func;
+
+        /* fields to detect modification while iterating */
+        unsigned put_count;    /* counts puts into the hashmap */
+        unsigned rem_count;    /* counts removals from hashmap */
+        unsigned last_rem_idx; /* remembers last removal index */
+};
+
+/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
+static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
+static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
+
+#else /* !ENABLE_DEBUG_HASHMAP */
+#define HASHMAP_DEBUG_FIELDS
+#endif /* ENABLE_DEBUG_HASHMAP */
+
+enum HashmapType {
+        HASHMAP_TYPE_PLAIN,
+        HASHMAP_TYPE_ORDERED,
+        HASHMAP_TYPE_SET,
+        _HASHMAP_TYPE_MAX
+};
+
+struct _packed_ indirect_storage {
+        char    *storage;                  /* where buckets and DIBs are stored */
+        uint8_t  hash_key[HASH_KEY_SIZE];  /* hash key; changes during resize */
+
+        unsigned n_entries;                /* number of stored entries */
+        unsigned n_buckets;                /* number of buckets */
+
+        unsigned idx_lowest_entry;         /* Index below which all buckets are free.
+                                              Makes "while(hashmap_steal_first())" loops
+                                              O(n) instead of O(n^2) for unordered hashmaps. */
+        uint8_t  _pad[3];                  /* padding for the whole HashmapBase */
+        /* The bitfields in HashmapBase complete the alignment of the whole thing. */
+};
+
+struct direct_storage {
+        /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
+         * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
+         *              or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
+        char storage[sizeof(struct indirect_storage)];
+};
+
+#define DIRECT_BUCKETS(entry_t) \
+        (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t)))
+
+/* We should be able to store at least one entry directly. */
+assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1);
+
+/* We have 3 bits for n_direct_entries. */
+assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
+
+/* Hashmaps with directly stored entries all use this shared hash key.
+ * It's no big deal if the key is guessed, because there can be only
+ * a handful of directly stored entries in a hashmap. When a hashmap
+ * outgrows direct storage, it gets its own key for indirect storage. */
+static uint8_t shared_hash_key[HASH_KEY_SIZE];
+static bool shared_hash_key_initialized;
+
+/* Fields that all hashmap/set types must have */
+struct HashmapBase {
+        const struct hash_ops *hash_ops;  /* hash and compare ops to use */
+
+        union _packed_ {
+                struct indirect_storage indirect; /* if  has_indirect */
+                struct direct_storage direct;     /* if !has_indirect */
+        };
+
+        enum HashmapType type:2;     /* HASHMAP_TYPE_* */
+        bool has_indirect:1;         /* whether indirect storage is used */
+        unsigned n_direct_entries:3; /* Number of entries in direct storage.
+                                      * Only valid if !has_indirect. */
+        bool from_pool:1;            /* whether was allocated from mempool */
+        HASHMAP_DEBUG_FIELDS         /* optional hashmap_debug_info */
+};
+
+/* Specific hash types
+ * HashmapBase must be at the beginning of each hashmap struct. */
+
+struct Hashmap {
+        struct HashmapBase b;
+};
+
+struct OrderedHashmap {
+        struct HashmapBase b;
+        unsigned iterate_list_head, iterate_list_tail;
+};
+
+struct Set {
+        struct HashmapBase b;
+};
+
+DEFINE_MEMPOOL(hashmap_pool,         Hashmap,        8);
+DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
+/* No need for a separate Set pool */
+assert_cc(sizeof(Hashmap) == sizeof(Set));
+
+struct hashmap_type_info {
+        size_t head_size;
+        size_t entry_size;
+        struct mempool *mempool;
+        unsigned n_direct_buckets;
+};
+
+static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
+        [HASHMAP_TYPE_PLAIN] = {
+                .head_size        = sizeof(Hashmap),
+                .entry_size       = sizeof(struct plain_hashmap_entry),
+                .mempool          = &hashmap_pool,
+                .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry),
+        },
+        [HASHMAP_TYPE_ORDERED] = {
+                .head_size        = sizeof(OrderedHashmap),
+                .entry_size       = sizeof(struct ordered_hashmap_entry),
+                .mempool          = &ordered_hashmap_pool,
+                .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry),
+        },
+        [HASHMAP_TYPE_SET] = {
+                .head_size        = sizeof(Set),
+                .entry_size       = sizeof(struct set_entry),
+                .mempool          = &hashmap_pool,
+                .n_direct_buckets = DIRECT_BUCKETS(struct set_entry),
+        },
+};
+
+void string_hash_func(const void *p, struct siphash *state) {
+        siphash24_compress(p, strlen(p) + 1, state);
+}
+
+int string_compare_func(const void *a, const void *b) {
+        return strcmp(a, b);
+}
+
+const struct hash_ops string_hash_ops = {
+        .hash = string_hash_func,
+        .compare = string_compare_func
+};
+
+void trivial_hash_func(const void *p, struct siphash *state) {
+        siphash24_compress(&p, sizeof(p), state);
+}
+
+int trivial_compare_func(const void *a, const void *b) {
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops trivial_hash_ops = {
+        .hash = trivial_hash_func,
+        .compare = trivial_compare_func
+};
+
+void uint64_hash_func(const void *p, struct siphash *state) {
+        siphash24_compress(p, sizeof(uint64_t), state);
+}
+
+int uint64_compare_func(const void *_a, const void *_b) {
+        uint64_t a, b;
+        a = *(const uint64_t*) _a;
+        b = *(const uint64_t*) _b;
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops uint64_hash_ops = {
+        .hash = uint64_hash_func,
+        .compare = uint64_compare_func
+};
+
+#if SIZEOF_DEV_T != 8
+void devt_hash_func(const void *p, struct siphash *state) {
+        siphash24_compress(p, sizeof(dev_t), state);
+}
+
+int devt_compare_func(const void *_a, const void *_b) {
+        dev_t a, b;
+        a = *(const dev_t*) _a;
+        b = *(const dev_t*) _b;
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops devt_hash_ops = {
+        .hash = devt_hash_func,
+        .compare = devt_compare_func
+};
+#endif
+
+static unsigned n_buckets(HashmapBase *h) {
+        return h->has_indirect ? h->indirect.n_buckets
+                               : hashmap_type_info[h->type].n_direct_buckets;
+}
+
+static unsigned n_entries(HashmapBase *h) {
+        return h->has_indirect ? h->indirect.n_entries
+                               : h->n_direct_entries;
+}
+
+static void n_entries_inc(HashmapBase *h) {
+        if (h->has_indirect)
+                h->indirect.n_entries++;
+        else
+                h->n_direct_entries++;
+}
+
+static void n_entries_dec(HashmapBase *h) {
+        if (h->has_indirect)
+                h->indirect.n_entries--;
+        else
+                h->n_direct_entries--;
+}
+
+static char *storage_ptr(HashmapBase *h) {
+        return h->has_indirect ? h->indirect.storage
+                               : h->direct.storage;
+}
+
+static uint8_t *hash_key(HashmapBase *h) {
+        return h->has_indirect ? h->indirect.hash_key
+                               : shared_hash_key;
+}
+
+static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
+        struct siphash state;
+        uint64_t hash;
+
+        siphash24_init(&state, hash_key(h));
+
+        h->hash_ops->hash(p, &state);
+
+        hash = siphash24_finalize(&state);
+
+        return (unsigned) (hash % n_buckets(h));
+}
+#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
+
+static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
+        static uint8_t current[HASH_KEY_SIZE];
+        static bool current_initialized = false;
+
+        /* Returns a hash function key to use. In order to keep things
+         * fast we will not generate a new key each time we allocate a
+         * new hash table. Instead, we'll just reuse the most recently
+         * generated one, except if we never generated one or when we
+         * are rehashing an entire hash table because we reached a
+         * fill level */
+
+        if (!current_initialized || !reuse_is_ok) {
+                random_bytes(current, sizeof(current));
+                current_initialized = true;
+        }
+
+        memcpy(hash_key, current, sizeof(current));
+}
+
+static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
+        return (struct hashmap_base_entry*)
+                (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
+}
+
+static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
+        return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
+        return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct set_entry *set_bucket_at(Set *h, unsigned idx) {
+        return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) {
+        return &swap->e[idx - _IDX_SWAP_BEGIN];
+}
+
+/* Returns a pointer to the bucket at index idx.
+ * Understands real indexes and swap indexes, hence "_virtual". */
+static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
+                                                    unsigned idx) {
+        if (idx < _IDX_SWAP_BEGIN)
+                return bucket_at(h, idx);
+
+        if (idx < _IDX_SWAP_END)
+                return &bucket_at_swap(swap, idx)->p.b;
+
+        assert_not_reached("Invalid index");
+}
+
+static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
+        return (dib_raw_t*)
+                (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
+}
+
+static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
+        return idx >= from ? idx - from
+                           : n_buckets(h) + idx - from;
+}
+
+static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) {
+        unsigned initial_bucket;
+
+        if (raw_dib == DIB_RAW_FREE)
+                return DIB_FREE;
+
+        if (_likely_(raw_dib < DIB_RAW_OVERFLOW))
+                return raw_dib;
+
+        /*
+         * Having an overflow DIB value is very unlikely. The hash function
+         * would have to be bad. For example, in a table of size 2^24 filled
+         * to load factor 0.9 the maximum observed DIB is only about 60.
+         * In theory (assuming I used Maxima correctly), for an infinite size
+         * hash table with load factor 0.8 the probability of a given entry
+         * having DIB > 40 is 1.9e-8.
+         * This returns the correct DIB value by recomputing the hash value in
+         * the unlikely case. XXX Hitting this case could be a hint to rehash.
+         */
+        initial_bucket = bucket_hash(h, bucket_at(h, idx)->key);
+        return bucket_distance(h, idx, initial_bucket);
+}
+
+static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) {
+        dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE;
+}
+
+static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) {
+        dib_raw_t *dibs;
+
+        dibs = dib_raw_ptr(h);
+
+        for ( ; idx < n_buckets(h); idx++)
+                if (dibs[idx] != DIB_RAW_FREE)
+                        return idx;
+
+        return IDX_NIL;
+}
+
+static void bucket_mark_free(HashmapBase *h, unsigned idx) {
+        memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size);
+        bucket_set_dib(h, idx, DIB_FREE);
+}
+
+static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap,
+                              unsigned from, unsigned to) {
+        struct hashmap_base_entry *e_from, *e_to;
+
+        assert(from != to);
+
+        e_from = bucket_at_virtual(h, swap, from);
+        e_to   = bucket_at_virtual(h, swap, to);
+
+        memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size);
+
+        if (h->type == HASHMAP_TYPE_ORDERED) {
+                OrderedHashmap *lh = (OrderedHashmap*) h;
+                struct ordered_hashmap_entry *le, *le_to;
+
+                le_to = (struct ordered_hashmap_entry*) e_to;
+
+                if (le_to->iterate_next != IDX_NIL) {
+                        le = (struct ordered_hashmap_entry*)
+                             bucket_at_virtual(h, swap, le_to->iterate_next);
+                        le->iterate_previous = to;
+                }
+
+                if (le_to->iterate_previous != IDX_NIL) {
+                        le = (struct ordered_hashmap_entry*)
+                             bucket_at_virtual(h, swap, le_to->iterate_previous);
+                        le->iterate_next = to;
+                }
+
+                if (lh->iterate_list_head == from)
+                        lh->iterate_list_head = to;
+                if (lh->iterate_list_tail == from)
+                        lh->iterate_list_tail = to;
+        }
+}
+
+static unsigned next_idx(HashmapBase *h, unsigned idx) {
+        return (idx + 1U) % n_buckets(h);
+}
+
+static unsigned prev_idx(HashmapBase *h, unsigned idx) {
+        return (n_buckets(h) + idx - 1U) % n_buckets(h);
+}
+
+static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
+        switch (h->type) {
+
+        case HASHMAP_TYPE_PLAIN:
+        case HASHMAP_TYPE_ORDERED:
+                return ((struct plain_hashmap_entry*)e)->value;
+
+        case HASHMAP_TYPE_SET:
+                return (void*) e->key;
+
+        default:
+                assert_not_reached("Unknown hashmap type");
+        }
+}
+
+static void base_remove_entry(HashmapBase *h, unsigned idx) {
+        unsigned left, right, prev, dib;
+        dib_raw_t raw_dib, *dibs;
+
+        dibs = dib_raw_ptr(h);
+        assert(dibs[idx] != DIB_RAW_FREE);
+
+#ifdef ENABLE_DEBUG_HASHMAP
+        h->debug.rem_count++;
+        h->debug.last_rem_idx = idx;
+#endif
+
+        left = idx;
+        /* Find the stop bucket ("right"). It is either free or has DIB == 0. */
+        for (right = next_idx(h, left); ; right = next_idx(h, right)) {
+                raw_dib = dibs[right];
+                if (raw_dib == 0 || raw_dib == DIB_RAW_FREE)
+                        break;
+
+                /* The buckets are not supposed to be all occupied and with DIB > 0.
+                 * That would mean we could make everyone better off by shifting them
+                 * backward. This scenario is impossible. */
+                assert(left != right);
+        }
+
+        if (h->type == HASHMAP_TYPE_ORDERED) {
+                OrderedHashmap *lh = (OrderedHashmap*) h;
+                struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx);
+
+                if (le->iterate_next != IDX_NIL)
+                        ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous;
+                else
+                        lh->iterate_list_tail = le->iterate_previous;
+
+                if (le->iterate_previous != IDX_NIL)
+                        ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next;
+                else
+                        lh->iterate_list_head = le->iterate_next;
+        }
+
+        /* Now shift all buckets in the interval (left, right) one step backwards */
+        for (prev = left, left = next_idx(h, left); left != right;
+             prev = left, left = next_idx(h, left)) {
+                dib = bucket_calculate_dib(h, left, dibs[left]);
+                assert(dib != 0);
+                bucket_move_entry(h, NULL, left, prev);
+                bucket_set_dib(h, prev, dib - 1);
+        }
+
+        bucket_mark_free(h, prev);
+        n_entries_dec(h);
+}
+#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
+
+static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) {
+        struct ordered_hashmap_entry *e;
+        unsigned idx;
+
+        assert(h);
+        assert(i);
+
+        if (i->idx == IDX_NIL)
+                goto at_end;
+
+        if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL)
+                goto at_end;
+
+        if (i->idx == IDX_FIRST) {
+                idx = h->iterate_list_head;
+                e = ordered_bucket_at(h, idx);
+        } else {
+                idx = i->idx;
+                e = ordered_bucket_at(h, idx);
+                /*
+                 * We allow removing the current entry while iterating, but removal may cause
+                 * a backward shift. The next entry may thus move one bucket to the left.
+                 * To detect when it happens, we remember the key pointer of the entry we were
+                 * going to iterate next. If it does not match, there was a backward shift.
+                 */
+                if (e->p.b.key != i->next_key) {
+                        idx = prev_idx(HASHMAP_BASE(h), idx);
+                        e = ordered_bucket_at(h, idx);
+                }
+                assert(e->p.b.key == i->next_key);
+        }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+        i->prev_idx = idx;
+#endif
+
+        if (e->iterate_next != IDX_NIL) {
+                struct ordered_hashmap_entry *n;
+                i->idx = e->iterate_next;
+                n = ordered_bucket_at(h, i->idx);
+                i->next_key = n->p.b.key;
+        } else
+                i->idx = IDX_NIL;
+
+        return idx;
+
+at_end:
+        i->idx = IDX_NIL;
+        return IDX_NIL;
+}
+
+static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) {
+        unsigned idx;
+
+        assert(h);
+        assert(i);
+
+        if (i->idx == IDX_NIL)
+                goto at_end;
+
+        if (i->idx == IDX_FIRST) {
+                /* fast forward to the first occupied bucket */
+                if (h->has_indirect) {
+                        i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry);
+                        h->indirect.idx_lowest_entry = i->idx;
+                } else
+                        i->idx = skip_free_buckets(h, 0);
+
+                if (i->idx == IDX_NIL)
+                        goto at_end;
+        } else {
+                struct hashmap_base_entry *e;
+
+                assert(i->idx > 0);
+
+                e = bucket_at(h, i->idx);
+                /*
+                 * We allow removing the current entry while iterating, but removal may cause
+                 * a backward shift. The next entry may thus move one bucket to the left.
+                 * To detect when it happens, we remember the key pointer of the entry we were
+                 * going to iterate next. If it does not match, there was a backward shift.
+                 */
+                if (e->key != i->next_key)
+                        e = bucket_at(h, --i->idx);
+
+                assert(e->key == i->next_key);
+        }
+
+        idx = i->idx;
+#ifdef ENABLE_DEBUG_HASHMAP
+        i->prev_idx = idx;
+#endif
+
+        i->idx = skip_free_buckets(h, i->idx + 1);
+        if (i->idx != IDX_NIL)
+                i->next_key = bucket_at(h, i->idx)->key;
+        else
+                i->idx = IDX_NIL;
+
+        return idx;
+
+at_end:
+        i->idx = IDX_NIL;
+        return IDX_NIL;
+}
+
+static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
+        if (!h) {
+                i->idx = IDX_NIL;
+                return IDX_NIL;
+        }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+        if (i->idx == IDX_FIRST) {
+                i->put_count = h->debug.put_count;
+                i->rem_count = h->debug.rem_count;
+        } else {
+                /* While iterating, must not add any new entries */
+                assert(i->put_count == h->debug.put_count);
+                /* ... or remove entries other than the current one */
+                assert(i->rem_count == h->debug.rem_count ||
+                       (i->rem_count == h->debug.rem_count - 1 &&
+                        i->prev_idx == h->debug.last_rem_idx));
+                /* Reset our removals counter */
+                i->rem_count = h->debug.rem_count;
+        }
+#endif
+
+        return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i)
+                                               : hashmap_iterate_in_internal_order(h, i);
+}
+
+bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) {
+        struct hashmap_base_entry *e;
+        void *data;
+        unsigned idx;
+
+        idx = hashmap_iterate_entry(h, i);
+        if (idx == IDX_NIL) {
+                if (value)
+                        *value = NULL;
+                if (key)
+                        *key = NULL;
+
+                return false;
+        }
+
+        e = bucket_at(h, idx);
+        data = entry_value(h, e);
+        if (value)
+                *value = data;
+        if (key)
+                *key = e->key;
+
+        return true;
+}
+
+bool set_iterate(Set *s, Iterator *i, void **value) {
+        return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL);
+}
+
+#define HASHMAP_FOREACH_IDX(idx, h, i) \
+        for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
+             (idx != IDX_NIL); \
+             (idx) = hashmap_iterate_entry((h), &(i)))
+
+static void reset_direct_storage(HashmapBase *h) {
+        const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
+        void *p;
+
+        assert(!h->has_indirect);
+
+        p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets);
+        memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
+}
+
+static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+        HashmapBase *h;
+        const struct hashmap_type_info *hi = &hashmap_type_info[type];
+        bool use_pool;
+
+        use_pool = is_main_thread();
+
+        h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
+
+        if (!h)
+                return NULL;
+
+        h->type = type;
+        h->from_pool = use_pool;
+        h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
+
+        if (type == HASHMAP_TYPE_ORDERED) {
+                OrderedHashmap *lh = (OrderedHashmap*)h;
+                lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
+        }
+
+        reset_direct_storage(h);
+
+        if (!shared_hash_key_initialized) {
+                random_bytes(shared_hash_key, sizeof(shared_hash_key));
+                shared_hash_key_initialized= true;
+        }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+        h->debug.func = func;
+        h->debug.file = file;
+        h->debug.line = line;
+        assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
+        LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug);
+        assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
+#endif
+
+        return h;
+}
+
+Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return (Hashmap*)        hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+}
+
+OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+}
+
+Set *internal_set_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return (Set*)            hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+}
+
+static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
+                                         enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+        HashmapBase *q;
+
+        assert(h);
+
+        if (*h)
+                return 0;
+
+        q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
+        if (!q)
+                return -ENOMEM;
+
+        *h = q;
+        return 0;
+}
+
+int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+}
+
+static void hashmap_free_no_clear(HashmapBase *h) {
+        assert(!h->has_indirect);
+        assert(!h->n_direct_entries);
+
+#ifdef ENABLE_DEBUG_HASHMAP
+        assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
+        LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug);
+        assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
+#endif
+
+        if (h->from_pool)
+                mempool_free_tile(hashmap_type_info[h->type].mempool, h);
+        else
+                free(h);
+}
+
+HashmapBase *internal_hashmap_free(HashmapBase *h) {
+
+        /* Free the hashmap, but nothing in it */
+
+        if (h) {
+                internal_hashmap_clear(h);
+                hashmap_free_no_clear(h);
+        }
+
+        return NULL;
+}
+
+HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
+
+        /* Free the hashmap and all data objects in it, but not the
+         * keys */
+
+        if (h) {
+                internal_hashmap_clear_free(h);
+                hashmap_free_no_clear(h);
+        }
+
+        return NULL;
+}
+
+Hashmap *hashmap_free_free_free(Hashmap *h) {
+
+        /* Free the hashmap and all data and key objects in it */
+
+        if (h) {
+                hashmap_clear_free_free(h);
+                hashmap_free_no_clear(HASHMAP_BASE(h));
+        }
+
+        return NULL;
+}
+
+void internal_hashmap_clear(HashmapBase *h) {
+        if (!h)
+                return;
+
+        if (h->has_indirect) {
+                free(h->indirect.storage);
+                h->has_indirect = false;
+        }
+
+        h->n_direct_entries = 0;
+        reset_direct_storage(h);
+
+        if (h->type == HASHMAP_TYPE_ORDERED) {
+                OrderedHashmap *lh = (OrderedHashmap*) h;
+                lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
+        }
+}
+
+void internal_hashmap_clear_free(HashmapBase *h) {
+        unsigned idx;
+
+        if (!h)
+                return;
+
+        for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
+             idx = skip_free_buckets(h, idx + 1))
+                free(entry_value(h, bucket_at(h, idx)));
+
+        internal_hashmap_clear(h);
+}
+
+void hashmap_clear_free_free(Hashmap *h) {
+        unsigned idx;
+
+        if (!h)
+                return;
+
+        for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
+             idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
+                struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
+                free((void*)e->b.key);
+                free(e->value);
+        }
+
+        internal_hashmap_clear(HASHMAP_BASE(h));
+}
+
+static int resize_buckets(HashmapBase *h, unsigned entries_add);
+
+/*
+ * Finds an empty bucket to put an entry into, starting the scan at 'idx'.
+ * Performs Robin Hood swaps as it goes. The entry to put must be placed
+ * by the caller into swap slot IDX_PUT.
+ * If used for in-place resizing, may leave a displaced entry in swap slot
+ * IDX_PUT. Caller must rehash it next.
+ * Returns: true if it left a displaced entry to rehash next in IDX_PUT,
+ *          false otherwise.
+ */
+static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
+                                   struct swap_entries *swap) {
+        dib_raw_t raw_dib, *dibs;
+        unsigned dib, distance;
+
+#ifdef ENABLE_DEBUG_HASHMAP
+        h->debug.put_count++;
+#endif
+
+        dibs = dib_raw_ptr(h);
+
+        for (distance = 0; ; distance++) {
+                raw_dib = dibs[idx];
+                if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) {
+                        if (raw_dib == DIB_RAW_REHASH)
+                                bucket_move_entry(h, swap, idx, IDX_TMP);
+
+                        if (h->has_indirect && h->indirect.idx_lowest_entry > idx)
+                                h->indirect.idx_lowest_entry = idx;
+
+                        bucket_set_dib(h, idx, distance);
+                        bucket_move_entry(h, swap, IDX_PUT, idx);
+                        if (raw_dib == DIB_RAW_REHASH) {
+                                bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
+                                return true;
+                        }
+
+                        return false;
+                }
+
+                dib = bucket_calculate_dib(h, idx, raw_dib);
+
+                if (dib < distance) {
+                        /* Found a wealthier entry. Go Robin Hood! */
+                        bucket_set_dib(h, idx, distance);
+
+                        /* swap the entries */
+                        bucket_move_entry(h, swap, idx, IDX_TMP);
+                        bucket_move_entry(h, swap, IDX_PUT, idx);
+                        bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
+
+                        distance = dib;
+                }
+
+                idx = next_idx(h, idx);
+        }
+}
+
+/*
+ * Puts an entry into a hashmap, boldly - no check whether key already exists.
+ * The caller must place the entry (only its key and value, not link indexes)
+ * in swap slot IDX_PUT.
+ * Caller must ensure: the key does not exist yet in the hashmap.
+ *                     that resize is not needed if !may_resize.
+ * Returns: 1 if entry was put successfully.
+ *          -ENOMEM if may_resize==true and resize failed with -ENOMEM.
+ *          Cannot return -ENOMEM if !may_resize.
+ */
+static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
+                                   struct swap_entries *swap, bool may_resize) {
+        struct ordered_hashmap_entry *new_entry;
+        int r;
+
+        assert(idx < n_buckets(h));
+
+        new_entry = bucket_at_swap(swap, IDX_PUT);
+
+        if (may_resize) {
+                r = resize_buckets(h, 1);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        idx = bucket_hash(h, new_entry->p.b.key);
+        }
+        assert(n_entries(h) < n_buckets(h));
+
+        if (h->type == HASHMAP_TYPE_ORDERED) {
+                OrderedHashmap *lh = (OrderedHashmap*) h;
+
+                new_entry->iterate_next = IDX_NIL;
+                new_entry->iterate_previous = lh->iterate_list_tail;
+
+                if (lh->iterate_list_tail != IDX_NIL) {
+                        struct ordered_hashmap_entry *old_tail;
+
+                        old_tail = ordered_bucket_at(lh, lh->iterate_list_tail);
+                        assert(old_tail->iterate_next == IDX_NIL);
+                        old_tail->iterate_next = IDX_PUT;
+                }
+
+                lh->iterate_list_tail = IDX_PUT;
+                if (lh->iterate_list_head == IDX_NIL)
+                        lh->iterate_list_head = IDX_PUT;
+        }
+
+        assert_se(hashmap_put_robin_hood(h, idx, swap) == false);
+
+        n_entries_inc(h);
+#ifdef ENABLE_DEBUG_HASHMAP
+        h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
+#endif
+
+        return 1;
+}
+#define hashmap_put_boldly(h, idx, swap, may_resize) \
+        hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize)
+
+/*
+ * Returns 0 if resize is not needed.
+ *         1 if successfully resized.
+ *         -ENOMEM on allocation failure.
+ */
+static int resize_buckets(HashmapBase *h, unsigned entries_add) {
+        struct swap_entries swap;
+        char *new_storage;
+        dib_raw_t *old_dibs, *new_dibs;
+        const struct hashmap_type_info *hi;
+        unsigned idx, optimal_idx;
+        unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries;
+        uint8_t new_shift;
+        bool rehash_next;
+
+        assert(h);
+
+        hi = &hashmap_type_info[h->type];
+        new_n_entries = n_entries(h) + entries_add;
+
+        /* overflow? */
+        if (_unlikely_(new_n_entries < entries_add))
+                return -ENOMEM;
+
+        /* For direct storage we allow 100% load, because it's tiny. */
+        if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets)
+                return 0;
+
+        /*
+         * Load factor = n/m = 1 - (1/INV_KEEP_FREE).
+         * From it follows: m = n + n/(INV_KEEP_FREE - 1)
+         */
+        new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1);
+        /* overflow? */
+        if (_unlikely_(new_n_buckets < new_n_entries))
+                return -ENOMEM;
+
+        if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t))))
+                return -ENOMEM;
+
+        old_n_buckets = n_buckets(h);
+
+        if (_likely_(new_n_buckets <= old_n_buckets))
+                return 0;
+
+        new_shift = log2u_round_up(MAX(
+                        new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)),
+                        2 * sizeof(struct direct_storage)));
+
+        /* Realloc storage (buckets and DIB array). */
+        new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL,
+                              1U << new_shift);
+        if (!new_storage)
+                return -ENOMEM;
+
+        /* Must upgrade direct to indirect storage. */
+        if (!h->has_indirect) {
+                memcpy(new_storage, h->direct.storage,
+                       old_n_buckets * (hi->entry_size + sizeof(dib_raw_t)));
+                h->indirect.n_entries = h->n_direct_entries;
+                h->indirect.idx_lowest_entry = 0;
+                h->n_direct_entries = 0;
+        }
+
+        /* Get a new hash key. If we've just upgraded to indirect storage,
+         * allow reusing a previously generated key. It's still a different key
+         * from the shared one that we used for direct storage. */
+        get_hash_key(h->indirect.hash_key, !h->has_indirect);
+
+        h->has_indirect = true;
+        h->indirect.storage = new_storage;
+        h->indirect.n_buckets = (1U << new_shift) /
+                                (hi->entry_size + sizeof(dib_raw_t));
+
+        old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets);
+        new_dibs = dib_raw_ptr(h);
+
+        /*
+         * Move the DIB array to the new place, replacing valid DIB values with
+         * DIB_RAW_REHASH to indicate all of the used buckets need rehashing.
+         * Note: Overlap is not possible, because we have at least doubled the
+         * number of buckets and dib_raw_t is smaller than any entry type.
+         */
+        for (idx = 0; idx < old_n_buckets; idx++) {
+                assert(old_dibs[idx] != DIB_RAW_REHASH);
+                new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE
+                                                              : DIB_RAW_REHASH;
+        }
+
+        /* Zero the area of newly added entries (including the old DIB area) */
+        memzero(bucket_at(h, old_n_buckets),
+               (n_buckets(h) - old_n_buckets) * hi->entry_size);
+
+        /* The upper half of the new DIB array needs initialization */
+        memset(&new_dibs[old_n_buckets], DIB_RAW_INIT,
+               (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t));
+
+        /* Rehash entries that need it */
+        n_rehashed = 0;
+        for (idx = 0; idx < old_n_buckets; idx++) {
+                if (new_dibs[idx] != DIB_RAW_REHASH)
+                        continue;
+
+                optimal_idx = bucket_hash(h, bucket_at(h, idx)->key);
+
+                /*
+                 * Not much to do if by luck the entry hashes to its current
+                 * location. Just set its DIB.
+                 */
+                if (optimal_idx == idx) {
+                        new_dibs[idx] = 0;
+                        n_rehashed++;
+                        continue;
+                }
+
+                new_dibs[idx] = DIB_RAW_FREE;
+                bucket_move_entry(h, &swap, idx, IDX_PUT);
+                /* bucket_move_entry does not clear the source */
+                memzero(bucket_at(h, idx), hi->entry_size);
+
+                do {
+                        /*
+                         * Find the new bucket for the current entry. This may make
+                         * another entry homeless and load it into IDX_PUT.
+                         */
+                        rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap);
+                        n_rehashed++;
+
+                        /* Did the current entry displace another one? */
+                        if (rehash_next)
+                                optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key);
+                } while (rehash_next);
+        }
+
+        assert(n_rehashed == n_entries(h));
+
+        return 1;
+}
+
+/*
+ * Finds an entry with a matching key
+ * Returns: index of the found entry, or IDX_NIL if not found.
+ */
+static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) {
+        struct hashmap_base_entry *e;
+        unsigned dib, distance;
+        dib_raw_t *dibs = dib_raw_ptr(h);
+
+        assert(idx < n_buckets(h));
+
+        for (distance = 0; ; distance++) {
+                if (dibs[idx] == DIB_RAW_FREE)
+                        return IDX_NIL;
+
+                dib = bucket_calculate_dib(h, idx, dibs[idx]);
+
+                if (dib < distance)
+                        return IDX_NIL;
+                if (dib == distance) {
+                        e = bucket_at(h, idx);
+                        if (h->hash_ops->compare(e->key, key) == 0)
+                                return idx;
+                }
+
+                idx = next_idx(h, idx);
+        }
+}
+#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key)
+
+int hashmap_put(Hashmap *h, const void *key, void *value) {
+        struct swap_entries swap;
+        struct plain_hashmap_entry *e;
+        unsigned hash, idx;
+
+        assert(h);
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx != IDX_NIL) {
+                e = plain_bucket_at(h, idx);
+                if (e->value == value)
+                        return 0;
+                return -EEXIST;
+        }
+
+        e = &bucket_at_swap(&swap, IDX_PUT)->p;
+        e->b.key = key;
+        e->value = value;
+        return hashmap_put_boldly(h, hash, &swap, true);
+}
+
+int set_put(Set *s, const void *key) {
+        struct swap_entries swap;
+        struct hashmap_base_entry *e;
+        unsigned hash, idx;
+
+        assert(s);
+
+        hash = bucket_hash(s, key);
+        idx = bucket_scan(s, hash, key);
+        if (idx != IDX_NIL)
+                return 0;
+
+        e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+        e->key = key;
+        return hashmap_put_boldly(s, hash, &swap, true);
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value) {
+        struct swap_entries swap;
+        struct plain_hashmap_entry *e;
+        unsigned hash, idx;
+
+        assert(h);
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx != IDX_NIL) {
+                e = plain_bucket_at(h, idx);
+#ifdef ENABLE_DEBUG_HASHMAP
+                /* Although the key is equal, the key pointer may have changed,
+                 * and this would break our assumption for iterating. So count
+                 * this operation as incompatible with iteration. */
+                if (e->b.key != key) {
+                        h->b.debug.put_count++;
+                        h->b.debug.rem_count++;
+                        h->b.debug.last_rem_idx = idx;
+                }
+#endif
+                e->b.key = key;
+                e->value = value;
+                return 0;
+        }
+
+        e = &bucket_at_swap(&swap, IDX_PUT)->p;
+        e->b.key = key;
+        e->value = value;
+        return hashmap_put_boldly(h, hash, &swap, true);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value) {
+        struct plain_hashmap_entry *e;
+        unsigned hash, idx;
+
+        assert(h);
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx == IDX_NIL)
+                return -ENOENT;
+
+        e = plain_bucket_at(h, idx);
+        e->value = value;
+        return 0;
+}
+
+void *internal_hashmap_get(HashmapBase *h, const void *key) {
+        struct hashmap_base_entry *e;
+        unsigned hash, idx;
+
+        if (!h)
+                return NULL;
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        e = bucket_at(h, idx);
+        return entry_value(h, e);
+}
+
+void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
+        struct plain_hashmap_entry *e;
+        unsigned hash, idx;
+
+        if (!h)
+                return NULL;
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        e = plain_bucket_at(h, idx);
+        if (key2)
+                *key2 = (void*) e->b.key;
+
+        return e->value;
+}
+
+bool internal_hashmap_contains(HashmapBase *h, const void *key) {
+        unsigned hash;
+
+        if (!h)
+                return false;
+
+        hash = bucket_hash(h, key);
+        return bucket_scan(h, hash, key) != IDX_NIL;
+}
+
+void *internal_hashmap_remove(HashmapBase *h, const void *key) {
+        struct hashmap_base_entry *e;
+        unsigned hash, idx;
+        void *data;
+
+        if (!h)
+                return NULL;
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        e = bucket_at(h, idx);
+        data = entry_value(h, e);
+        remove_entry(h, idx);
+
+        return data;
+}
+
+void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
+        struct plain_hashmap_entry *e;
+        unsigned hash, idx;
+        void *data;
+
+        if (!h) {
+                if (rkey)
+                        *rkey = NULL;
+                return NULL;
+        }
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx == IDX_NIL) {
+                if (rkey)
+                        *rkey = NULL;
+                return NULL;
+        }
+
+        e = plain_bucket_at(h, idx);
+        data = e->value;
+        if (rkey)
+                *rkey = (void*) e->b.key;
+
+        remove_entry(h, idx);
+
+        return data;
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+        struct swap_entries swap;
+        struct plain_hashmap_entry *e;
+        unsigned old_hash, new_hash, idx;
+
+        if (!h)
+                return -ENOENT;
+
+        old_hash = bucket_hash(h, old_key);
+        idx = bucket_scan(h, old_hash, old_key);
+        if (idx == IDX_NIL)
+                return -ENOENT;
+
+        new_hash = bucket_hash(h, new_key);
+        if (bucket_scan(h, new_hash, new_key) != IDX_NIL)
+                return -EEXIST;
+
+        remove_entry(h, idx);
+
+        e = &bucket_at_swap(&swap, IDX_PUT)->p;
+        e->b.key = new_key;
+        e->value = value;
+        assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
+
+        return 0;
+}
+
+int set_remove_and_put(Set *s, const void *old_key, const void *new_key) {
+        struct swap_entries swap;
+        struct hashmap_base_entry *e;
+        unsigned old_hash, new_hash, idx;
+
+        if (!s)
+                return -ENOENT;
+
+        old_hash = bucket_hash(s, old_key);
+        idx = bucket_scan(s, old_hash, old_key);
+        if (idx == IDX_NIL)
+                return -ENOENT;
+
+        new_hash = bucket_hash(s, new_key);
+        if (bucket_scan(s, new_hash, new_key) != IDX_NIL)
+                return -EEXIST;
+
+        remove_entry(s, idx);
+
+        e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+        e->key = new_key;
+        assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1);
+
+        return 0;
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+        struct swap_entries swap;
+        struct plain_hashmap_entry *e;
+        unsigned old_hash, new_hash, idx_old, idx_new;
+
+        if (!h)
+                return -ENOENT;
+
+        old_hash = bucket_hash(h, old_key);
+        idx_old = bucket_scan(h, old_hash, old_key);
+        if (idx_old == IDX_NIL)
+                return -ENOENT;
+
+        old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key;
+
+        new_hash = bucket_hash(h, new_key);
+        idx_new = bucket_scan(h, new_hash, new_key);
+        if (idx_new != IDX_NIL)
+                if (idx_old != idx_new) {
+                        remove_entry(h, idx_new);
+                        /* Compensate for a possible backward shift. */
+                        if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key)
+                                idx_old = prev_idx(HASHMAP_BASE(h), idx_old);
+                        assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key);
+                }
+
+        remove_entry(h, idx_old);
+
+        e = &bucket_at_swap(&swap, IDX_PUT)->p;
+        e->b.key = new_key;
+        e->value = value;
+        assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
+
+        return 0;
+}
+
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
+        struct plain_hashmap_entry *e;
+        unsigned hash, idx;
+
+        if (!h)
+                return NULL;
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        e = plain_bucket_at(h, idx);
+        if (e->value != value)
+                return NULL;
+
+        remove_entry(h, idx);
+
+        return value;
+}
+
+static unsigned find_first_entry(HashmapBase *h) {
+        Iterator i = ITERATOR_FIRST;
+
+        if (!h || !n_entries(h))
+                return IDX_NIL;
+
+        return hashmap_iterate_entry(h, &i);
+}
+
+void *internal_hashmap_first(HashmapBase *h) {
+        unsigned idx;
+
+        idx = find_first_entry(h);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        return entry_value(h, bucket_at(h, idx));
+}
+
+void *internal_hashmap_first_key(HashmapBase *h) {
+        struct hashmap_base_entry *e;
+        unsigned idx;
+
+        idx = find_first_entry(h);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        e = bucket_at(h, idx);
+        return (void*) e->key;
+}
+
+void *internal_hashmap_steal_first(HashmapBase *h) {
+        struct hashmap_base_entry *e;
+        void *data;
+        unsigned idx;
+
+        idx = find_first_entry(h);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        e = bucket_at(h, idx);
+        data = entry_value(h, e);
+        remove_entry(h, idx);
+
+        return data;
+}
+
+void *internal_hashmap_steal_first_key(HashmapBase *h) {
+        struct hashmap_base_entry *e;
+        void *key;
+        unsigned idx;
+
+        idx = find_first_entry(h);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        e = bucket_at(h, idx);
+        key = (void*) e->key;
+        remove_entry(h, idx);
+
+        return key;
+}
+
+unsigned internal_hashmap_size(HashmapBase *h) {
+
+        if (!h)
+                return 0;
+
+        return n_entries(h);
+}
+
+unsigned internal_hashmap_buckets(HashmapBase *h) {
+
+        if (!h)
+                return 0;
+
+        return n_buckets(h);
+}
+
+int internal_hashmap_merge(Hashmap *h, Hashmap *other) {
+        Iterator i;
+        unsigned idx;
+
+        assert(h);
+
+        HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
+                struct plain_hashmap_entry *pe = plain_bucket_at(other, idx);
+                int r;
+
+                r = hashmap_put(h, pe->b.key, pe->value);
+                if (r < 0 && r != -EEXIST)
+                        return r;
+        }
+
+        return 0;
+}
+
+int set_merge(Set *s, Set *other) {
+        Iterator i;
+        unsigned idx;
+
+        assert(s);
+
+        HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
+                struct set_entry *se = set_bucket_at(other, idx);
+                int r;
+
+                r = set_put(s, se->b.key);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
+        int r;
+
+        assert(h);
+
+        r = resize_buckets(h, entries_add);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+/*
+ * The same as hashmap_merge(), but every new item from other is moved to h.
+ * Keys already in h are skipped and stay in other.
+ * Returns: 0 on success.
+ *          -ENOMEM on alloc failure, in which case no move has been done.
+ */
+int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
+        struct swap_entries swap;
+        struct hashmap_base_entry *e, *n;
+        Iterator i;
+        unsigned idx;
+        int r;
+
+        assert(h);
+
+        if (!other)
+                return 0;
+
+        assert(other->type == h->type);
+
+        /*
+         * This reserves buckets for the worst case, where none of other's
+         * entries are yet present in h. This is preferable to risking
+         * an allocation failure in the middle of the moving and having to
+         * rollback or return a partial result.
+         */
+        r = resize_buckets(h, n_entries(other));
+        if (r < 0)
+                return r;
+
+        HASHMAP_FOREACH_IDX(idx, other, i) {
+                unsigned h_hash;
+
+                e = bucket_at(other, idx);
+                h_hash = bucket_hash(h, e->key);
+                if (bucket_scan(h, h_hash, e->key) != IDX_NIL)
+                        continue;
+
+                n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+                n->key = e->key;
+                if (h->type != HASHMAP_TYPE_SET)
+                        ((struct plain_hashmap_entry*) n)->value =
+                                ((struct plain_hashmap_entry*) e)->value;
+                assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1);
+
+                remove_entry(other, idx);
+        }
+
+        return 0;
+}
+
+int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
+        struct swap_entries swap;
+        unsigned h_hash, other_hash, idx;
+        struct hashmap_base_entry *e, *n;
+        int r;
+
+        assert(h);
+
+        h_hash = bucket_hash(h, key);
+        if (bucket_scan(h, h_hash, key) != IDX_NIL)
+                return -EEXIST;
+
+        if (!other)
+                return -ENOENT;
+
+        assert(other->type == h->type);
+
+        other_hash = bucket_hash(other, key);
+        idx = bucket_scan(other, other_hash, key);
+        if (idx == IDX_NIL)
+                return -ENOENT;
+
+        e = bucket_at(other, idx);
+
+        n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+        n->key = e->key;
+        if (h->type != HASHMAP_TYPE_SET)
+                ((struct plain_hashmap_entry*) n)->value =
+                        ((struct plain_hashmap_entry*) e)->value;
+        r = hashmap_put_boldly(h, h_hash, &swap, true);
+        if (r < 0)
+                return r;
+
+        remove_entry(other, idx);
+        return 0;
+}
+
+HashmapBase *internal_hashmap_copy(HashmapBase *h) {
+        HashmapBase *copy;
+        int r;
+
+        assert(h);
+
+        copy = hashmap_base_new(h->hash_ops, h->type  HASHMAP_DEBUG_SRC_ARGS);
+        if (!copy)
+                return NULL;
+
+        switch (h->type) {
+        case HASHMAP_TYPE_PLAIN:
+        case HASHMAP_TYPE_ORDERED:
+                r = hashmap_merge((Hashmap*)copy, (Hashmap*)h);
+                break;
+        case HASHMAP_TYPE_SET:
+                r = set_merge((Set*)copy, (Set*)h);
+                break;
+        default:
+                assert_not_reached("Unknown hashmap type");
+        }
+
+        if (r < 0) {
+                internal_hashmap_free(copy);
+                return NULL;
+        }
+
+        return copy;
+}
+
+char **internal_hashmap_get_strv(HashmapBase *h) {
+        char **sv;
+        Iterator i;
+        unsigned idx, n;
+
+        sv = new(char*, n_entries(h)+1);
+        if (!sv)
+                return NULL;
+
+        n = 0;
+        HASHMAP_FOREACH_IDX(idx, h, i)
+                sv[n++] = entry_value(h, bucket_at(h, idx));
+        sv[n] = NULL;
+
+        return sv;
+}
+
+void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
+        struct ordered_hashmap_entry *e;
+        unsigned hash, idx;
+
+        if (!h)
+                return NULL;
+
+        hash = bucket_hash(h, key);
+        idx = bucket_scan(h, hash, key);
+        if (idx == IDX_NIL)
+                return NULL;
+
+        e = ordered_bucket_at(h, idx);
+        if (e->iterate_next == IDX_NIL)
+                return NULL;
+        return ordered_bucket_at(h, e->iterate_next)->p.value;
+}
+
+int set_consume(Set *s, void *value) {
+        int r;
+
+        r = set_put(s, value);
+        if (r <= 0)
+                free(value);
+
+        return r;
+}
+
+int set_put_strdup(Set *s, const char *p) {
+        char *c;
+        int r;
+
+        assert(s);
+        assert(p);
+
+        c = strdup(p);
+        if (!c)
+                return -ENOMEM;
+
+        r = set_consume(s, c);
+        if (r == -EEXIST)
+                return 0;
+
+        return r;
+}
+
+int set_put_strdupv(Set *s, char **l) {
+        int n = 0, r;
+        char **i;
+
+        STRV_FOREACH(i, l) {
+                r = set_put_strdup(s, *i);
+                if (r < 0)
+                        return r;
+
+                n += r;
+        }
+
+        return n;
+}
diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h
new file mode 100644
index 00000000..0485e1d4
--- /dev/null
+++ b/src/systemd/src/basic/hashmap.h
@@ -0,0 +1,417 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2014 Michal Schmidt
+
+  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 <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "macro.h"
+#include "siphash24.h"
+#include "util.h"
+
+/*
+ * A hash table implementation. As a minor optimization a NULL hashmap object
+ * will be treated as empty hashmap for all read operations. That way it is not
+ * necessary to instantiate an object for each Hashmap use.
+ *
+ * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
+ * the implemention will:
+ * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
+ * - perform extra checks for invalid use of iterators
+ */
+
+#define HASH_KEY_SIZE 16
+
+/* The base type for all hashmap and set types. Many functions in the
+ * implementation take (HashmapBase*) parameters and are run-time polymorphic,
+ * though the API is not meant to be polymorphic (do not call functions
+ * internal_*() directly). */
+typedef struct HashmapBase HashmapBase;
+
+/* Specific hashmap/set types */
+typedef struct Hashmap Hashmap;               /* Maps keys to values */
+typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
+typedef struct Set Set;                       /* Stores just keys */
+
+/* Ideally the Iterator would be an opaque struct, but it is instantiated
+ * by hashmap users, so the definition has to be here. Do not use its fields
+ * directly. */
+typedef struct {
+        unsigned idx;         /* index of an entry to be iterated next */
+        const void *next_key; /* expected value of that entry's key pointer */
+#ifdef ENABLE_DEBUG_HASHMAP
+        unsigned put_count;   /* hashmap's put_count recorded at start of iteration */
+        unsigned rem_count;   /* hashmap's rem_count in previous iteration */
+        unsigned prev_idx;    /* idx in previous iteration */
+#endif
+} Iterator;
+
+#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
+#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
+
+typedef void (*hash_func_t)(const void *p, struct siphash *state);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+struct hash_ops {
+        hash_func_t hash;
+        compare_func_t compare;
+};
+
+void string_hash_func(const void *p, struct siphash *state);
+int string_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops string_hash_ops;
+
+/* This will compare the passed pointers directly, and will not
+ * dereference them. This is hence not useful for strings or
+ * suchlike. */
+void trivial_hash_func(const void *p, struct siphash *state);
+int trivial_compare_func(const void *a, const void *b) _const_;
+extern const struct hash_ops trivial_hash_ops;
+
+/* 32bit values we can always just embedd in the pointer itself, but
+ * in order to support 32bit archs we need store 64bit values
+ * indirectly, since they don't fit in a pointer. */
+void uint64_hash_func(const void *p, struct siphash *state);
+int uint64_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops uint64_hash_ops;
+
+/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
+ * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
+#if SIZEOF_DEV_T != 8
+void devt_hash_func(const void *p, struct siphash *state) _pure_;
+int devt_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops devt_hash_ops = {
+        .hash = devt_hash_func,
+        .compare = devt_compare_func
+};
+#else
+#define devt_hash_func uint64_hash_func
+#define devt_compare_func uint64_compare_func
+#define devt_hash_ops uint64_hash_ops
+#endif
+
+/* Macros for type checking */
+#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
+        (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
+         __builtin_types_compatible_p(typeof(h), Hashmap*) || \
+         __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \
+         __builtin_types_compatible_p(typeof(h), Set*))
+
+#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \
+        (__builtin_types_compatible_p(typeof(h), Hashmap*) || \
+         __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \
+
+#define HASHMAP_BASE(h) \
+        __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \
+                (HashmapBase*)(h), \
+                (void)0)
+
+#define PLAIN_HASHMAP(h) \
+        __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \
+                (Hashmap*)(h), \
+                (void)0)
+
+#ifdef ENABLE_DEBUG_HASHMAP
+# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
+# define HASHMAP_DEBUG_SRC_ARGS   , __func__, __FILE__, __LINE__
+# define HASHMAP_DEBUG_PASS_ARGS   , func, file, line
+#else
+# define HASHMAP_DEBUG_PARAMS
+# define HASHMAP_DEBUG_SRC_ARGS
+# define HASHMAP_DEBUG_PASS_ARGS
+#endif
+
+Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+#define hashmap_new(ops) internal_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
+
+HashmapBase *internal_hashmap_free(HashmapBase *h);
+static inline Hashmap *hashmap_free(Hashmap *h) {
+        return (void*)internal_hashmap_free(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
+        return (void*)internal_hashmap_free(HASHMAP_BASE(h));
+}
+
+HashmapBase *internal_hashmap_free_free(HashmapBase *h);
+static inline Hashmap *hashmap_free_free(Hashmap *h) {
+        return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
+        return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
+}
+
+Hashmap *hashmap_free_free_free(Hashmap *h);
+static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
+        return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
+}
+
+HashmapBase *internal_hashmap_copy(HashmapBase *h);
+static inline Hashmap *hashmap_copy(Hashmap *h) {
+        return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
+        return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+}
+
+int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
+
+int hashmap_put(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
+        return hashmap_put(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
+        return hashmap_update(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) {
+        return hashmap_replace(PLAIN_HASHMAP(h), key, value);
+}
+
+void *internal_hashmap_get(HashmapBase *h, const void *key);
+static inline void *hashmap_get(Hashmap *h, const void *key) {
+        return internal_hashmap_get(HASHMAP_BASE(h), key);
+}
+static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
+        return internal_hashmap_get(HASHMAP_BASE(h), key);
+}
+
+void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
+static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
+        return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
+}
+
+bool internal_hashmap_contains(HashmapBase *h, const void *key);
+static inline bool hashmap_contains(Hashmap *h, const void *key) {
+        return internal_hashmap_contains(HASHMAP_BASE(h), key);
+}
+static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
+        return internal_hashmap_contains(HASHMAP_BASE(h), key);
+}
+
+void *internal_hashmap_remove(HashmapBase *h, const void *key);
+static inline void *hashmap_remove(Hashmap *h, const void *key) {
+        return internal_hashmap_remove(HASHMAP_BASE(h), key);
+}
+static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
+        return internal_hashmap_remove(HASHMAP_BASE(h), key);
+}
+
+void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
+static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
+        return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
+}
+
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
+static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
+        return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
+static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
+        return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value);
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
+static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
+        return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
+}
+
+/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
+ * should just work, allow this by having looser type-checking here. */
+int internal_hashmap_merge(Hashmap *h, Hashmap *other);
+#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
+#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
+
+int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
+static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
+        return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
+        return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+
+int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
+/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
+static inline int hashmap_move(Hashmap *h, Hashmap *other) {
+        return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+}
+static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
+        return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+}
+
+int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
+static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
+        return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+}
+static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
+        return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+}
+
+unsigned internal_hashmap_size(HashmapBase *h) _pure_;
+static inline unsigned hashmap_size(Hashmap *h) {
+        return internal_hashmap_size(HASHMAP_BASE(h));
+}
+static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
+        return internal_hashmap_size(HASHMAP_BASE(h));
+}
+
+static inline bool hashmap_isempty(Hashmap *h) {
+        return hashmap_size(h) == 0;
+}
+static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
+        return ordered_hashmap_size(h) == 0;
+}
+
+unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
+static inline unsigned hashmap_buckets(Hashmap *h) {
+        return internal_hashmap_buckets(HASHMAP_BASE(h));
+}
+static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
+        return internal_hashmap_buckets(HASHMAP_BASE(h));
+}
+
+bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key);
+static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) {
+        return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
+}
+static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) {
+        return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
+}
+
+void internal_hashmap_clear(HashmapBase *h);
+static inline void hashmap_clear(Hashmap *h) {
+        internal_hashmap_clear(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_clear(OrderedHashmap *h) {
+        internal_hashmap_clear(HASHMAP_BASE(h));
+}
+
+void internal_hashmap_clear_free(HashmapBase *h);
+static inline void hashmap_clear_free(Hashmap *h) {
+        internal_hashmap_clear_free(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
+        internal_hashmap_clear_free(HASHMAP_BASE(h));
+}
+
+void hashmap_clear_free_free(Hashmap *h);
+static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
+        hashmap_clear_free_free(PLAIN_HASHMAP(h));
+}
+
+/*
+ * Note about all *_first*() functions
+ *
+ * For plain Hashmaps and Sets the order of entries is undefined.
+ * The functions find whatever entry is first in the implementation
+ * internal order.
+ *
+ * Only for OrderedHashmaps the order is well defined and finding
+ * the first entry is O(1).
+ */
+
+void *internal_hashmap_steal_first(HashmapBase *h);
+static inline void *hashmap_steal_first(Hashmap *h) {
+        return internal_hashmap_steal_first(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
+        return internal_hashmap_steal_first(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_steal_first_key(HashmapBase *h);
+static inline void *hashmap_steal_first_key(Hashmap *h) {
+        return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
+        return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_first_key(HashmapBase *h) _pure_;
+static inline void *hashmap_first_key(Hashmap *h) {
+        return internal_hashmap_first_key(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
+        return internal_hashmap_first_key(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_first(HashmapBase *h) _pure_;
+static inline void *hashmap_first(Hashmap *h) {
+        return internal_hashmap_first(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_first(OrderedHashmap *h) {
+        return internal_hashmap_first(HASHMAP_BASE(h));
+}
+
+/* no hashmap_next */
+void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
+
+char **internal_hashmap_get_strv(HashmapBase *h);
+static inline char **hashmap_get_strv(Hashmap *h) {
+        return internal_hashmap_get_strv(HASHMAP_BASE(h));
+}
+static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
+        return internal_hashmap_get_strv(HASHMAP_BASE(h));
+}
+
+/*
+ * Hashmaps are iterated in unpredictable order.
+ * OrderedHashmaps are an exception to this. They are iterated in the order
+ * the entries were inserted.
+ * It is safe to remove the current entry.
+ */
+#define HASHMAP_FOREACH(e, h, i) \
+        for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); )
+
+#define ORDERED_HASHMAP_FOREACH(e, h, i) \
+        for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); )
+
+#define HASHMAP_FOREACH_KEY(e, k, h, i) \
+        for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
+
+#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
+        for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
+
+#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
+#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
+#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
+#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
+#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
+#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c
new file mode 100644
index 00000000..1f257bd5
--- /dev/null
+++ b/src/systemd/src/basic/hexdecoct.c
@@ -0,0 +1,702 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "hexdecoct.h"
+#include "macro.h"
+
+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 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;
+}
+
+int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        uint8_t *z;
+        const char *x;
+
+        assert(mem);
+        assert(len);
+        assert(p);
+
+        z = r = malloc((l + 1) / 2 + 1);
+        if (!r)
+                return -ENOMEM;
+
+        for (x = p; x < p + l; x += 2) {
+                int a, b;
+
+                a = unhexchar(x[0]);
+                if (a < 0)
+                        return a;
+                else if (x+1 < p + l) {
+                        b = unhexchar(x[1]);
+                        if (b < 0)
+                                return b;
+                } else
+                        b = 0;
+
+                *(z++) = (uint8_t) a << 4 | (uint8_t) b;
+        }
+
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *len = (l + 1) / 2;
+
+        return 0;
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-6
+ * Notice that base32hex differs from base32 in the alphabet it uses.
+ * The distinction is that the base32hex representation preserves the
+ * order of the underlying data when compared as bytestrings, this is
+ * useful when representing NSEC3 hashes, as one can then verify the
+ * order of hashes directly from their representation. */
+char base32hexchar(int x) {
+        static const char table[32] = "0123456789"
+                                      "ABCDEFGHIJKLMNOPQRSTUV";
+
+        return table[x & 31];
+}
+
+int unbase32hexchar(char c) {
+        unsigned offset;
+
+        if (c >= '0' && c <= '9')
+                return c - '0';
+
+        offset = '9' - '0' + 1;
+
+        if (c >= 'A' && c <= 'V')
+                return c - 'A' + offset;
+
+        return -EINVAL;
+}
+
+char *base32hexmem(const void *p, size_t l, bool padding) {
+        char *r, *z;
+        const uint8_t *x;
+        size_t len;
+
+        if (padding)
+                /* five input bytes makes eight output bytes, padding is added so we must round up */
+                len = 8 * (l + 4) / 5;
+        else {
+                /* same, but round down as there is no padding */
+                len = 8 * l / 5;
+
+                switch (l % 5) {
+                case 4:
+                        len += 7;
+                        break;
+                case 3:
+                        len += 5;
+                        break;
+                case 2:
+                        len += 4;
+                        break;
+                case 1:
+                        len += 2;
+                        break;
+                }
+        }
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
+                /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
+                   x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
+                *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);  /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
+                *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
+                *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5);  /* 000QQWWW */
+                *(z++) = base32hexchar((x[4] & 31));                  /* 000WWWWW */
+        }
+
+        switch (l % 5) {
+        case 4:
+                *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);   /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
+                *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
+                *(z++) = base32hexchar((x[3] & 3) << 3);              /* 000QQ000 */
+                if (padding)
+                        *(z++) = '=';
+
+                break;
+
+        case 3:
+                *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1);            /* 000ZZZZ0 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+
+        case 2:
+                *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4);             /* 000Y0000 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+
+        case 1:
+                *(z++) = base32hexchar(x[0] >> 3);       /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+        }
+
+        *z = 0;
+        return r;
+}
+
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        int a, b, c, d, e, f, g, h;
+        uint8_t *z;
+        const char *x;
+        size_t len;
+        unsigned pad = 0;
+
+        assert(p);
+
+        /* padding ensures any base32hex input has input divisible by 8 */
+        if (padding && l % 8 != 0)
+                return -EINVAL;
+
+        if (padding) {
+                /* strip the padding */
+                while (l > 0 && p[l - 1] == '=' && pad < 7) {
+                        pad ++;
+                        l --;
+                }
+        }
+
+        /* a group of eight input bytes needs five output bytes, in case of
+           padding we need to add some extra bytes */
+        len = (l / 8) * 5;
+
+        switch (l % 8) {
+        case 7:
+                len += 4;
+                break;
+        case 5:
+                len += 3;
+                break;
+        case 4:
+                len += 2;
+                break;
+        case 2:
+                len += 1;
+                break;
+        case 0:
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return -ENOMEM;
+
+        for (x = p; x < p + (l / 8) * 8; x += 8) {
+                /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
+                   e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                f = unbase32hexchar(x[5]);
+                if (f < 0)
+                        return -EINVAL;
+
+                g = unbase32hexchar(x[6]);
+                if (g < 0)
+                        return -EINVAL;
+
+                h = unbase32hexchar(x[7]);
+                if (h < 0)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+                *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
+                *(z++) = (uint8_t) g << 5 | (uint8_t) h;                         /* VVVRRRRR */
+        }
+
+        switch (l % 8) {
+        case 7:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                f = unbase32hexchar(x[5]);
+                if (f < 0)
+                        return -EINVAL;
+
+                g = unbase32hexchar(x[6]);
+                if (g < 0)
+                        return -EINVAL;
+
+                /* g == 000VV000 */
+                if (g & 7)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+                *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
+
+                break;
+        case 5:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                /* e == 000SSSS0 */
+                if (e & 1)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+
+                break;
+        case 4:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                /* d == 000W0000 */
+                if (d & 15)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+
+                break;
+        case 2:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                /* b == 000YYY00 */
+                if (b & 3)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
+
+                break;
+        case 0:
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *_len = len;
+
+        return 0;
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-4 */
+char base64char(int x) {
+        static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                      "abcdefghijklmnopqrstuvwxyz"
+                                      "0123456789+/";
+        return table[x & 63];
+}
+
+int unbase64char(char c) {
+        unsigned offset;
+
+        if (c >= 'A' && c <= 'Z')
+                return c - 'A';
+
+        offset = 'Z' - 'A' + 1;
+
+        if (c >= 'a' && c <= 'z')
+                return c - 'a' + offset;
+
+        offset += 'z' - 'a' + 1;
+
+        if (c >= '0' && c <= '9')
+                return c - '0' + offset;
+
+        offset += '9' - '0' + 1;
+
+        if (c == '+')
+                return offset;
+
+        offset ++;
+
+        if (c == '/')
+                return offset;
+
+        return -EINVAL;
+}
+
+char *base64mem(const void *p, size_t l) {
+        char *r, *z;
+        const uint8_t *x;
+
+        /* three input bytes makes four output bytes, padding is added so we must round up */
+        z = r = malloc(4 * (l + 2) / 3 + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
+                /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
+                *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
+                *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
+                *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
+        }
+
+        switch (l % 3) {
+        case 2:
+                *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+                *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
+                *(z++) = '=';
+
+                break;
+        case 1:
+                *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
+                *(z++) = '=';
+                *(z++) = '=';
+
+                break;
+        }
+
+        *z = 0;
+        return r;
+}
+
+int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        int a, b, c, d;
+        uint8_t *z;
+        const char *x;
+        size_t len;
+
+        assert(p);
+
+        /* padding ensures any base63 input has input divisible by 4 */
+        if (l % 4 != 0)
+                return -EINVAL;
+
+        /* strip the padding */
+        if (l > 0 && p[l - 1] == '=')
+                l --;
+        if (l > 0 && p[l - 1] == '=')
+                l --;
+
+        /* a group of four input bytes needs three output bytes, in case of
+           padding we need to add two or three extra bytes */
+        len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0);
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return -ENOMEM;
+
+        for (x = p; x < p + (l / 4) * 4; x += 4) {
+                /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase64char(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase64char(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+                *(z++) = (uint8_t) c << 6 | (uint8_t) d;      /* ZZWWWWWW */
+        }
+
+        switch (l % 4) {
+        case 3:
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase64char(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                /* c == 00ZZZZ00 */
+                if (c & 3)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+
+                break;
+        case 2:
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                /* b == 00YY0000 */
+                if (b & 15)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+
+                break;
+        case 0:
+
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *_len = len;
+
+        return 0;
+}
+
+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;
+        }
+}
diff --git a/src/systemd/src/basic/hexdecoct.h b/src/systemd/src/basic/hexdecoct.h
new file mode 100644
index 00000000..ec4115f2
--- /dev/null
+++ b/src/systemd/src/basic/hexdecoct.h
@@ -0,0 +1,57 @@
+/*-*- 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 <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+char octchar(int x) _const_;
+int unoctchar(char c) _const_;
+
+char decchar(int x) _const_;
+int undecchar(char c) _const_;
+
+char hexchar(int x) _const_;
+int unhexchar(char c) _const_;
+
+char *hexmem(const void *p, size_t l);
+int unhexmem(const char *p, size_t l, void **mem, size_t *len);
+
+char base32hexchar(int x) _const_;
+int unbase32hexchar(char c) _const_;
+
+char base64char(int x) _const_;
+int unbase64char(char c) _const_;
+
+char *base32hexmem(const void *p, size_t l, bool padding);
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
+
+char *base64mem(const void *p, size_t l);
+int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
+
+void hexdump(FILE *f, const void *p, size_t s);
diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c
new file mode 100644
index 00000000..50f81925
--- /dev/null
+++ b/src/systemd/src/basic/hostname-util.c
@@ -0,0 +1,230 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 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 <bits/local_lim.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "hostname-util.h"
+#include "macro.h"
+#include "string-util.h"
+
+bool hostname_is_set(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        if (isempty(u.nodename))
+                return false;
+
+        /* This is the built-in kernel default host name */
+        if (streq(u.nodename, "(none)"))
+                return false;
+
+        return true;
+}
+
+char* gethostname_malloc(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        if (isempty(u.nodename) || streq(u.nodename, "(none)"))
+                return strdup(u.sysname);
+
+        return strdup(u.nodename);
+}
+
+static bool hostname_valid_char(char c) {
+        return
+                (c >= 'a' && c <= 'z') ||
+                (c >= 'A' && c <= 'Z') ||
+                (c >= '0' && c <= '9') ||
+                c == '-' ||
+                c == '_' ||
+                c == '.';
+}
+
+/**
+ * Check if s looks like a valid host name or FQDN. This does not do
+ * full DNS validation, but only checks if the name is composed of
+ * allowed characters and the length is not above the maximum allowed
+ * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
+ * allow_trailing_dot is true and at least two components are present
+ * in the name. Note that due to the restricted charset and length
+ * this call is substantially more conservative than
+ * dns_name_is_valid().
+ */
+bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
+        unsigned n_dots = 0;
+        const char *p;
+        bool dot;
+
+        if (isempty(s))
+                return false;
+
+        /* Doesn't accept empty hostnames, hostnames with
+         * leading dots, and hostnames with multiple dots in a
+         * sequence. Also ensures that the length stays below
+         * HOST_NAME_MAX. */
+
+        for (p = s, dot = true; *p; p++) {
+                if (*p == '.') {
+                        if (dot)
+                                return false;
+
+                        dot = true;
+                        n_dots ++;
+                } else {
+                        if (!hostname_valid_char(*p))
+                                return false;
+
+                        dot = false;
+                }
+        }
+
+        if (dot && (n_dots < 2 || !allow_trailing_dot))
+                return false;
+
+        if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
+                                  * Linux, but DNS allows domain names
+                                  * up to 255 characters */
+                return false;
+
+        return true;
+}
+
+char* hostname_cleanup(char *s) {
+        char *p, *d;
+        bool dot;
+
+        assert(s);
+
+        for (p = s, d = s, dot = true; *p; p++) {
+                if (*p == '.') {
+                        if (dot)
+                                continue;
+
+                        *(d++) = '.';
+                        dot = true;
+                } else if (hostname_valid_char(*p)) {
+                        *(d++) = *p;
+                        dot = false;
+                }
+
+        }
+
+        if (dot && d > s)
+                d[-1] = 0;
+        else
+                *d = 0;
+
+        strshorten(s, HOST_NAME_MAX);
+
+        return s;
+}
+
+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 strcaseeq(hostname, "localhost") ||
+               strcaseeq(hostname, "localhost.") ||
+               strcaseeq(hostname, "localdomain.") ||
+               strcaseeq(hostname, "localdomain") ||
+               endswith_no_case(hostname, ".localhost") ||
+               endswith_no_case(hostname, ".localhost.") ||
+               endswith_no_case(hostname, ".localdomain") ||
+               endswith_no_case(hostname, ".localdomain.");
+}
+
+#if 0 /* NM_IGNORED */
+bool is_gateway_hostname(const char *hostname) {
+        assert(hostname);
+
+        /* This tries to identify the valid syntaxes for the our
+         * synthetic "gateway" host. */
+
+        return
+                strcaseeq(hostname, "gateway") ||
+                strcaseeq(hostname, "gateway.");
+}
+
+int sethostname_idempotent(const char *s) {
+        char buf[HOST_NAME_MAX + 1] = {};
+
+        assert(s);
+
+        if (gethostname(buf, sizeof(buf)) < 0)
+                return -errno;
+
+        if (streq(buf, s))
+                return 0;
+
+        if (sethostname(s, strlen(s)) < 0)
+                return -errno;
+
+        return 1;
+}
+
+int read_hostname_config(const char *path, char **hostname) {
+        _cleanup_fclose_ FILE *f = NULL;
+        char l[LINE_MAX];
+        char *name = NULL;
+
+        assert(path);
+        assert(hostname);
+
+        f = fopen(path, "re");
+        if (!f)
+                return -errno;
+
+        /* may have comments, ignore them */
+        FOREACH_LINE(l, f, return -errno) {
+                truncate_nl(l);
+                if (l[0] != '\0' && l[0] != '#') {
+                        /* found line with value */
+                        name = hostname_cleanup(l);
+                        name = strdup(name);
+                        if (!name)
+                                return -ENOMEM;
+                        break;
+                }
+        }
+
+        if (!name)
+                /* no non-empty line found */
+                return -ENOENT;
+
+        *hostname = name;
+        return 0;
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h
new file mode 100644
index 00000000..a2e69200
--- /dev/null
+++ b/src/systemd/src/basic/hostname-util.h
@@ -0,0 +1,44 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-2015 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 <stdbool.h>
+
+#include "macro.h"
+
+bool hostname_is_set(void);
+
+char* gethostname_malloc(void);
+
+bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
+char* hostname_cleanup(char *s);
+
+#define machine_name_is_valid(s) hostname_is_valid(s, false)
+
+bool is_localhost(const char *hostname);
+bool is_gateway_hostname(const char *hostname);
+
+int sethostname_idempotent(const char *s);
+
+int read_hostname_config(const char *path, char **hostname);
diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c
new file mode 100644
index 00000000..5027f401
--- /dev/null
+++ b/src/systemd/src/basic/in-addr-util.c
@@ -0,0 +1,360 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <arpa/inet.h>
+#include <endian.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "macro.h"
+#include "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) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
+
+        if (family == AF_INET6)
+                return IN6_IS_ADDR_LINKLOCAL(&u->in6);
+
+        return -EAFNOSUPPORT;
+}
+
+int in_addr_is_localhost(int family, const union in_addr_union *u) {
+        assert(u);
+
+        if (family == AF_INET)
+                /* All of 127.x.x.x is localhost. */
+                return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
+
+        if (family == AF_INET6)
+                return IN6_IS_ADDR_LOOPBACK(&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 char in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
+        assert(addr);
+
+        return 32 - u32ctz(be32toh(addr->s_addr));
+}
+
+struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
+        assert(addr);
+        assert(prefixlen <= 32);
+
+        /* Shifting beyond 32 is not defined, handle this specially. */
+        if (prefixlen == 0)
+                addr->s_addr = 0;
+        else
+                addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
+
+        return 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;
+
+        in_addr_prefixlen_to_netmask(mask, prefixlen);
+        return 0;
+}
+
+int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
+        assert(addr);
+
+        if (family == AF_INET) {
+                struct in_addr mask;
+
+                if (!in_addr_prefixlen_to_netmask(&mask, prefixlen))
+                        return -EINVAL;
+
+                addr->in.s_addr &= mask.s_addr;
+                return 0;
+        }
+
+        if (family == AF_INET6) {
+                unsigned i;
+
+                for (i = 0; i < 16; i++) {
+                        uint8_t mask;
+
+                        if (prefixlen >= 8) {
+                                mask = 0xFF;
+                                prefixlen -= 8;
+                        } else {
+                                mask = 0xFF << (8 - prefixlen);
+                                prefixlen = 0;
+                        }
+
+                        addr->in6.s6_addr[i] &= mask;
+                }
+
+                return 0;
+        }
+
+        return -EAFNOSUPPORT;
+}
diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h
new file mode 100644
index 00000000..f94aba83
--- /dev/null
+++ b/src/systemd/src/basic/in-addr-util.h
@@ -0,0 +1,58 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <netinet/in.h>
+#include <stddef.h>
+#include <sys/socket.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_is_localhost(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 char in_addr_netmask_to_prefixlen(const struct in_addr *addr);
+struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
+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);
+int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
+
+static inline size_t FAMILY_ADDRESS_SIZE(int family) {
+        assert(family == AF_INET || family == AF_INET6);
+        return family == AF_INET6 ? 16 : 4;
+}
+
+#define IN_ADDR_NULL ((union in_addr_union) {})
diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c
new file mode 100644
index 00000000..c7bb1e78
--- /dev/null
+++ b/src/systemd/src/basic/io-util.c
@@ -0,0 +1,268 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "io-util.h"
+#include "time-util.h"
+
+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;
+        }
+}
+
+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);
+
+        /* If called with nbytes == 0, let's call read() at least
+         * once, to validate the operation */
+
+        if (nbytes > (size_t) SSIZE_MAX)
+                return -EINVAL;
+
+        do {
+                ssize_t k;
+
+                k = read(fd, p, nbytes);
+                if (k < 0) {
+                        if (errno == EINTR)
+                                continue;
+
+                        if (errno == EAGAIN && do_poll) {
+
+                                /* We knowingly ignore any return value here,
+                                 * and expect that any error/EOF is reported
+                                 * via read() */
+
+                                (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
+                                continue;
+                        }
+
+                        return n > 0 ? n : -errno;
+                }
+
+                if (k == 0)
+                        return n;
+
+                assert((size_t) k <= nbytes);
+
+                p += k;
+                nbytes -= k;
+                n += k;
+        } while (nbytes > 0);
+
+        return n;
+}
+
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
+        ssize_t n;
+
+        n = loop_read(fd, buf, nbytes, do_poll);
+        if (n < 0)
+                return (int) n;
+        if ((size_t) n != nbytes)
+                return -EIO;
+
+        return 0;
+}
+
+int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
+        const uint8_t *p = buf;
+
+        assert(fd >= 0);
+        assert(buf);
+
+        if (nbytes > (size_t) SSIZE_MAX)
+                return -EINVAL;
+
+        do {
+                ssize_t k;
+
+                k = write(fd, p, nbytes);
+                if (k < 0) {
+                        if (errno == EINTR)
+                                continue;
+
+                        if (errno == EAGAIN && do_poll) {
+                                /* We knowingly ignore any return value here,
+                                 * and expect that any error/EOF is reported
+                                 * via write() */
+
+                                (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
+                                continue;
+                        }
+
+                        return -errno;
+                }
+
+                if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
+                        return -EIO;
+
+                assert((size_t) k <= nbytes);
+
+                p += k;
+                nbytes -= k;
+        } while (nbytes > 0);
+
+        return 0;
+}
+
+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;
+}
+
+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;
+}
+
+static size_t nul_length(const uint8_t *p, size_t sz) {
+        size_t n = 0;
+
+        while (sz > 0) {
+                if (*p != 0)
+                        break;
+
+                n++;
+                p++;
+                sz--;
+        }
+
+        return n;
+}
+
+ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
+        const uint8_t *q, *w, *e;
+        ssize_t l;
+
+        q = w = p;
+        e = q + sz;
+        while (q < e) {
+                size_t n;
+
+                n = nul_length(q, e - q);
+
+                /* If there are more than the specified run length of
+                 * NUL bytes, or if this is the beginning or the end
+                 * of the buffer, then seek instead of write */
+                if ((n > run_length) ||
+                    (n > 0 && q == p) ||
+                    (n > 0 && q + n >= e)) {
+                        if (q > w) {
+                                l = write(fd, w, q - w);
+                                if (l < 0)
+                                        return -errno;
+                                if (l != q -w)
+                                        return -EIO;
+                        }
+
+                        if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
+                                return -errno;
+
+                        q += n;
+                        w = q;
+                } else if (n > 0)
+                        q += n;
+                else
+                        q ++;
+        }
+
+        if (q > w) {
+                l = write(fd, w, q - w);
+                if (l < 0)
+                        return -errno;
+                if (l != q - w)
+                        return -EIO;
+        }
+
+        return q - (const uint8_t*) p;
+}
diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h
new file mode 100644
index 00000000..a6260965
--- /dev/null
+++ b/src/systemd/src/basic/io-util.h
@@ -0,0 +1,81 @@
+/*-*- 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 <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+int flush_fd(int fd);
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
+int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
+
+int pipe_eof(int fd);
+
+int fd_wait_for_event(int fd, int event, usec_t timeout);
+
+ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
+
+#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;
+}
diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h
new file mode 100644
index 00000000..3ac38418
--- /dev/null
+++ b/src/systemd/src/basic/list.h
@@ -0,0 +1,186 @@
+/*-*- 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"
+
+/* 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)
+
+/* Append an item to the list */
+#define LIST_APPEND(name,head,item)                                     \
+        do {                                                            \
+                typeof(*(head)) *_tail;                                 \
+                LIST_FIND_TAIL(name,head,_tail);                        \
+                LIST_INSERT_AFTER(name,head,_tail,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)
+
+/* Insert an item before another one (a = where, b = what) */
+#define LIST_INSERT_BEFORE(name,head,a,b)                               \
+        do {                                                            \
+                typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
+                assert(_b);                                             \
+                if (!_a) {                                              \
+                        if (!*_head) {                                  \
+                                _b->name##_next = NULL;                 \
+                                _b->name##_prev = NULL;                 \
+                                *_head = _b;                            \
+                        } else {                                        \
+                                typeof(*(head)) *_tail = (head);        \
+                                while (_tail->name##_next)              \
+                                        _tail = _tail->name##_next;     \
+                                _b->name##_next = NULL;                 \
+                                _b->name##_prev = _tail;                \
+                                _tail->name##_next = _b;                \
+                        }                                               \
+                } else {                                                \
+                        if ((_b->name##_prev = _a->name##_prev))        \
+                                _b->name##_prev->name##_next = _b;      \
+                        _b->name##_next = _a;                           \
+                        _a->name##_prev = _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)
+
+/* Iterate through all the members of the list p is included in, but skip over p */
+#define LIST_FOREACH_OTHERS(name,i,p)                                   \
+        for (({                                                         \
+                (i) = (p);                                              \
+                while ((i) && (i)->name##_prev)                         \
+                        (i) = (i)->name##_prev;                         \
+                if ((i) == (p))                                         \
+                        (i) = (p)->name##_next;                         \
+             });                                                        \
+             (i);                                                       \
+             (i) = (i)->name##_next == (p) ? (p)->name##_next : (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/systemd/src/basic/log.h b/src/systemd/src/basic/log.h
new file mode 100644
index 00000000..bc316189
--- /dev/null
+++ b/src/systemd/src/basic/log.h
@@ -0,0 +1,246 @@
+/*-*- 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 <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/signalfd.h>
+#include <syslog.h>
+
+#include "sd-id128.h"
+
+#include "macro.h"
+
+typedef enum LogTarget{
+        LOG_TARGET_CONSOLE,
+        LOG_TARGET_CONSOLE_PREFIXED,
+        LOG_TARGET_KMSG,
+        LOG_TARGET_JOURNAL,
+        LOG_TARGET_JOURNAL_OR_KMSG,
+        LOG_TARGET_SYSLOG,
+        LOG_TARGET_SYSLOG_OR_KMSG,
+        LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
+        LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
+        LOG_TARGET_NULL,
+        _LOG_TARGET_MAX,
+        _LOG_TARGET_INVALID = -1
+}  LogTarget;
+
+void log_set_target(LogTarget target);
+void log_set_max_level(int level);
+void log_set_facility(int facility);
+
+int log_set_target_from_string(const char *e);
+int log_set_max_level_from_string(const char *e);
+
+void log_show_color(bool b);
+bool log_get_show_color(void) _pure_;
+void log_show_location(bool b);
+bool log_get_show_location(void) _pure_;
+
+int log_show_color_from_string(const char *e);
+int log_show_location_from_string(const char *e);
+
+LogTarget log_get_target(void) _pure_;
+int log_get_max_level(void) _pure_;
+
+int log_open(void);
+void log_close(void);
+void log_forget_fds(void);
+
+void log_close_syslog(void);
+void log_close_journal(void);
+void log_close_kmsg(void);
+void log_close_console(void);
+
+void log_parse_environment(void);
+
+#if 0 /* NM_IGNORED */
+int log_internal(
+                int level,
+                int error,
+                const char *file,
+                int line,
+                const char *func,
+                const char *format, ...) _printf_(6,7);
+
+int log_internalv(
+                int level,
+                int error,
+                const char *file,
+                int line,
+                const char *func,
+                const char *format,
+                va_list ap) _printf_(6,0);
+
+int log_object_internal(
+                int level,
+                int error,
+                const char *file,
+                int line,
+                const char *func,
+                const char *object_field,
+                const char *object,
+                const char *format, ...) _printf_(8,9);
+
+int log_object_internalv(
+                int level,
+                int error,
+                const char*file,
+                int line,
+                const char *func,
+                const char *object_field,
+                const char *object,
+                const char *format,
+                va_list ap) _printf_(8,0);
+
+int log_struct_internal(
+                int level,
+                int error,
+                const char *file,
+                int line,
+                const char *func,
+                const char *format, ...) _printf_(6,0) _sentinel_;
+
+int log_oom_internal(
+                const char *file,
+                int line,
+                const char *func);
+
+/* This modifies the buffer passed! */
+int log_dump_internal(
+                int level,
+                int error,
+                const char *file,
+                int line,
+                const char *func,
+                char *buffer);
+
+/* Logging for various assertions */
+noreturn void log_assert_failed(
+                const char *text,
+                const char *file,
+                int line,
+                const char *func);
+
+noreturn void log_assert_failed_unreachable(
+                const char *text,
+                const char *file,
+                int line,
+                const char *func);
+
+void log_assert_failed_return(
+                const char *text,
+                const char *file,
+                int line,
+                const char *func);
+
+/* Logging with level */
+#define log_full_errno(level, error, ...)                               \
+        ({                                                              \
+                int _level = (level), _e = (error);                     \
+                (log_get_max_level() >= LOG_PRI(_level))                \
+                        ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+                        : -abs(_e);                                     \
+        })
+#endif /* NM_IGNORED */
+
+#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__)
+
+/* Normal logging */
+#define log_debug(...)     log_full(LOG_DEBUG,   __VA_ARGS__)
+#define log_info(...)      log_full(LOG_INFO,    __VA_ARGS__)
+#define log_notice(...)    log_full(LOG_NOTICE,  __VA_ARGS__)
+#define log_warning(...)   log_full(LOG_WARNING, __VA_ARGS__)
+#define log_error(...)     log_full(LOG_ERR,     __VA_ARGS__)
+#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
+
+/* Logging triggered by an errno-like error */
+#define log_debug_errno(error, ...)     log_full_errno(LOG_DEBUG,   error, __VA_ARGS__)
+#define log_info_errno(error, ...)      log_full_errno(LOG_INFO,    error, __VA_ARGS__)
+#define log_notice_errno(error, ...)    log_full_errno(LOG_NOTICE,  error, __VA_ARGS__)
+#define log_warning_errno(error, ...)   log_full_errno(LOG_WARNING, error, __VA_ARGS__)
+#define log_error_errno(error, ...)     log_full_errno(LOG_ERR,     error, __VA_ARGS__)
+#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
+
+#ifdef LOG_TRACE
+#  define log_trace(...) log_debug(__VA_ARGS__)
+#else
+#  define log_trace(...) do {} while(0)
+#endif
+
+/* Structured logging */
+#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+/* This modifies the buffer passed! */
+#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer)
+
+#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__)
+
+bool log_on_console(void) _pure_;
+
+const char *log_target_to_string(LogTarget target) _const_;
+LogTarget log_target_from_string(const char *s) _pure_;
+
+/* Helpers to prepare various fields for structured logging */
+#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
+#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
+
+void log_received_signal(int level, const struct signalfd_siginfo *si);
+
+void log_set_upgrade_syslog_to_journal(bool b);
+
+int log_syntax_internal(
+                const char *unit,
+                int level,
+                const char *config_file,
+                unsigned config_line,
+                int error,
+                const char *file,
+                int line,
+                const char *func,
+                const char *format, ...) _printf_(9, 10);
+
+#define log_syntax(unit, level, config_file, config_line, error, ...)   \
+        ({                                                              \
+                int _level = (level), _e = (error);                     \
+                (log_get_max_level() >= LOG_PRI(_level))                \
+                        ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+                        : -abs(_e);                                     \
+        })
+
+#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
+        ({                                                              \
+                int _level = (level);                                   \
+                if (log_get_max_level() >= LOG_PRI(_level)) {           \
+                        _cleanup_free_ char *_p = NULL;                 \
+                        _p = utf8_escape_invalid(rvalue);               \
+                        log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
+                                            "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
+                }                                                       \
+                -EINVAL;                                                \
+        })
diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h
new file mode 100644
index 00000000..222f3c42
--- /dev/null
+++ b/src/systemd/src/basic/macro.h
@@ -0,0 +1,379 @@
+/*-*- 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 <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/param.h>
+#include <sys/types.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)))
+
+#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__)
+/* 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 DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES                      \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
+
+#define REENABLE_WARNING                                                \
+        _Pragma("GCC diagnostic pop")
+#else
+#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT
+#define DISABLE_WARNING_FORMAT_NONLITERAL
+#define DISABLE_WARNING_MISSING_PROTOTYPES
+#define DISABLE_WARNING_NONNULL
+#define DISABLE_WARNING_SHADOW
+#define REENABLE_WARNING
+#endif
+
+/* 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)                                                    \
+        __extension__ (__builtin_choose_expr(                            \
+                !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
+                sizeof(x)/sizeof((x)[0]),                                \
+                (void)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);                   \
+        })
+
+/* [(x + y - 1) / y] suffers from an integer overflow, even though the
+ * computation should be possible in the given type. Therefore, we use
+ * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
+ * quotient and the remainder, so both should be equally fast. */
+#define DIV_ROUND_UP(_x, _y)                                            \
+        __extension__ ({                                                \
+                const typeof(_x) __x = (_x);                            \
+                const typeof(_y) __y = (_y);                            \
+                (__x / __y + !!(__x % __y));                            \
+        })
+
+#define assert_message_se(expr, message)                                \
+        do {                                                            \
+                if (_unlikely_(!(expr)))                                \
+                        log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+        } while (false)
+
+#define assert_se(expr) assert_message_se(expr, #expr)
+
+/* We override the glibc assert() here. */
+#undef assert
+#ifdef NDEBUG
+#define assert(expr) do {} while(false)
+#else
+#define assert(expr) assert_message_se(expr, #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_, __COUNTER__) {              \
+                char x[(expr) ? 0 : -1];                                \
+        };                                                              \
+        REENABLE_WARNING
+#endif
+
+#define assert_log(expr, message) ((_likely_(expr))                     \
+        ? (true)                                                        \
+        : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
+
+#define assert_return(expr, r)                                          \
+        do {                                                            \
+                if (!assert_log(expr, #expr))                           \
+                        return (r);                                     \
+        } while (false)
+
+#define assert_return_errno(expr, r, err)                               \
+        do {                                                            \
+                if (!assert_log(expr, #expr)) {                         \
+                        errno = err;                                    \
+                        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 CHAR_TO_STR(x) ((char[2]) { x, 0 })
+
+#define char_array_0(x) x[sizeof(x)-1] = 0;
+
+/* 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 (hence works correctly on signed
+ * types). Includes space for the trailing NUL. */
+#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 DECIMAL_STR_WIDTH(x)                            \
+        ({                                              \
+                typeof(x) _x_ = (x);                    \
+                unsigned ans = 1;                       \
+                while (_x_ /= 10)                       \
+                        ans++;                          \
+                ans;                                    \
+        })
+
+#define SET_FLAG(v, flag, b) \
+        (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
+
+#define IN_SET(x, y, ...)                                               \
+        ({                                                              \
+                static const typeof(y) _array[] = { (y), __VA_ARGS__ }; \
+                const typeof(y) _x = (x);                               \
+                unsigned _i;                                            \
+                bool _found = false;                                    \
+                for (_i = 0; _i < ELEMENTSOF(_array); _i++)             \
+                        if (_array[_i] == _x) {                         \
+                                _found = true;                          \
+                                break;                                  \
+                        }                                               \
+                _found;                                                 \
+        })
+
+/* 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 defined(__STDC_VERSION__) && __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 defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+#define noreturn _Noreturn
+#else
+#define noreturn __attribute__((noreturn))
+#endif
+#endif
+
+#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__
+
+#include "log.h"
diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c
new file mode 100644
index 00000000..511b962f
--- /dev/null
+++ b/src/systemd/src/basic/mempool.c
@@ -0,0 +1,108 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-2014 Lennart Poettering
+  Copyright 2014 Michal Schmidt
+
+  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 <stdlib.h>
+
+#include "macro.h"
+#include "mempool.h"
+#include "util.h"
+
+struct pool {
+        struct pool *next;
+        unsigned n_tiles;
+        unsigned n_used;
+};
+
+void* mempool_alloc_tile(struct mempool *mp) {
+        unsigned i;
+
+        /* When a tile is released we add it to the list and simply
+         * place the next pointer at its offset 0. */
+
+        assert(mp->tile_size >= sizeof(void*));
+        assert(mp->at_least > 0);
+
+        if (mp->freelist) {
+                void *r;
+
+                r = mp->freelist;
+                mp->freelist = * (void**) mp->freelist;
+                return r;
+        }
+
+        if (_unlikely_(!mp->first_pool) ||
+            _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) {
+                unsigned n;
+                size_t size;
+                struct pool *p;
+
+                n = mp->first_pool ? mp->first_pool->n_tiles : 0;
+                n = MAX(mp->at_least, n * 2);
+                size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size);
+                n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size;
+
+                p = malloc(size);
+                if (!p)
+                        return NULL;
+
+                p->next = mp->first_pool;
+                p->n_tiles = n;
+                p->n_used = 0;
+
+                mp->first_pool = p;
+        }
+
+        i = mp->first_pool->n_used++;
+
+        return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size;
+}
+
+void* mempool_alloc0_tile(struct mempool *mp) {
+        void *p;
+
+        p = mempool_alloc_tile(mp);
+        if (p)
+                memzero(p, mp->tile_size);
+        return p;
+}
+
+void mempool_free_tile(struct mempool *mp, void *p) {
+        * (void**) p = mp->freelist;
+        mp->freelist = p;
+}
+
+#ifdef VALGRIND
+
+void mempool_drop(struct mempool *mp) {
+        struct pool *p = mp->first_pool;
+        while (p) {
+                struct pool *n;
+                n = p->next;
+                free(p);
+                p = n;
+        }
+}
+
+#endif
diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h
new file mode 100644
index 00000000..e9753adb
--- /dev/null
+++ b/src/systemd/src/basic/mempool.h
@@ -0,0 +1,51 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011-2014 Lennart Poettering
+  Copyright 2014 Michal Schmidt
+
+  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 <stddef.h>
+
+struct pool;
+
+struct mempool {
+        struct pool *first_pool;
+        void *freelist;
+        size_t tile_size;
+        unsigned at_least;
+};
+
+void* mempool_alloc_tile(struct mempool *mp);
+void* mempool_alloc0_tile(struct mempool *mp);
+void mempool_free_tile(struct mempool *mp, void *p);
+
+#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
+struct mempool pool_name = { \
+        .tile_size = sizeof(tile_type), \
+        .at_least = alloc_at_least, \
+}
+
+
+#ifdef VALGRIND
+void mempool_drop(struct mempool *mp);
+#endif
diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c
new file mode 100644
index 00000000..86aec95c
--- /dev/null
+++ b/src/systemd/src/basic/parse-util.c
@@ -0,0 +1,542 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <xlocale.h>
+
+#include "alloc-util.h"
+#if 0 /* NM_IGNORED */
+#include "extract-word.h"
+#endif /* NM_IGNORED */
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+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_mode(const char *s, mode_t *ret) {
+        char *x;
+        long l;
+
+        assert(s);
+        assert(ret);
+
+        s += strspn(s, WHITESPACE);
+        if (s[0] == '-')
+                return -ERANGE;
+
+        errno = 0;
+        l = strtol(s, &x, 8);
+        if (errno != 0)
+                return -errno;
+        if (!x || x == s || *x)
+                return -EINVAL;
+        if (l < 0 || l  > 07777)
+                return -ERANGE;
+
+        *ret = (mode_t) l;
+        return 0;
+}
+
+int parse_ifindex(const char *s, int *ret) {
+        int ifi, r;
+
+        r = safe_atoi(s, &ifi);
+        if (r < 0)
+                return r;
+        if (ifi <= 0)
+                return -EINVAL;
+
+        *ret = ifi;
+        return 0;
+}
+
+int parse_size(const char *t, uint64_t base, uint64_t *size) {
+
+        /* Soo, sometimes we want to parse IEC binary suffixes, 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
+         * hardware 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", 1ULL },
+                { "",  1ULL },
+        };
+
+        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", 1ULL },
+                { "",  1ULL },
+        };
+
+        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 {
+                unsigned long long l, tmp;
+                double frac = 0;
+                char *e;
+                unsigned i;
+
+                p += strspn(p, WHITESPACE);
+
+                errno = 0;
+                l = strtoull(p, &e, 10);
+                if (errno != 0)
+                        return -errno;
+                if (e == p)
+                        return -EINVAL;
+                if (*p == '-')
+                        return -ERANGE;
+
+                if (*e == '.') {
+                        e++;
+
+                        /* strtoull() itself would accept space/+/- */
+                        if (*e >= '0' && *e <= '9') {
+                                unsigned long long l2;
+                                char *e2;
+
+                                l2 = strtoull(e, &e2, 10);
+                                if (errno != 0)
+                                        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))
+                                break;
+
+                if (i >= n_entries)
+                        return -EINVAL;
+
+                if (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) (uint64_t) r != r)
+                        return -ERANGE;
+
+                p = e + strlen(table[i].suffix);
+
+                start_pos = i + 1;
+
+        } while (*p);
+
+        *size = r;
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int parse_range(const char *t, unsigned *lower, unsigned *upper) {
+        _cleanup_free_ char *word = NULL;
+        unsigned l, u;
+        int r;
+
+        assert(lower);
+        assert(upper);
+
+        /* Extract the lower bound. */
+        r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
+
+        r = safe_atou(word, &l);
+        if (r < 0)
+                return r;
+
+        /* Check for the upper bound and extract it if needed */
+        if (!t)
+                /* Single number with no dashes. */
+                u = l;
+        else if (!*t)
+                /* Trailing dash is an error. */
+                return -EINVAL;
+        else {
+                r = safe_atou(t, &u);
+                if (r < 0)
+                        return r;
+        }
+
+        *lower = l;
+        *upper = u;
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+char *format_bytes(char *buf, size_t l, uint64_t t) {
+        unsigned i;
+
+        /* This only does IEC units so far */
+
+        static const struct {
+                const char *suffix;
+                uint64_t factor;
+        } table[] = {
+                { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+                { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+                { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+                { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+                { "M", UINT64_C(1024)*UINT64_C(1024) },
+                { "K", UINT64_C(1024) },
+        };
+
+        if (t == (uint64_t) -1)
+                return NULL;
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+
+                if (t >= table[i].factor) {
+                        snprintf(buf, l,
+                                 "%" PRIu64 ".%" PRIu64 "%s",
+                                 t / table[i].factor,
+                                 ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
+                                 table[i].suffix);
+
+                        goto finish;
+                }
+        }
+
+        snprintf(buf, l, "%" PRIu64 "B", t);
+
+finish:
+        buf[l-1] = 0;
+        return buf;
+
+}
+
+int safe_atou(const char *s, unsigned *ret_u) {
+        char *x = NULL;
+        unsigned long l;
+
+        assert(s);
+        assert(ret_u);
+
+        /* strtoul() is happy to parse negative values, and silently
+         * converts them to unsigned values without generating an
+         * error. We want a clean error, hence let's look for the "-"
+         * prefix on our own, and generate an error. But let's do so
+         * only after strtoul() validated that the string is clean
+         * otherwise, so that we return EINVAL preferably over
+         * ERANGE. */
+
+        s += strspn(s, WHITESPACE);
+
+        errno = 0;
+        l = strtoul(s, &x, 0);
+        if (errno != 0)
+                return -errno;
+        if (!x || x == s || *x)
+                return -EINVAL;
+        if (s[0] == '-')
+                return -ERANGE;
+        if ((unsigned long) (unsigned) l != l)
+                return -ERANGE;
+
+        *ret_u = (unsigned) l;
+        return 0;
+}
+
+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 (errno != 0)
+                return -errno;
+        if (!x || x == s || *x)
+                return -EINVAL;
+        if ((long) (int) l != l)
+                return -ERANGE;
+
+        *ret_i = (int) 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);
+
+        s += strspn(s, WHITESPACE);
+
+        errno = 0;
+        l = strtoull(s, &x, 0);
+        if (errno != 0)
+                return -errno;
+        if (!x || x == s || *x)
+                return -EINVAL;
+        if (*s == '-')
+                return -ERANGE;
+
+        *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 (errno != 0)
+                return -errno;
+        if (!x || x == s || *x)
+                return -EINVAL;
+
+        *ret_lli = l;
+        return 0;
+}
+
+int safe_atou8(const char *s, uint8_t *ret) {
+        char *x = NULL;
+        unsigned long l;
+
+        assert(s);
+        assert(ret);
+
+        s += strspn(s, WHITESPACE);
+
+        errno = 0;
+        l = strtoul(s, &x, 0);
+        if (errno != 0)
+                return -errno;
+        if (!x || x == s || *x)
+                return -EINVAL;
+        if (s[0] == '-')
+                return -ERANGE;
+        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);
+
+        s += strspn(s, WHITESPACE);
+
+        errno = 0;
+        l = strtoul(s, &x, 0);
+        if (errno != 0)
+                return -errno;
+        if (!x || x == s || *x)
+                return -EINVAL;
+        if (s[0] == '-')
+                return -ERANGE;
+        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 (errno != 0)
+                return -errno;
+        if (!x || x == s || *x)
+                return -EINVAL;
+        if ((long) (int16_t) l != l)
+                return -ERANGE;
+
+        *ret = (int16_t) l;
+        return 0;
+}
+
+int safe_atod(const char *s, double *ret_d) {
+        char *x = NULL;
+        double d = 0;
+        locale_t loc;
+
+        assert(s);
+        assert(ret_d);
+
+        loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
+        if (loc == (locale_t) 0)
+                return -errno;
+
+        errno = 0;
+        d = strtod_l(s, &x, loc);
+        if (errno != 0) {
+                freelocale(loc);
+                return -errno;
+        }
+        if (!x || x == s || *x) {
+                freelocale(loc);
+                return -EINVAL;
+        }
+
+        freelocale(loc);
+        *ret_d = (double) d;
+        return 0;
+}
+
+int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
+        size_t i;
+        unsigned val = 0;
+        const char *s;
+
+        s = *p;
+
+        /* accept any number of digits, strtoull is limted to 19 */
+        for(i=0; i < digits; i++,s++) {
+                if (*s < '0' || *s > '9') {
+                        if (i == 0)
+                                return -EINVAL;
+
+                        /* too few digits, pad with 0 */
+                        for (; i < digits; i++)
+                                val *= 10;
+
+                        break;
+                }
+
+                val *= 10;
+                val += *s - '0';
+        }
+
+        /* maybe round up */
+        if (*s >= '5' && *s <= '9')
+                val++;
+
+        s += strspn(s, DIGITS);
+
+        *p = s;
+        *res = val;
+
+        return 0;
+}
diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h
new file mode 100644
index 00000000..fbdf1e43
--- /dev/null
+++ b/src/systemd/src/basic/parse-util.h
@@ -0,0 +1,99 @@
+/*-*- 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 <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+#define MODE_INVALID ((mode_t) -1)
+
+int parse_boolean(const char *v) _pure_;
+int parse_pid(const char *s, pid_t* ret_pid);
+int parse_mode(const char *s, mode_t *ret);
+int parse_ifindex(const char *s, int *ret);
+
+int parse_size(const char *t, uint64_t base, uint64_t *size);
+int parse_range(const char *t, unsigned *lower, unsigned *upper);
+
+#define FORMAT_BYTES_MAX 8
+char *format_bytes(char *buf, size_t l, uint64_t t);
+
+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_atou8(const char *s, uint8_t *ret);
+
+int safe_atou16(const char *s, uint16_t *ret);
+int safe_atoi16(const char *s, int16_t *ret);
+
+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);
+}
+
+#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
+
+int safe_atod(const char *s, double *ret_d);
+
+int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c
new file mode 100644
index 00000000..14dc35db
--- /dev/null
+++ b/src/systemd/src/basic/path-util.c
@@ -0,0 +1,813 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* When we include libgen.h because we need dirname() we immediately
+ * undefine basename() since libgen.h defines it as a macro to the
+ * POSIX version which is really broken. We prefer GNU basename(). */
+#include <libgen.h>
+#undef basename
+
+#include "alloc-util.h"
+#if 0 /* NM_IGNORED */
+#include "extract-word.h"
+#endif /* NM_IGNORED */
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#endif /* NM_IGNORED */
+#include "path-util.h"
+#if 0 /* NM_IGNORED */
+#include "stat-util.h"
+#endif /* NM_IGNORED */
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+#if 0 /* NM_IGNORED */
+bool path_is_absolute(const char *p) {
+        return p[0] == '/';
+}
+
+bool is_path(const char *p) {
+        return !!strchr(p, '/');
+}
+
+int path_split_and_make_absolute(const char *p, char ***ret) {
+        char **l;
+        int r;
+
+        assert(p);
+        assert(ret);
+
+        l = strv_split(p, ":");
+        if (!l)
+                return -ENOMEM;
+
+        r = path_strv_make_absolute_cwd(l);
+        if (r < 0) {
+                strv_free(l);
+                return r;
+        }
+
+        *ret = l;
+        return r;
+}
+
+char *path_make_absolute(const char *p, const char *prefix) {
+        assert(p);
+
+        /* Makes every item in the list an absolute path by prepending
+         * the prefix, if specified and necessary */
+
+        if (path_is_absolute(p) || !prefix)
+                return strdup(p);
+
+        return strjoin(prefix, "/", p, NULL);
+}
+
+int path_make_absolute_cwd(const char *p, char **ret) {
+        char *c;
+
+        assert(p);
+        assert(ret);
+
+        /* Similar to path_make_absolute(), but prefixes with the
+         * current working directory. */
+
+        if (path_is_absolute(p))
+                c = strdup(p);
+        else {
+                _cleanup_free_ char *cwd = NULL;
+
+                cwd = get_current_dir_name();
+                if (!cwd)
+                        return -errno;
+
+                c = strjoin(cwd, "/", p, NULL);
+        }
+        if (!c)
+                return -ENOMEM;
+
+        *ret = c;
+        return 0;
+}
+
+int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
+        char *r, *p;
+        unsigned n_parents;
+
+        assert(from_dir);
+        assert(to_path);
+        assert(_r);
+
+        /* Strips the common part, and adds ".." elements as necessary. */
+
+        if (!path_is_absolute(from_dir))
+                return -EINVAL;
+
+        if (!path_is_absolute(to_path))
+                return -EINVAL;
+
+        /* Skip the common part. */
+        for (;;) {
+                size_t a;
+                size_t b;
+
+                from_dir += strspn(from_dir, "/");
+                to_path += strspn(to_path, "/");
+
+                if (!*from_dir) {
+                        if (!*to_path)
+                                /* from_dir equals to_path. */
+                                r = strdup(".");
+                        else
+                                /* from_dir is a parent directory of to_path. */
+                                r = strdup(to_path);
+
+                        if (!r)
+                                return -ENOMEM;
+
+                        path_kill_slashes(r);
+
+                        *_r = r;
+                        return 0;
+                }
+
+                if (!*to_path)
+                        break;
+
+                a = strcspn(from_dir, "/");
+                b = strcspn(to_path, "/");
+
+                if (a != b)
+                        break;
+
+                if (memcmp(from_dir, to_path, a) != 0)
+                        break;
+
+                from_dir += a;
+                to_path += b;
+        }
+
+        /* If we're here, then "from_dir" has one or more elements that need to
+         * be replaced with "..". */
+
+        /* Count the number of necessary ".." elements. */
+        for (n_parents = 0;;) {
+                from_dir += strspn(from_dir, "/");
+
+                if (!*from_dir)
+                        break;
+
+                from_dir += strcspn(from_dir, "/");
+                n_parents++;
+        }
+
+        r = malloc(n_parents * 3 + strlen(to_path) + 1);
+        if (!r)
+                return -ENOMEM;
+
+        for (p = r; n_parents > 0; n_parents--, p += 3)
+                memcpy(p, "../", 3);
+
+        strcpy(p, to_path);
+        path_kill_slashes(r);
+
+        *_r = r;
+        return 0;
+}
+
+int path_strv_make_absolute_cwd(char **l) {
+        char **s;
+        int r;
+
+        /* Goes through every item in the string list and makes it
+         * absolute. This works in place and won't rollback any
+         * changes on failure. */
+
+        STRV_FOREACH(s, l) {
+                char *t;
+
+                r = path_make_absolute_cwd(*s, &t);
+                if (r < 0)
+                        return r;
+
+                free(*s);
+                *s = t;
+        }
+
+        return 0;
+}
+
+char **path_strv_resolve(char **l, const char *prefix) {
+        char **s;
+        unsigned k = 0;
+        bool enomem = false;
+
+        if (strv_isempty(l))
+                return l;
+
+        /* Goes through every item in the string list and canonicalize
+         * the path. This works in place and won't rollback any
+         * changes on failure. */
+
+        STRV_FOREACH(s, l) {
+                char *t, *u;
+                _cleanup_free_ char *orig = NULL;
+
+                if (!path_is_absolute(*s)) {
+                        free(*s);
+                        continue;
+                }
+
+                if (prefix) {
+                        orig = *s;
+                        t = strappend(prefix, orig);
+                        if (!t) {
+                                enomem = true;
+                                continue;
+                        }
+                } else
+                        t = *s;
+
+                errno = 0;
+                u = canonicalize_file_name(t);
+                if (!u) {
+                        if (errno == ENOENT) {
+                                if (prefix) {
+                                        u = orig;
+                                        orig = NULL;
+                                        free(t);
+                                } else
+                                        u = t;
+                        } else {
+                                free(t);
+                                if (errno == ENOMEM || errno == 0)
+                                        enomem = true;
+
+                                continue;
+                        }
+                } else if (prefix) {
+                        char *x;
+
+                        free(t);
+                        x = path_startswith(u, prefix);
+                        if (x) {
+                                /* restore the slash if it was lost */
+                                if (!startswith(x, "/"))
+                                        *(--x) = '/';
+
+                                t = strdup(x);
+                                free(u);
+                                if (!t) {
+                                        enomem = true;
+                                        continue;
+                                }
+                                u = t;
+                        } else {
+                                /* canonicalized path goes outside of
+                                 * prefix, keep the original path instead */
+                                free(u);
+                                u = orig;
+                                orig = NULL;
+                        }
+                } else
+                        free(t);
+
+                l[k++] = u;
+        }
+
+        l[k] = NULL;
+
+        if (enomem)
+                return NULL;
+
+        return l;
+}
+
+char **path_strv_resolve_uniq(char **l, const char *prefix) {
+
+        if (strv_isempty(l))
+                return l;
+
+        if (!path_strv_resolve(l, prefix))
+                return NULL;
+
+        return strv_uniq(l);
+}
+#endif /* NM_IGNORED */
+
+char *path_kill_slashes(char *path) {
+        char *f, *t;
+        bool slash = false;
+
+        /* Removes redundant inner and trailing slashes. Modifies the
+         * passed string in-place.
+         *
+         * ///foo///bar/ becomes /foo/bar
+         */
+
+        for (f = path, t = path; *f; f++) {
+
+                if (*f == '/') {
+                        slash = true;
+                        continue;
+                }
+
+                if (slash) {
+                        slash = false;
+                        *(t++) = '/';
+                }
+
+                *(t++) = *f;
+        }
+
+        /* Special rule, if we are talking of the root directory, a
+        trailing slash is good */
+
+        if (t == path && slash)
+                *(t++) = '/';
+
+        *t = 0;
+        return path;
+}
+
+#if 0 /* NM_IGNORED */
+char* path_startswith(const char *path, const char *prefix) {
+        assert(path);
+        assert(prefix);
+
+        if ((path[0] == '/') != (prefix[0] == '/'))
+                return NULL;
+
+        for (;;) {
+                size_t a, b;
+
+                path += strspn(path, "/");
+                prefix += strspn(prefix, "/");
+
+                if (*prefix == 0)
+                        return (char*) path;
+
+                if (*path == 0)
+                        return NULL;
+
+                a = strcspn(path, "/");
+                b = strcspn(prefix, "/");
+
+                if (a != b)
+                        return NULL;
+
+                if (memcmp(path, prefix, a) != 0)
+                        return NULL;
+
+                path += a;
+                prefix += b;
+        }
+}
+
+int path_compare(const char *a, const char *b) {
+        int d;
+
+        assert(a);
+        assert(b);
+
+        /* A relative path and an abolute path must not compare as equal.
+         * Which one is sorted before the other does not really matter.
+         * Here a relative path is ordered before an absolute path. */
+        d = (a[0] == '/') - (b[0] == '/');
+        if (d != 0)
+                return d;
+
+        for (;;) {
+                size_t j, k;
+
+                a += strspn(a, "/");
+                b += strspn(b, "/");
+
+                if (*a == 0 && *b == 0)
+                        return 0;
+
+                /* Order prefixes first: "/foo" before "/foo/bar" */
+                if (*a == 0)
+                        return -1;
+                if (*b == 0)
+                        return 1;
+
+                j = strcspn(a, "/");
+                k = strcspn(b, "/");
+
+                /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
+                d = memcmp(a, b, MIN(j, k));
+                if (d != 0)
+                        return (d > 0) - (d < 0); /* sign of d */
+
+                /* Sort "/foo/a" before "/foo/aaa" */
+                d = (j > k) - (j < k);  /* sign of (j - k) */
+                if (d != 0)
+                        return d;
+
+                a += j;
+                b += k;
+        }
+}
+
+bool path_equal(const char *a, const char *b) {
+        return path_compare(a, b) == 0;
+}
+
+bool path_equal_or_files_same(const char *a, const char *b) {
+        return path_equal(a, b) || files_same(a, b) > 0;
+}
+
+char* path_join(const char *root, const char *path, const char *rest) {
+        assert(path);
+
+        if (!isempty(root))
+                return strjoin(root, endswith(root, "/") ? "" : "/",
+                               path[0] == '/' ? path+1 : path,
+                               rest ? (endswith(path, "/") ? "" : "/") : NULL,
+                               rest && rest[0] == '/' ? rest+1 : rest,
+                               NULL);
+        else
+                return strjoin(path,
+                               rest ? (endswith(path, "/") ? "" : "/") : NULL,
+                               rest && rest[0] == '/' ? rest+1 : rest,
+                               NULL);
+}
+
+int find_binary(const char *name, char **ret) {
+        int last_error, r;
+        const char *p;
+
+        assert(name);
+
+        if (is_path(name)) {
+                if (access(name, X_OK) < 0)
+                        return -errno;
+
+                if (ret) {
+                        r = path_make_absolute_cwd(name, ret);
+                        if (r < 0)
+                                return r;
+                }
+
+                return 0;
+        }
+
+        /**
+         * Plain getenv, not secure_getenv, because we want
+         * to actually allow the user to pick the binary.
+         */
+        p = getenv("PATH");
+        if (!p)
+                p = DEFAULT_PATH;
+
+        last_error = -ENOENT;
+
+        for (;;) {
+                _cleanup_free_ char *j = NULL, *element = NULL;
+
+                r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (!path_is_absolute(element))
+                        continue;
+
+                j = strjoin(element, "/", name, NULL);
+                if (!j)
+                        return -ENOMEM;
+
+                if (access(j, X_OK) >= 0) {
+                        /* Found it! */
+
+                        if (ret) {
+                                *ret = path_kill_slashes(j);
+                                j = NULL;
+                        }
+
+                        return 0;
+                }
+
+                last_error = -errno;
+        }
+
+        return last_error;
+}
+
+bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
+        bool changed = false;
+        const char* const* i;
+
+        assert(timestamp);
+
+        if (paths == NULL)
+                return false;
+
+        STRV_FOREACH(i, paths) {
+                struct stat stats;
+                usec_t u;
+
+                if (stat(*i, &stats) < 0)
+                        continue;
+
+                u = timespec_load(&stats.st_mtim);
+
+                /* first check */
+                if (*timestamp >= u)
+                        continue;
+
+                log_debug("timestamp of '%s' changed", *i);
+
+                /* update timestamp */
+                if (update) {
+                        *timestamp = u;
+                        changed = true;
+                } else
+                        return true;
+        }
+
+        return changed;
+}
+
+static int binary_is_good(const char *binary) {
+        _cleanup_free_ char *p = NULL, *d = NULL;
+        int r;
+
+        r = find_binary(binary, &p);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return r;
+
+        /* An fsck that is linked to /bin/true is a non-existent
+         * fsck */
+
+        r = readlink_malloc(p, &d);
+        if (r == -EINVAL) /* not a symlink */
+                return 1;
+        if (r < 0)
+                return r;
+
+        return !path_equal(d, "true") &&
+               !path_equal(d, "/bin/true") &&
+               !path_equal(d, "/usr/bin/true") &&
+               !path_equal(d, "/dev/null");
+}
+
+int fsck_exists(const char *fstype) {
+        const char *checker;
+
+        assert(fstype);
+
+        if (streq(fstype, "auto"))
+                return -EINVAL;
+
+        checker = strjoina("fsck.", fstype);
+        return binary_is_good(checker);
+}
+
+int mkfs_exists(const char *fstype) {
+        const char *mkfs;
+
+        assert(fstype);
+
+        if (streq(fstype, "auto"))
+                return -EINVAL;
+
+        mkfs = strjoina("mkfs.", fstype);
+        return binary_is_good(mkfs);
+}
+
+char *prefix_root(const char *root, const char *path) {
+        char *n, *p;
+        size_t l;
+
+        /* If root is passed, prefixes path with it. Otherwise returns
+         * it as is. */
+
+        assert(path);
+
+        /* First, drop duplicate prefixing slashes from the path */
+        while (path[0] == '/' && path[1] == '/')
+                path++;
+
+        if (isempty(root) || path_equal(root, "/"))
+                return strdup(path);
+
+        l = strlen(root) + 1 + strlen(path) + 1;
+
+        n = new(char, l);
+        if (!n)
+                return NULL;
+
+        p = stpcpy(n, root);
+
+        while (p > n && p[-1] == '/')
+                p--;
+
+        if (path[0] != '/')
+                *(p++) = '/';
+
+        strcpy(p, path);
+        return n;
+}
+
+int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
+        char *p;
+        int r;
+
+        /*
+         * This function is intended to be used in command line
+         * parsers, to handle paths that are passed in. It makes the
+         * path absolute, and reduces it to NULL if omitted or
+         * root (the latter optionally).
+         *
+         * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
+         * SUCCESS! Hence, do not pass in uninitialized pointers.
+         */
+
+        if (isempty(path)) {
+                *arg = mfree(*arg);
+                return 0;
+        }
+
+        r = path_make_absolute_cwd(path, &p);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
+
+        path_kill_slashes(p);
+        if (suppress_root && path_equal(p, "/"))
+                p = mfree(p);
+
+        free(*arg);
+        *arg = p;
+        return 0;
+}
+
+char* dirname_malloc(const char *path) {
+        char *d, *dir, *dir2;
+
+        assert(path);
+
+        d = strdup(path);
+        if (!d)
+                return NULL;
+
+        dir = dirname(d);
+        assert(dir);
+
+        if (dir == d)
+                return d;
+
+        dir2 = strdup(dir);
+        free(d);
+
+        return dir2;
+}
+#endif /* NM_IGNORED */
+
+bool filename_is_valid(const char *p) {
+        const char *e;
+
+        if (isempty(p))
+                return false;
+
+        if (streq(p, "."))
+                return false;
+
+        if (streq(p, ".."))
+                return false;
+
+        e = strchrnul(p, '/');
+        if (*e != 0)
+                return false;
+
+        if (e - p > FILENAME_MAX)
+                return false;
+
+        return true;
+}
+
+#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)+1 > 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;
+}
+
+char *file_in_same_dir(const char *path, const char *filename) {
+        char *e, *ret;
+        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);
+
+        e = strrchr(path, '/');
+        if (!e)
+                return strdup(filename);
+
+        k = strlen(filename);
+        ret = new(char, (e + 1 - path) + k + 1);
+        if (!ret)
+                return NULL;
+
+        memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
+        return ret;
+}
+
+bool hidden_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, ".dpkg-dist") ||
+                endswith(filename, ".dpkg-bak") ||
+                endswith(filename, ".dpkg-backup") ||
+                endswith(filename, ".dpkg-remove") ||
+                endswith(filename, ".swp");
+}
+
+bool hidden_file(const char *filename) {
+        assert(filename);
+
+        if (endswith(filename, "~"))
+                return true;
+
+        return hidden_file_allow_backup(filename);
+}
+
+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/");
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h
new file mode 100644
index 00000000..2bc23fec
--- /dev/null
+++ b/src/systemd/src/basic/path-util.h
@@ -0,0 +1,115 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-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 "nm-sd-adapt.h"
+
+#include <alloca.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
+#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin"
+
+#ifdef HAVE_SPLIT_USR
+#  define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
+#else
+#  define DEFAULT_PATH DEFAULT_PATH_NORMAL
+#endif
+
+bool is_path(const char *p) _pure_;
+int path_split_and_make_absolute(const char *p, char ***ret);
+bool path_is_absolute(const char *p) _pure_;
+char* path_make_absolute(const char *p, const char *prefix);
+int path_make_absolute_cwd(const char *p, char **ret);
+int path_make_relative(const char *from_dir, const char *to_path, char **_r);
+char* path_kill_slashes(char *path);
+char* path_startswith(const char *path, const char *prefix) _pure_;
+int path_compare(const char *a, const char *b) _pure_;
+bool path_equal(const char *a, const char *b) _pure_;
+bool path_equal_or_files_same(const char *a, const char *b);
+char* path_join(const char *root, const char *path, const char *rest);
+
+int path_strv_make_absolute_cwd(char **l);
+char** path_strv_resolve(char **l, const char *prefix);
+char** path_strv_resolve_uniq(char **l, const char *prefix);
+
+int find_binary(const char *name, char **filename);
+
+bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
+
+int fsck_exists(const char *fstype);
+int mkfs_exists(const char *fstype);
+
+/* Iterates through the path prefixes of the specified path, going up
+ * the tree, to root. Also returns "" (and not "/"!) for the root
+ * directory. Excludes the specified directory itself */
+#define PATH_FOREACH_PREFIX(prefix, path) \
+        for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+
+/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
+#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
+        for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+
+char *prefix_root(const char *root, const char *path);
+
+/* Similar to prefix_root(), but returns an alloca() buffer, or
+ * possibly a const pointer into the path parameter */
+#define prefix_roota(root, path)                                        \
+        ({                                                              \
+                const char* _path = (path), *_root = (root), *_ret;     \
+                char *_p, *_n;                                          \
+                size_t _l;                                              \
+                while (_path[0] == '/' && _path[1] == '/')              \
+                        _path ++;                                       \
+                if (isempty(_root) || path_equal(_root, "/"))           \
+                        _ret = _path;                                   \
+                else {                                                  \
+                        _l = strlen(_root) + 1 + strlen(_path) + 1;     \
+                        _n = alloca(_l);                                \
+                        _p = stpcpy(_n, _root);                         \
+                        while (_p > _n && _p[-1] == '/')                \
+                                _p--;                                   \
+                        if (_path[0] != '/')                            \
+                                *(_p++) = '/';                          \
+                        strcpy(_p, _path);                              \
+                        _ret = _n;                                      \
+                }                                                       \
+                _ret;                                                   \
+        })
+
+int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg);
+
+char* dirname_malloc(const char *path);
+
+bool filename_is_valid(const char *p) _pure_;
+bool path_is_safe(const char *p) _pure_;
+
+char *file_in_same_dir(const char *path, const char *filename);
+
+bool hidden_file_allow_backup(const char *filename);
+bool hidden_file(const char *filename) _pure_;
+
+bool is_device_path(const char *path);
diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c
new file mode 100644
index 00000000..4be4ad6e
--- /dev/null
+++ b/src/systemd/src/basic/prioq.c
@@ -0,0 +1,324 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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/>.
+***/
+
+/*
+ * Priority Queue
+ * The prioq object implements a priority queue. That is, it orders objects by
+ * their priority and allows O(1) access to the object with the highest
+ * priority. Insertion and removal are Θ(log n). Optionally, the caller can
+ * provide a pointer to an index which will be kept up-to-date by the prioq.
+ *
+ * The underlying algorithm used in this implementation is a Heap.
+ */
+
+#include "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "hashmap.h"
+#include "prioq.h"
+
+struct prioq_item {
+        void *data;
+        unsigned *idx;
+};
+
+struct Prioq {
+        compare_func_t compare_func;
+        unsigned n_items, n_allocated;
+
+        struct prioq_item *items;
+};
+
+Prioq *prioq_new(compare_func_t compare_func) {
+        Prioq *q;
+
+        q = new0(Prioq, 1);
+        if (!q)
+                return q;
+
+        q->compare_func = compare_func;
+        return q;
+}
+
+Prioq* prioq_free(Prioq *q) {
+        if (!q)
+                return NULL;
+
+        free(q->items);
+        free(q);
+
+        return NULL;
+}
+
+int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
+        assert(q);
+
+        if (*q)
+                return 0;
+
+        *q = prioq_new(compare_func);
+        if (!*q)
+                return -ENOMEM;
+
+        return 0;
+}
+
+static void swap(Prioq *q, unsigned j, unsigned k) {
+        void *saved_data;
+        unsigned *saved_idx;
+
+        assert(q);
+        assert(j < q->n_items);
+        assert(k < q->n_items);
+
+        assert(!q->items[j].idx || *(q->items[j].idx) == j);
+        assert(!q->items[k].idx || *(q->items[k].idx) == k);
+
+        saved_data = q->items[j].data;
+        saved_idx = q->items[j].idx;
+        q->items[j].data = q->items[k].data;
+        q->items[j].idx = q->items[k].idx;
+        q->items[k].data = saved_data;
+        q->items[k].idx = saved_idx;
+
+        if (q->items[j].idx)
+                *q->items[j].idx = j;
+
+        if (q->items[k].idx)
+                *q->items[k].idx = k;
+}
+
+static unsigned shuffle_up(Prioq *q, unsigned idx) {
+        assert(q);
+
+        while (idx > 0) {
+                unsigned k;
+
+                k = (idx-1)/2;
+
+                if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
+                        break;
+
+                swap(q, idx, k);
+                idx = k;
+        }
+
+        return idx;
+}
+
+static unsigned shuffle_down(Prioq *q, unsigned idx) {
+        assert(q);
+
+        for (;;) {
+                unsigned j, k, s;
+
+                k = (idx+1)*2; /* right child */
+                j = k-1;       /* left child */
+
+                if (j >= q->n_items)
+                        break;
+
+                if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
+
+                        /* So our left child is smaller than we are, let's
+                         * remember this fact */
+                        s = j;
+                else
+                        s = idx;
+
+                if (k < q->n_items &&
+                    q->compare_func(q->items[k].data, q->items[s].data) < 0)
+
+                        /* So our right child is smaller than we are, let's
+                         * remember this fact */
+                        s = k;
+
+                /* s now points to the smallest of the three items */
+
+                if (s == idx)
+                        /* No swap necessary, we're done */
+                        break;
+
+                swap(q, idx, s);
+                idx = s;
+        }
+
+        return idx;
+}
+
+int prioq_put(Prioq *q, void *data, unsigned *idx) {
+        struct prioq_item *i;
+        unsigned k;
+
+        assert(q);
+
+        if (q->n_items >= q->n_allocated) {
+                unsigned n;
+                struct prioq_item *j;
+
+                n = MAX((q->n_items+1) * 2, 16u);
+                j = realloc(q->items, sizeof(struct prioq_item) * n);
+                if (!j)
+                        return -ENOMEM;
+
+                q->items = j;
+                q->n_allocated = n;
+        }
+
+        k = q->n_items++;
+        i = q->items + k;
+        i->data = data;
+        i->idx = idx;
+
+        if (idx)
+                *idx = k;
+
+        shuffle_up(q, k);
+
+        return 0;
+}
+
+static void remove_item(Prioq *q, struct prioq_item *i) {
+        struct prioq_item *l;
+
+        assert(q);
+        assert(i);
+
+        l = q->items + q->n_items - 1;
+
+        if (i == l)
+                /* Last entry, let's just remove it */
+                q->n_items--;
+        else {
+                unsigned k;
+
+                /* Not last entry, let's replace the last entry with
+                 * this one, and reshuffle */
+
+                k = i - q->items;
+
+                i->data = l->data;
+                i->idx = l->idx;
+                if (i->idx)
+                        *i->idx = k;
+                q->n_items--;
+
+                k = shuffle_down(q, k);
+                shuffle_up(q, k);
+        }
+}
+
+_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
+        struct prioq_item *i;
+
+        assert(q);
+
+        if (idx) {
+                if (*idx == PRIOQ_IDX_NULL ||
+                    *idx > q->n_items)
+                        return NULL;
+
+                i = q->items + *idx;
+                if (i->data != data)
+                        return NULL;
+
+                return i;
+        } else {
+                for (i = q->items; i < q->items + q->n_items; i++)
+                        if (i->data == data)
+                                return i;
+                return NULL;
+        }
+}
+
+int prioq_remove(Prioq *q, void *data, unsigned *idx) {
+        struct prioq_item *i;
+
+        if (!q)
+                return 0;
+
+        i = find_item(q, data, idx);
+        if (!i)
+                return 0;
+
+        remove_item(q, i);
+        return 1;
+}
+
+int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
+        struct prioq_item *i;
+        unsigned k;
+
+        assert(q);
+
+        i = find_item(q, data, idx);
+        if (!i)
+                return 0;
+
+        k = i - q->items;
+        k = shuffle_down(q, k);
+        shuffle_up(q, k);
+        return 1;
+}
+
+void *prioq_peek(Prioq *q) {
+
+        if (!q)
+                return NULL;
+
+        if (q->n_items <= 0)
+                return NULL;
+
+        return q->items[0].data;
+}
+
+void *prioq_pop(Prioq *q) {
+        void *data;
+
+        if (!q)
+                return NULL;
+
+        if (q->n_items <= 0)
+                return NULL;
+
+        data = q->items[0].data;
+        remove_item(q, q->items);
+        return data;
+}
+
+unsigned prioq_size(Prioq *q) {
+
+        if (!q)
+                return 0;
+
+        return q->n_items;
+}
+
+bool prioq_isempty(Prioq *q) {
+
+        if (!q)
+                return true;
+
+        return q->n_items <= 0;
+}
diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h
new file mode 100644
index 00000000..f6a88f0a
--- /dev/null
+++ b/src/systemd/src/basic/prioq.h
@@ -0,0 +1,47 @@
+/*-*- 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/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <stdbool.h>
+
+#include "hashmap.h"
+#include "macro.h"
+
+typedef struct Prioq Prioq;
+
+#define PRIOQ_IDX_NULL ((unsigned) -1)
+
+Prioq *prioq_new(compare_func_t compare);
+Prioq *prioq_free(Prioq *q);
+int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
+
+int prioq_put(Prioq *q, void *data, unsigned *idx);
+int prioq_remove(Prioq *q, void *data, unsigned *idx);
+int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
+
+void *prioq_peek(Prioq *q) _pure_;
+void *prioq_pop(Prioq *q);
+
+unsigned prioq_size(Prioq *q) _pure_;
+bool prioq_isempty(Prioq *q) _pure_;
diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c
new file mode 100644
index 00000000..21d9bde7
--- /dev/null
+++ b/src/systemd/src/basic/random-util.c
@@ -0,0 +1,140 @@
+/***
+  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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <linux/random.h>
+#include <stdint.h>
+
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+
+#include "fd-util.h"
+#include "io-util.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#endif
+#include "random-util.h"
+#include "time-util.h"
+
+int dev_urandom(void *p, size_t n) {
+#if 0 /* NM_IGNORED */
+        static int have_syscall = -1;
+
+        _cleanup_close_ int fd = -1;
+        int r;
+
+        /* 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 -ENODATA;
+        }
+#else /* NM_IGNORED */
+        _cleanup_close_ int fd = -1;
+#endif /* NM_IGNORED */
+
+        fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return errno == ENOENT ? -ENOSYS : -errno;
+
+        return loop_read_exact(fd, p, n, true);
+}
+
+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();
+}
diff --git a/src/systemd/src/basic/random-util.h b/src/systemd/src/basic/random-util.h
new file mode 100644
index 00000000..1ae76ef0
--- /dev/null
+++ b/src/systemd/src/basic/random-util.h
@@ -0,0 +1,41 @@
+#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 <stddef.h>
+#include <stdint.h>
+
+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;
+}
diff --git a/src/systemd/src/basic/refcnt.h b/src/systemd/src/basic/refcnt.h
new file mode 100644
index 00000000..f7a26192
--- /dev/null
+++ b/src/systemd/src/basic/refcnt.h
@@ -0,0 +1,38 @@
+/*-*- 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/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+/* A type-safe atomic refcounter.
+ *
+ * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */
+
+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/systemd/src/basic/set.h b/src/systemd/src/basic/set.h
new file mode 100644
index 00000000..84c53888
--- /dev/null
+++ b/src/systemd/src/basic/set.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 "nm-sd-adapt.h"
+
+#include "hashmap.h"
+#include "macro.h"
+
+Set *internal_set_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) internal_set_new(ops  HASHMAP_DEBUG_SRC_ARGS)
+
+static inline Set *set_free(Set *s) {
+        internal_hashmap_free(HASHMAP_BASE(s));
+        return NULL;
+}
+
+static inline Set *set_free_free(Set *s) {
+        internal_hashmap_free_free(HASHMAP_BASE(s));
+        return NULL;
+}
+
+/* no set_free_free_free */
+
+static inline Set *set_copy(Set *s) {
+        return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
+}
+
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
+
+int set_put(Set *s, const void *key);
+/* no set_update */
+/* no set_replace */
+static inline void *set_get(Set *s, void *key) {
+        return internal_hashmap_get(HASHMAP_BASE(s), key);
+}
+/* no set_get2 */
+
+static inline bool set_contains(Set *s, const void *key) {
+        return internal_hashmap_contains(HASHMAP_BASE(s), key);
+}
+
+static inline void *set_remove(Set *s, const void *key) {
+        return internal_hashmap_remove(HASHMAP_BASE(s), key);
+}
+
+/* no set_remove2 */
+/* no set_remove_value */
+int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
+/* no set_remove_and_replace */
+int set_merge(Set *s, Set *other);
+
+static inline int set_reserve(Set *h, unsigned entries_add) {
+        return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+
+static inline int set_move(Set *s, Set *other) {
+        return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
+}
+
+static inline int set_move_one(Set *s, Set *other, const void *key) {
+        return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
+}
+
+static inline unsigned set_size(Set *s) {
+        return internal_hashmap_size(HASHMAP_BASE(s));
+}
+
+static inline bool set_isempty(Set *s) {
+        return set_size(s) == 0;
+}
+
+static inline unsigned set_buckets(Set *s) {
+        return internal_hashmap_buckets(HASHMAP_BASE(s));
+}
+
+bool set_iterate(Set *s, Iterator *i, void **value);
+
+static inline void set_clear(Set *s) {
+        internal_hashmap_clear(HASHMAP_BASE(s));
+}
+
+static inline void set_clear_free(Set *s) {
+        internal_hashmap_clear_free(HASHMAP_BASE(s));
+}
+
+/* no set_clear_free_free */
+
+static inline void *set_steal_first(Set *s) {
+        return internal_hashmap_steal_first(HASHMAP_BASE(s));
+}
+
+/* no set_steal_first_key */
+/* no set_first_key */
+
+static inline void *set_first(Set *s) {
+        return internal_hashmap_first(HASHMAP_BASE(s));
+}
+
+/* no set_next */
+
+static inline char **set_get_strv(Set *s) {
+        return internal_hashmap_get_strv(HASHMAP_BASE(s));
+}
+
+int set_consume(Set *s, void *value);
+int set_put_strdup(Set *s, const char *p);
+int set_put_strdupv(Set *s, char **l);
+
+#define SET_FOREACH(e, s, i) \
+        for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
+
+#define _cleanup_set_free_ _cleanup_(set_freep)
+#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
diff --git a/src/systemd/src/basic/siphash24.c b/src/systemd/src/basic/siphash24.c
new file mode 100644
index 00000000..145f64b2
--- /dev/null
+++ b/src/systemd/src/basic/siphash24.c
@@ -0,0 +1,193 @@
+/*
+   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)
+   (Refactored by Tom Gundersen to split up in several functions and follow systemd
+    coding style)
+*/
+
+#include "nm-sd-adapt.h"
+
+#include "macro.h"
+#include "siphash24.h"
+#include "unaligned.h"
+
+static inline uint64_t rotate_left(uint64_t x, uint8_t b) {
+        assert(b < 64);
+
+        return (x << b) | (x >> (64 - b));
+}
+
+static inline void sipround(struct siphash *state) {
+        assert(state);
+
+        state->v0 += state->v1;
+        state->v1 = rotate_left(state->v1, 13);
+        state->v1 ^= state->v0;
+        state->v0 = rotate_left(state->v0, 32);
+        state->v2 += state->v3;
+        state->v3 = rotate_left(state->v3, 16);
+        state->v3 ^= state->v2;
+        state->v0 += state->v3;
+        state->v3 = rotate_left(state->v3, 21);
+        state->v3 ^= state->v0;
+        state->v2 += state->v1;
+        state->v1 = rotate_left(state->v1, 17);
+        state->v1 ^= state->v2;
+        state->v2 = rotate_left(state->v2, 32);
+}
+
+void siphash24_init(struct siphash *state, const uint8_t k[16]) {
+        uint64_t k0, k1;
+
+        assert(state);
+        assert(k);
+
+        k0 = unaligned_read_le64(k);
+        k1 = unaligned_read_le64(k + 8);
+
+        *state = (struct siphash) {
+                /* "somepseudorandomlygeneratedbytes" */
+                .v0 = 0x736f6d6570736575ULL ^ k0,
+                .v1 = 0x646f72616e646f6dULL ^ k1,
+                .v2 = 0x6c7967656e657261ULL ^ k0,
+                .v3 = 0x7465646279746573ULL ^ k1,
+                .padding = 0,
+                .inlen = 0,
+        };
+}
+
+void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
+
+        const uint8_t *in = _in;
+        const uint8_t *end = in + inlen;
+        size_t left = state->inlen & 7;
+        uint64_t m;
+
+        assert(in);
+        assert(state);
+
+        /* Update total length */
+        state->inlen += inlen;
+
+        /* If padding exists, fill it out */
+        if (left > 0) {
+                for ( ; in < end && left < 8; in ++, left ++)
+                        state->padding |= ((uint64_t) *in) << (left * 8);
+
+                if (in == end && left < 8)
+                        /* We did not have enough input to fill out the padding completely */
+                        return;
+
+#ifdef DEBUG
+                printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+                printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+                printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+                printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+                printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding);
+#endif
+
+                state->v3 ^= state->padding;
+                sipround(state);
+                sipround(state);
+                state->v0 ^= state->padding;
+
+                state->padding = 0;
+        }
+
+        end -= (state->inlen % sizeof(uint64_t));
+
+        for ( ; in < end; in += 8) {
+                m = unaligned_read_le64(in);
+#ifdef DEBUG
+                printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+                printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+                printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+                printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+                printf("(%3zu) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m);
+#endif
+                state->v3 ^= m;
+                sipround(state);
+                sipround(state);
+                state->v0 ^= m;
+        }
+
+        left = state->inlen & 7;
+        switch (left) {
+                case 7:
+                        state->padding |= ((uint64_t) in[6]) << 48;
+                case 6:
+                        state->padding |= ((uint64_t) in[5]) << 40;
+                case 5:
+                        state->padding |= ((uint64_t) in[4]) << 32;
+                case 4:
+                        state->padding |= ((uint64_t) in[3]) << 24;
+                case 3:
+                        state->padding |= ((uint64_t) in[2]) << 16;
+                case 2:
+                        state->padding |= ((uint64_t) in[1]) <<  8;
+                case 1:
+                        state->padding |= ((uint64_t) in[0]);
+                case 0:
+                        break;
+        }
+}
+
+uint64_t siphash24_finalize(struct siphash *state) {
+        uint64_t b;
+
+        assert(state);
+
+        b = state->padding | (((uint64_t) state->inlen) << 56);
+
+#ifdef DEBUG
+        printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+        printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+        printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+        printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+        printf("(%3zu) padding   %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding);
+#endif
+
+        state->v3 ^= b;
+        sipround(state);
+        sipround(state);
+        state->v0 ^= b;
+
+#ifdef DEBUG
+        printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+        printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+        printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+        printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+#endif
+        state->v2 ^= 0xff;
+
+        sipround(state);
+        sipround(state);
+        sipround(state);
+        sipround(state);
+
+        return state->v0 ^ state->v1 ^ state->v2  ^ state->v3;
+}
+
+uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]) {
+        struct siphash state;
+
+        assert(in);
+        assert(k);
+
+        siphash24_init(&state, k);
+        siphash24_compress(in, inlen, &state);
+
+        return siphash24_finalize(&state);
+}
diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h
new file mode 100644
index 00000000..0cc0163d
--- /dev/null
+++ b/src/systemd/src/basic/siphash24.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+struct siphash {
+        uint64_t v0;
+        uint64_t v1;
+        uint64_t v2;
+        uint64_t v3;
+        uint64_t padding;
+        size_t inlen;
+};
+
+void siphash24_init(struct siphash *state, const uint8_t k[16]);
+void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
+uint64_t siphash24_finalize(struct siphash *state);
+
+uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]);
diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h
new file mode 100644
index 00000000..d06a7645
--- /dev/null
+++ b/src/systemd/src/basic/socket-util.h
@@ -0,0 +1,141 @@
+/*-*- 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 <netinet/ether.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.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_and_warn(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 reuse_port,
+                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_port(const struct sockaddr *_sa) _pure_;
+
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, 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);
+
+int fd_inc_sndbuf(int fd, size_t n);
+int fd_inc_rcvbuf(int fd, size_t n);
+
+int ip_tos_to_string_alloc(int i, char **s);
+int ip_tos_from_string(const char *s);
+
+int getpeercred(int fd, struct ucred *ucred);
+int getpeersec(int fd, char **ret);
+
+int send_one_fd_sa(int transport_fd,
+                   int fd,
+                   const struct sockaddr *sa, socklen_t len,
+                   int flags);
+#define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags)
+int receive_one_fd(int transport_fd, int flags);
+
+#define CMSG_FOREACH(cmsg, mh)                                          \
+        for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
diff --git a/src/systemd/src/basic/sparse-endian.h b/src/systemd/src/basic/sparse-endian.h
new file mode 100644
index 00000000..02de0fec
--- /dev/null
+++ b/src/systemd/src/basic/sparse-endian.h
@@ -0,0 +1,90 @@
+/* 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 "nm-sd-adapt.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/systemd/src/basic/string-table.c b/src/systemd/src/basic/string-table.c
new file mode 100644
index 00000000..be02c2f9
--- /dev/null
+++ b/src/systemd/src/basic/string-table.c
@@ -0,0 +1,38 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include "string-table.h"
+#include "string-util.h"
+
+ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
+        size_t i;
+
+        if (!key)
+                return -1;
+
+        for (i = 0; i < len; ++i)
+                if (streq_ptr(table[i], key))
+                        return (ssize_t) i;
+
+        return -1;
+}
diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h
new file mode 100644
index 00000000..ee64d34f
--- /dev/null
+++ b/src/systemd/src/basic/string-table.h
@@ -0,0 +1,91 @@
+
+/*-*- 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 <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
+
+/* For basic lookup tables with strictly enumerated entries */
+#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(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];                                 \
+        }
+
+#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope)        \
+        scope type name##_from_string(const char *s) {                  \
+                return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
+        }
+
+#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope)                    \
+        _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope)          \
+        _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope)        \
+        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)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(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;                                                \
+                if (i < 0 || i > max)                                   \
+                        return -ERANGE;                                 \
+                if (i < (type) ELEMENTSOF(name##_table)) {              \
+                        s = strdup(name##_table[i]);                    \
+                        if (!s)                                         \
+                                return -ENOMEM;                         \
+                } else {                                                \
+                        if (asprintf(&s, "%i", i) < 0)                  \
+                                return -ENOMEM;                         \
+                }                                                       \
+                *str = s;                                               \
+                return 0;                                               \
+        }                                                               \
+        type name##_from_string(const char *s) {                        \
+                type i;                                                 \
+                unsigned u = 0;                                         \
+                if (!s)                                                 \
+                        return (type) -1;                               \
+                for (i = 0; i < (type) ELEMENTSOF(name##_table); i++)   \
+                        if (streq_ptr(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__
diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c
new file mode 100644
index 00000000..35e00af9
--- /dev/null
+++ b/src/systemd/src/basic/string-util.c
@@ -0,0 +1,813 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#if 0 /* NM_IGNORED */
+#include "gunicode.h"
+#endif /* NM_IGNORED */
+#include "macro.h"
+#include "string-util.h"
+#include "utf8.h"
+#include "util.h"
+
+int strcmp_ptr(const char *a, const char *b) {
+
+        /* Like strcmp(), but tries to make sense of NULL pointers */
+        if (a && b)
+                return strcmp(a, b);
+
+        if (!a && b)
+                return -1;
+
+        if (a && !b)
+                return 1;
+
+        return 0;
+}
+
+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* endswith_no_case(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 (strcasecmp(s + sl - pl, postfix) != 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;
+}
+
+static size_t strcspn_escaped(const char *s, const char *reject) {
+        bool escaped = false;
+        int 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 + 1] != quotechars[0] ||
+                    (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
+                        /* right quote missing or garbage at the end */
+                        *state = current;
+                        return NULL;
+                }
+                *state = current++ + *l + 2;
+        } else if (quoted) {
+                *l = strcspn_escaped(current, separator);
+                if (current[*l] && !strchr(separator, current[*l])) {
+                        /* unfinished escape */
+                        *state = current;
+                        return NULL;
+                }
+                *state = current + *l;
+        } else {
+                *l = strcspn(current, separator);
+                *state = current + *l;
+        }
+
+        return current;
+}
+
+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);
+}
+
+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;
+}
+
+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;
+}
+
+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 *truncate_nl(char *s) {
+        assert(s);
+
+        s[strcspn(s, NEWLINE)] = 0;
+        return s;
+}
+
+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;
+}
+
+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;
+}
+
+bool string_has_cc(const char *p, const char *ok) {
+        const char *t;
+
+        assert(p);
+
+        /*
+         * Check if a string contains control characters. If 'ok' is
+         * non-NULL it may be a string containing additional CCs to be
+         * considered OK.
+         */
+
+        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 */
+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);
+}
+#endif /* NM_IGNORED */
+
+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;
+}
+
+char* strshorten(char *s, size_t l) {
+        assert(s);
+
+        if (l < strlen(s))
+                s[l] = 0;
+
+        return s;
+}
+
+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;
+}
+
+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;
+}
+
+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 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 (streq_ptr(*p, s))
+                return 0;
+
+        if (s) {
+                t = strdup(s);
+                if (!t)
+                        return -ENOMEM;
+        } else
+                t = NULL;
+
+        free(*p);
+        *p = t;
+
+        return 1;
+}
+
+#pragma GCC push_options
+#pragma GCC optimize("O0")
+
+void* memory_erase(void *p, size_t l) {
+        volatile uint8_t* x = (volatile uint8_t*) p;
+
+        /* This basically does what memset() does, but hopefully isn't
+         * optimized away by the compiler. One of those days, when
+         * glibc learns memset_s() we should replace this call by
+         * memset_s(), but until then this has to do. */
+
+        for (; l > 0; l--)
+                *(x++) = 'x';
+
+        return p;
+}
+
+#pragma GCC pop_options
+
+char* string_erase(char *x) {
+
+        if (!x)
+                return NULL;
+
+        /* A delicious drop of snake-oil! To be called on memory where
+         * we stored passphrases or so, after we used them. */
+
+        return memory_erase(x, strlen(x));
+}
+
+char *string_free_erase(char *s) {
+        return mfree(string_erase(s));
+}
+
+bool string_is_safe(const char *p) {
+        const char *t;
+
+        if (!p)
+                return false;
+
+        for (t = p; *t; t++) {
+                if (*t > 0 && *t < ' ') /* no control characters */
+                        return false;
+
+                if (strchr(QUOTES "\\\x7f", *t))
+                        return false;
+        }
+
+        return true;
+}
diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h
new file mode 100644
index 00000000..f6aa4584
--- /dev/null
+++ b/src/systemd/src/basic/string-util.h
@@ -0,0 +1,188 @@
+/*-*- 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 <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "macro.h"
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE        " \t\n\r"
+#define NEWLINE           "\n\r"
+#define QUOTES            "\"\'"
+#define COMMENTS          "#;"
+#define GLOB_CHARS        "*?["
+#define DIGITS            "0123456789"
+#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
+#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define LETTERS           LOWERCASE_LETTERS UPPERCASE_LETTERS
+#define ALPHANUMERICAL    LETTERS DIGITS
+
+#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)
+
+int strcmp_ptr(const char *a, const char *b) _pure_;
+
+static inline bool streq_ptr(const char *a, const char *b) {
+        return strcmp_ptr(a, b) == 0;
+}
+
+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 *endswith_no_case(const char *s, const char *postfix) _pure_;
+
+char *first_word(const char *s, const char *word) _pure_;
+
+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)))
+
+char *strappend(const char *s, const char *suffix);
+char *strnappend(const char *s, const char *suffix, size_t length);
+
+char *strjoin(const char *x, ...) _sentinel_;
+
+#define strjoina(a, ...)                                                \
+        ({                                                              \
+                const char *_appendees_[] = { a, __VA_ARGS__ };         \
+                char *_d_, *_p_;                                        \
+                int _len_ = 0;                                          \
+                unsigned _i_;                                           \
+                for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
+                        _len_ += strlen(_appendees_[_i_]);              \
+                _p_ = _d_ = alloca(_len_ + 1);                          \
+                for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
+                        _p_ = stpcpy(_p_, _appendees_[_i_]);            \
+                *_p_ = 0;                                               \
+                _d_;                                                    \
+        })
+
+char *strstrip(char *s);
+char *delete_chars(char *s, const char *bad);
+char *truncate_nl(char *s);
+
+char *ascii_strlower(char *path);
+
+bool chars_intersect(const char *a, const char *b) _pure_;
+
+static inline bool _pure_ in_charset(const char *s, const char* charset) {
+        assert(s);
+        assert(charset);
+        return s[strspn(s, charset)] == '\0';
+}
+
+bool string_has_cc(const char *p, const char *ok) _pure_;
+
+char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent);
+char *ellipsize(const char *s, size_t length, unsigned percent);
+
+bool nulstr_contains(const char*nulstr, const char *needle);
+
+char* strshorten(char *s, size_t l);
+
+char *strreplace(const char *text, const char *old_string, const char *new_string);
+
+char *strip_tab_ansi(char **p, size_t *l);
+
+char *strextend(char **x, ...) _sentinel_;
+
+char *strrep(const char *s, unsigned n);
+
+int split_pair(const char *s, const char *sep, char **l, char **r);
+
+int free_and_strdup(char **p, const char *s);
+
+/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
+static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+
+        if (needlelen <= 0)
+                return (void*) haystack;
+
+        if (haystacklen < needlelen)
+                return NULL;
+
+        assert(haystack);
+        assert(needle);
+
+        return memmem(haystack, haystacklen, needle, needlelen);
+}
+
+void* memory_erase(void *p, size_t l);
+char *string_erase(char *x);
+
+char *string_free_erase(char *s);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase);
+#define _cleanup_string_free_erase_ _cleanup_(string_free_erasep)
+
+bool string_is_safe(const char *p) _pure_;
diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c
new file mode 100644
index 00000000..38624902
--- /dev/null
+++ b/src/systemd/src/basic/strv.c
@@ -0,0 +1,879 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "escape.h"
+#if 0 /* NM_IGNORED */
+#include "extract-word.h"
+#endif /* NM_IGNORED */
+#include "string-util.h"
+#include "strv.h"
+#include "util.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_clear(char **l) {
+        char **k;
+
+        if (!l)
+                return;
+
+        for (k = l; *k; k++)
+                free(*k);
+
+        *l = NULL;
+}
+
+char **strv_free(char **l) {
+        strv_clear(l);
+        free(l);
+        return NULL;
+}
+
+char **strv_free_erase(char **l) {
+        char **i;
+
+        STRV_FOREACH(i, l)
+                string_erase(*i);
+
+        return strv_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, bool filter_duplicates) {
+        char **s, **t;
+        size_t p, q, i = 0, j;
+
+        assert(a);
+
+        if (strv_isempty(b))
+                return 0;
+
+        p = strv_length(*a);
+        q = strv_length(b);
+
+        t = realloc(*a, sizeof(char*) * (p + q + 1));
+        if (!t)
+                return -ENOMEM;
+
+        t[p] = NULL;
+        *a = t;
+
+        STRV_FOREACH(s, b) {
+
+                if (filter_duplicates && strv_contains(t, *s))
+                        continue;
+
+                t[p+i] = strdup(*s);
+                if (!t[p+i])
+                        goto rollback;
+
+                i++;
+                t[p+i] = NULL;
+        }
+
+        assert(i <= q);
+
+        return (int) i;
+
+rollback:
+        for (j = 0; j < i; j++)
+                free(t[p + j]);
+
+        t[p] = NULL;
+        return -ENOMEM;
+}
+
+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]))
+                l[n - 1] = mfree(l[n - 1]);
+
+        return l;
+}
+
+#if 0 /* NM_IGNORED */
+int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
+        _cleanup_strv_free_ char **l = NULL;
+        size_t n = 0, allocated = 0;
+        int r;
+
+        assert(t);
+        assert(s);
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = extract_first_word(&s, &word, separators, flags);
+                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);
+                if (!l)
+                        return -ENOMEM;
+        }
+
+        *t = l;
+        l = NULL;
+
+        return (int) n;
+}
+#endif /* NM_IGNORED */
+
+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_pair(char ***l, char *a, char *b) {
+        char **c;
+        unsigned n, m;
+
+        if (!a && !b)
+                return 0;
+
+        n = strv_length(*l);
+
+        /* increase and check for overflow */
+        m = n + !!a + !!b + 1;
+        if (m < n)
+                return -ENOMEM;
+
+        c = realloc_multiply(*l, sizeof(char*), m);
+        if (!c)
+                return -ENOMEM;
+
+        if (a)
+                c[n++] = a;
+        if (b)
+                c[n++] = b;
+        c[n] = 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_pair(char ***l, char *a, char *b) {
+        int r;
+
+        r = strv_push_pair(l, a, b);
+        if (r < 0) {
+                free(a);
+                free(b);
+        }
+
+        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;
+}
+
+bool strv_is_uniq(char **l) {
+        char **i;
+
+        STRV_FOREACH(i, l)
+                if (strv_find(i+1, *i))
+                        return false;
+
+        return true;
+}
+
+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;
+}
+
+int strv_make_nulstr(char **l, char **p, size_t *q) {
+        size_t n_allocated = 0, n = 0;
+        _cleanup_free_ char *m = NULL;
+        char **i;
+
+        assert(p);
+        assert(q);
+
+        STRV_FOREACH(i, l) {
+                size_t z;
+
+                z = strlen(*i);
+
+                if (!GREEDY_REALLOC(m, n_allocated, n + z + 1))
+                        return -ENOMEM;
+
+                memcpy(m + n, *i, z + 1);
+                n += z + 1;
+        }
+
+        if (!m) {
+                m = new0(char, 1);
+                if (!m)
+                        return -ENOMEM;
+                n = 0;
+        }
+
+        *p = m;
+        *q = n;
+
+        m = NULL;
+
+        return 0;
+}
+
+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;
+}
+
+bool strv_equal(char **a, char **b) {
+
+        if (strv_isempty(a))
+                return strv_isempty(b);
+
+        if (strv_isempty(b))
+                return false;
+
+        for ( ; *a || *b; ++a, ++b)
+                if (!streq_ptr(*a, *b))
+                        return false;
+
+        return true;
+}
+
+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);
+}
+
+char **strv_reverse(char **l) {
+        unsigned n, i;
+
+        n = strv_length(l);
+        if (n <= 1)
+                return l;
+
+        for (i = 0; i < n / 2; i++) {
+                char *t;
+
+                t = l[i];
+                l[i] = l[n-1-i];
+                l[n-1-i] = t;
+        }
+
+        return l;
+}
+
+char **strv_shell_escape(char **l, const char *bad) {
+        char **s;
+
+        /* Escapes every character in every string in l that is in bad,
+         * edits in-place, does not roll-back on error. */
+
+        STRV_FOREACH(s, l) {
+                char *v;
+
+                v = shell_escape(*s, bad);
+                if (!v)
+                        return NULL;
+
+                free(*s);
+                *s = v;
+        }
+
+        return l;
+}
+
+bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
+        char* const* p;
+
+        STRV_FOREACH(p, patterns)
+                if (fnmatch(*p, s, 0) == 0)
+                        return true;
+
+        return false;
+}
+
+char ***strv_free_free(char ***l) {
+        char ***i;
+
+        if (!l)
+                return NULL;
+
+        for (i = l; *i; i++)
+                strv_free(*i);
+
+        free(l);
+        return NULL;
+}
+
+char **strv_skip(char **l, size_t n) {
+
+        while (n > 0) {
+                if (strv_isempty(l))
+                        return l;
+
+                l++, n--;
+        }
+
+        return l;
+}
+
+int strv_extend_n(char ***l, const char *value, size_t n) {
+        size_t i, j, k;
+        char **nl;
+
+        assert(l);
+
+        if (!value)
+                return 0;
+        if (n == 0)
+                return 0;
+
+        /* Adds the value value n times to l */
+
+        k = strv_length(*l);
+
+        nl = realloc(*l, sizeof(char*) * (k + n + 1));
+        if (!nl)
+                return -ENOMEM;
+
+        *l = nl;
+
+        for (i = k; i < k + n; i++) {
+                nl[i] = strdup(value);
+                if (!nl[i])
+                        goto rollback;
+        }
+
+        nl[i] = NULL;
+        return 0;
+
+rollback:
+        for (j = k; j < i; j++)
+                free(nl[j]);
+
+        nl[k] = NULL;
+        return -ENOMEM;
+}
diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h
new file mode 100644
index 00000000..d3608af3
--- /dev/null
+++ b/src/systemd/src/basic/strv.h
@@ -0,0 +1,177 @@
+/*-*- 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 <fnmatch.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "alloc-util.h"
+#if 0 /* NM_IGNORED */
+#include "extract-word.h"
+#endif /* NM_IGNORED */
+#include "macro.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_;
+
+char **strv_free(char **l);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
+#define _cleanup_strv_free_ _cleanup_(strv_freep)
+
+char **strv_free_erase(char **l);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
+#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
+
+void strv_clear(char **l);
+
+char **strv_copy(char * const *l);
+unsigned strv_length(char * const *l) _pure_;
+
+int strv_extend_strv(char ***a, char **b, bool filter_duplicates);
+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_pair(char ***l, char *a, char *b);
+int strv_push_prepend(char ***l, char *value);
+int strv_consume(char ***l, char *value);
+int strv_consume_pair(char ***l, char *a, char *b);
+int strv_consume_prepend(char ***l, char *value);
+
+char **strv_remove(char **l, const char *s);
+char **strv_uniq(char **l);
+bool strv_is_uniq(char **l);
+
+bool strv_equal(char **a, char **b);
+
+#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);
+
+#if 0 /* NM_IGNORED */
+int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
+#endif /* NM_IGNORED */
+
+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);
+int strv_make_nulstr(char **l, char **p, size_t *n);
+
+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];                                       \
+        }))
+
+char **strv_reverse(char **l);
+char **strv_shell_escape(char **l, const char *bad);
+
+bool strv_fnmatch(char* const* patterns, const char *s, int flags);
+
+static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) {
+        assert(s);
+        return strv_isempty(patterns) ||
+               strv_fnmatch(patterns, s, flags);
+}
+
+char ***strv_free_free(char ***l);
+
+char **strv_skip(char **l, size_t n);
+
+int strv_extend_n(char ***l, const char *value, size_t n);
diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c
new file mode 100644
index 00000000..8ca41f0c
--- /dev/null
+++ b/src/systemd/src/basic/time-util.c
@@ -0,0 +1,1154 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/timerfd.h>
+#include <sys/timex.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+usec_t now(clockid_t clock_id) {
+        struct timespec ts;
+
+        assert_se(clock_gettime(clock_id, &ts) == 0);
+
+        return timespec_load(&ts);
+}
+
+nsec_t now_nsec(clockid_t clock_id) {
+        struct timespec ts;
+
+        assert_se(clock_gettime(clock_id, &ts) == 0);
+
+        return timespec_load_nsec(&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;
+}
+
+dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
+        int64_t delta;
+
+        if (u == USEC_INFINITY) {
+                ts->realtime = ts->monotonic = USEC_INFINITY;
+                return ts;
+        }
+        ts->realtime = now(CLOCK_REALTIME);
+        ts->monotonic = now(CLOCK_MONOTONIC);
+
+        delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
+
+        if ((int64_t) ts->realtime > delta)
+                ts->realtime -= delta;
+        else
+                ts->realtime = 0;
+
+        if ((int64_t) ts->monotonic > delta)
+                ts->monotonic -= delta;
+        else
+                ts->monotonic = 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;
+}
+
+nsec_t timespec_load_nsec(const struct timespec *ts) {
+        assert(ts);
+
+        if (ts->tv_sec == (time_t) -1 &&
+            ts->tv_nsec == (long) -1)
+                return NSEC_INFINITY;
+
+        return
+                (nsec_t) ts->tv_sec * NSEC_PER_SEC +
+                (nsec_t) ts->tv_nsec;
+}
+
+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);
+        localtime_or_gmtime_r(&sec, &tm, utc);
+
+        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);
+        localtime_or_gmtime_r(&sec, &tm, utc);
+
+        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 /* NM_IGNORED */
+
+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);
+}
+
+int 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);
+                return -EINVAL;
+        }
+
+        t->realtime = a;
+        t->monotonic = b;
+
+        return 0;
+}
+
+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;
+        const char *utc;
+        struct tm tm, copy;
+        time_t x;
+        usec_t x_usec, 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);
+
+        if (t[0] == '@')
+                return parse_sec(t + 1, usec);
+
+        ret = now(CLOCK_REALTIME);
+
+        if (streq(t, "now"))
+                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 ((k = endswith(t, " ago"))) {
+                t = strndupa(t, k - t);
+
+                r = parse_sec(t, &minus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+
+        } else if ((k = endswith(t, " left"))) {
+                t = strndupa(t, k - t);
+
+                r = parse_sec(t, &plus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        }
+
+        utc = endswith_no_case(t, " UTC");
+        if (utc)
+                t = strndupa(t, utc - t);
+
+        x = ret / USEC_PER_SEC;
+        x_usec = 0;
+
+        assert_se(localtime_or_gmtime_r(&x, &tm, utc));
+        tm.tm_isdst = -1;
+
+        if (streq(t, "today")) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto from_tm;
+
+        } else if (streq(t, "yesterday")) {
+                tm.tm_mday --;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto from_tm;
+
+        } else if (streq(t, "tomorrow")) {
+                tm.tm_mday ++;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto from_tm;
+        }
+
+
+        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) {
+                if (*k == '.')
+                        goto parse_usec;
+                else if (*k == 0)
+                        goto from_tm;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+        if (k) {
+                if (*k == '.')
+                        goto parse_usec;
+                else if (*k == 0)
+                        goto from_tm;
+        }
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto from_tm;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto from_tm;
+        }
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto from_tm;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto from_tm;
+        }
+
+        tm = copy;
+        k = strptime(t, "%H:%M:%S", &tm);
+        if (k) {
+                if (*k == '.')
+                        goto parse_usec;
+                else if (*k == 0)
+                        goto from_tm;
+        }
+
+        tm = copy;
+        k = strptime(t, "%H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto from_tm;
+        }
+
+        return -EINVAL;
+
+parse_usec:
+        {
+                unsigned add;
+
+                k++;
+                r = parse_fractional_part_u(&k, 6, &add);
+                if (r < 0)
+                        return -EINVAL;
+
+                if (*k)
+                        return -EINVAL;
+
+                x_usec = add;
+
+        }
+
+from_tm:
+        x = mktime_or_timegm(&tm, utc);
+        if (x == (time_t) -1)
+                return -EINVAL;
+
+        if (weekday >= 0 && tm.tm_wday != weekday)
+                return -EINVAL;
+
+        ret = (usec_t) x * USEC_PER_SEC + x_usec;
+
+finish:
+        ret += plus;
+        if (ret > minus)
+                ret -= minus;
+        else
+                ret = 0;
+
+        *usec = ret;
+
+        return 0;
+}
+
+int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
+
+        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  },
+                { "M",       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            },
+        };
+
+        const char *p, *s;
+        usec_t r = 0;
+        bool something = false;
+
+        assert(t);
+        assert(usec);
+        assert(default_unit > 0);
+
+        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;
+                usec_t multiplier, k;
+
+                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)) {
+                                multiplier = table[i].usec;
+                                p = e + strlen(table[i].suffix);
+                                break;
+                        }
+
+                if (i >= ELEMENTSOF(table)) {
+                        multiplier = default_unit;
+                        p = e;
+                }
+
+                something = true;
+
+                k = (usec_t) z * multiplier;
+
+                for (; n > 0; n--)
+                        k /= 10;
+
+                r += (usec_t) l * multiplier + k;
+        }
+
+        *usec = r;
+
+        return 0;
+}
+
+int parse_sec(const char *t, usec_t *usec) {
+        return parse_time(t, usec, USEC_PER_SEC);
+}
+
+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;
+}
+#endif /* NM_IGNORED */
+
+bool timezone_is_valid(const char *name) {
+        bool slash = false;
+        const char *p, *t;
+        struct stat st;
+
+        if (isempty(name))
+                return false;
+
+        if (name[0] == '/')
+                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 = strjoina("/usr/share/zoneinfo/", name);
+        if (stat(t, &st) < 0)
+                return false;
+
+        if (!S_ISREG(st.st_mode))
+                return false;
+
+        return true;
+}
+
+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;
+}
+
+#if 0 /* NM_IGNORED */
+int get_timezone(char **tz) {
+        _cleanup_free_ char *t = NULL;
+        const char *e;
+        char *z;
+        int r;
+
+        r = readlink_malloc("/etc/localtime", &t);
+        if (r < 0)
+                return r; /* returns EINVAL if not a symlink */
+
+        e = path_startswith(t, "/usr/share/zoneinfo/");
+        if (!e)
+                e = path_startswith(t, "../usr/share/zoneinfo/");
+        if (!e)
+                return -EINVAL;
+
+        if (!timezone_is_valid(e))
+                return -EINVAL;
+
+        z = strdup(e);
+        if (!z)
+                return -ENOMEM;
+
+        *tz = z;
+        return 0;
+}
+
+time_t mktime_or_timegm(struct tm *tm, bool utc) {
+        return utc ? timegm(tm) : mktime(tm);
+}
+
+struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
+        return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
+}
+
+unsigned long usec_to_jiffies(usec_t u) {
+        static thread_local unsigned long hz = 0;
+        long r;
+
+        if (hz == 0) {
+                r = sysconf(_SC_CLK_TCK);
+
+                assert(r > 0);
+                hz = (unsigned long) r;
+        }
+
+        return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h
new file mode 100644
index 00000000..760ac2a0
--- /dev/null
+++ b/src/systemd/src/basic/time-util.h
@@ -0,0 +1,131 @@
+/*-*- 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 <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+#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) { 0ULL, 0ULL })
+
+usec_t now(clockid_t clock);
+nsec_t now_nsec(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);
+dual_timestamp* dual_timestamp_from_boottime_or_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);
+
+nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
+
+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);
+int 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_time(const char *t, usec_t *usec, usec_t default_unit);
+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);
+
+#define xstrftime(buf, fmt, tm) \
+        assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \
+                          "xstrftime: " #buf "[] must be big enough")
+
+int get_timezone(char **timezone);
+
+time_t mktime_or_timegm(struct tm *tm, bool utc);
+struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
+
+unsigned long usec_to_jiffies(usec_t usec);
diff --git a/src/systemd/src/basic/umask-util.h b/src/systemd/src/basic/umask-util.h
new file mode 100644
index 00000000..aada0540
--- /dev/null
+++ b/src/systemd/src/basic/umask-util.h
@@ -0,0 +1,50 @@
+/*-*- 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 <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+static inline void umaskp(mode_t *u) {
+        umask(*u);
+}
+
+#define _cleanup_umask_ _cleanup_(umaskp)
+
+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)
diff --git a/src/systemd/src/basic/unaligned.h b/src/systemd/src/basic/unaligned.h
new file mode 100644
index 00000000..597c890d
--- /dev/null
+++ b/src/systemd/src/basic/unaligned.h
@@ -0,0 +1,115 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <endian.h>
+#include <stdint.h>
+
+/* BE */
+
+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);
+}
+
+/* LE */
+
+static inline uint16_t unaligned_read_le16(const void *_u) {
+        const uint8_t *u = _u;
+
+        return (((uint16_t) u[1]) << 8) |
+                ((uint16_t) u[0]);
+}
+
+static inline uint32_t unaligned_read_le32(const void *_u) {
+        const uint8_t *u = _u;
+
+        return (((uint32_t) unaligned_read_le16(u + 2)) << 16) |
+                ((uint32_t) unaligned_read_le16(u));
+}
+
+static inline uint64_t unaligned_read_le64(const void *_u) {
+        const uint8_t *u = _u;
+
+        return (((uint64_t) unaligned_read_le32(u + 4)) << 32) |
+                ((uint64_t) unaligned_read_le32(u));
+}
+
+static inline void unaligned_write_le16(void *_u, uint16_t a) {
+        uint8_t *u = _u;
+
+        u[0] = (uint8_t) a;
+        u[1] = (uint8_t) (a >> 8);
+}
+
+static inline void unaligned_write_le32(void *_u, uint32_t a) {
+        uint8_t *u = _u;
+
+        unaligned_write_le16(u, (uint16_t) a);
+        unaligned_write_le16(u + 2, (uint16_t) (a >> 16));
+}
+
+static inline void unaligned_write_le64(void *_u, uint64_t a) {
+        uint8_t *u = _u;
+
+        unaligned_write_le32(u, (uint32_t) a);
+        unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
+}
diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c
new file mode 100644
index 00000000..20beb6ea
--- /dev/null
+++ b/src/systemd/src/basic/utf8.c
@@ -0,0 +1,405 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "hexdecoct.h"
+#include "macro.h"
+#include "utf8.h"
+
+bool unichar_is_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 unichar_is_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 char *p;
+
+        assert(str);
+
+        for (p = str; length;) {
+                int encoded_len, val;
+
+                encoded_len = utf8_encoded_valid_unichar(p);
+                if (encoded_len < 0 ||
+                    (size_t) encoded_len > length)
+                        return false;
+
+                val = utf8_encoded_to_unichar(p);
+                if (val < 0 ||
+                    unichar_is_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 = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
+                        str += 1;
+                }
+        }
+
+        *s = '\0';
+
+        return p;
+}
+
+char *utf8_escape_non_printable(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) {
+                        if (utf8_is_printable(str, len)) {
+                                s = mempcpy(s, str, len);
+                                str += len;
+                        } else {
+                                while (len > 0) {
+                                        *(s++) = '\\';
+                                        *(s++) = 'x';
+                                        *(s++) = hexchar((int) *str >> 4);
+                                        *(s++) = hexchar((int) *str);
+
+                                        str += 1;
+                                        len --;
+                                }
+                        }
+                } else {
+                        s = stpcpy(s, 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;
+}
+
+/**
+ * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
+ * @out_utf8: output buffer of at least 4 bytes or NULL
+ * @g: UCS-4 character to encode
+ *
+ * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
+ * The length of the character is returned. It is not zero-terminated! If the
+ * output buffer is NULL, only the length is returned.
+ *
+ * Returns: The length in bytes that the UTF-8 representation does or would
+ *          occupy.
+ */
+size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
+
+        if (g < (1 << 7)) {
+                if (out_utf8)
+                        out_utf8[0] = g & 0x7f;
+                return 1;
+        } else if (g < (1 << 11)) {
+                if (out_utf8) {
+                        out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
+                        out_utf8[1] = 0x80 | (g & 0x3f);
+                }
+                return 2;
+        } else if (g < (1 << 16)) {
+                if (out_utf8) {
+                        out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
+                        out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
+                        out_utf8[2] = 0x80 | (g & 0x3f);
+                }
+                return 3;
+        } else if (g < (1 << 21)) {
+                if (out_utf8) {
+                        out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
+                        out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
+                        out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
+                        out_utf8[3] = 0x80 | (g & 0x3f);
+                }
+                return 4;
+        }
+
+        return 0;
+}
+
+char *utf16_to_utf8(const void *s, size_t length) {
+        const uint8_t *f;
+        char *r, *t;
+
+        r = new(char, (length * 4 + 1) / 2 + 1);
+        if (!r)
+                return NULL;
+
+        f = s;
+        t = r;
+
+        while (f < (const uint8_t*) s + length) {
+                uint16_t w1, w2;
+
+                /* see RFC 2781 section 2.2 */
+
+                w1 = f[1] << 8 | f[0];
+                f += 2;
+
+                if (!utf16_is_surrogate(w1)) {
+                        t += utf8_encode_unichar(t, w1);
+
+                        continue;
+                }
+
+                if (utf16_is_trailing_surrogate(w1))
+                        continue;
+                else if (f >= (const uint8_t*) s + length)
+                        break;
+
+                w2 = f[1] << 8 | f[0];
+                f += 2;
+
+                if (!utf16_is_trailing_surrogate(w2)) {
+                        f -= 2;
+                        continue;
+                }
+
+                t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
+        }
+
+        *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 (!unichar_is_valid(unichar))
+                return -EINVAL;
+
+        return len;
+}
diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h
new file mode 100644
index 00000000..32172c31
--- /dev/null
+++ b/src/systemd/src/basic/utf8.h
@@ -0,0 +1,61 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "macro.h"
+
+#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
+
+bool unichar_is_valid(uint32_t c);
+
+const char *utf8_is_valid(const char *s) _pure_;
+char *ascii_is_valid(const char *s) _pure_;
+
+bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_;
+#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
+
+char *utf8_escape_invalid(const char *s);
+char *utf8_escape_non_printable(const char *str);
+
+size_t utf8_encode_unichar(char *out_utf8, uint32_t g);
+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);
+
+static inline bool utf16_is_surrogate(uint16_t c) {
+        return (0xd800 <= c && c <= 0xdfff);
+}
+
+static inline bool utf16_is_trailing_surrogate(uint16_t c) {
+        return (0xdc00 <= c && c <= 0xdfff);
+}
+
+static inline uint32_t utf16_surrogate_pair_to_unichar(uint16_t lead, uint16_t trail) {
+                return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
+}
diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c
new file mode 100644
index 00000000..43e453ee
--- /dev/null
+++ b/src/systemd/src/basic/util.c
@@ -0,0 +1,813 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <alloca.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/statfs.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#if 0 /* NM_IGNORED */
+#include "build.h"
+#include "def.h"
+#include "dirent-util.h"
+#endif /* NM_IGNORED */
+#include "fd-util.h"
+#include "fileio.h"
+#if 0 /* NM_IGNORED */
+#include "formats-util.h"
+#endif /* NM_IGNORED */
+#include "hashmap.h"
+#include "hostname-util.h"
+#include "log.h"
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#endif /* NM_IGNORED */
+#include "parse-util.h"
+#include "path-util.h"
+#if 0 /* NM_IGNORED */
+#include "process-util.h"
+#endif /* NM_IGNORED */
+#include "set.h"
+#if 0 /* NM_IGNORED */
+#include "signal-util.h"
+#include "stat-util.h"
+#endif /* NM_IGNORED */
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+#if 0 /* NM_IGNORED */
+#include "user-util.h"
+#endif /* NM_IGNORED */
+#include "util.h"
+
+/* Put this test here for a lack of better place */
+assert_cc(EAGAIN == EWOULDBLOCK);
+
+#if 0 /* NM_IGNORED */
+int saved_argc = 0;
+char **saved_argv = NULL;
+#endif /* NM_IGNORED */
+
+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;
+}
+
+#if 0 /* NM_IGNORED */
+static int do_execute(char **directories, usec_t timeout, char *argv[]) {
+        _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
+        _cleanup_set_free_free_ Set *seen = NULL;
+        char **directory;
+
+        /* We fork this all off from a child process so that we can
+         * somewhat cleanly make use of SIGALRM to set a time limit */
+
+        (void) reset_all_signal_handlers();
+        (void) reset_signal_mask();
+
+        assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+        pids = hashmap_new(NULL);
+        if (!pids)
+                return log_oom();
+
+        seen = set_new(&string_hash_ops);
+        if (!seen)
+                return log_oom();
+
+        STRV_FOREACH(directory, directories) {
+                _cleanup_closedir_ DIR *d;
+                struct dirent *de;
+
+                d = opendir(*directory);
+                if (!d) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
+                }
+
+                FOREACH_DIRENT(de, d, break) {
+                        _cleanup_free_ char *path = NULL;
+                        pid_t pid;
+                        int r;
+
+                        if (!dirent_is_file(de))
+                                continue;
+
+                        if (set_contains(seen, de->d_name)) {
+                                log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
+                                continue;
+                        }
+
+                        r = set_put_strdup(seen, de->d_name);
+                        if (r < 0)
+                                return log_oom();
+
+                        path = strjoin(*directory, "/", de->d_name, NULL);
+                        if (!path)
+                                return log_oom();
+
+                        if (null_or_empty_path(path)) {
+                                log_debug("%s is empty (a mask).", path);
+                                continue;
+                        }
+
+                        pid = fork();
+                        if (pid < 0) {
+                                log_error_errno(errno, "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);
+                                return log_error_errno(errno, "Failed to execute %s: %m", path);
+                        }
+
+                        log_debug("Spawned %s as " PID_FMT ".", path, pid);
+
+                        r = hashmap_put(pids, PID_TO_PTR(pid), path);
+                        if (r < 0)
+                                return log_oom();
+                        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_PID(hashmap_first_key(pids));
+                assert(pid > 0);
+
+                path = hashmap_remove(pids, PID_TO_PTR(pid));
+                assert(path);
+
+                wait_for_terminate_and_warn(path, pid, true);
+        }
+
+        return 0;
+}
+
+void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
+        pid_t executor_pid;
+        int r;
+        char *name;
+        char **dirs = (char**) directories;
+
+        assert(!strv_isempty(dirs));
+
+        name = basename(dirs[0]);
+        assert(!isempty(name));
+
+        /* Executes all binaries in the directories in parallel and waits
+         * for them to finish. Optionally a timeout is applied. If a file
+         * with the same name exists in more than one directory, the
+         * earliest one wins. */
+
+        executor_pid = fork();
+        if (executor_pid < 0) {
+                log_error_errno(errno, "Failed to fork: %m");
+                return;
+
+        } else if (executor_pid == 0) {
+                r = do_execute(dirs, timeout, argv);
+                _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+        }
+
+        wait_for_terminate_and_warn(name, executor_pid, true);
+}
+
+bool plymouth_running(void) {
+        return access("/run/plymouth/pid", F_OK) >= 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 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;
+}
+
+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;
+        }
+}
+
+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... */
+        (void) reset_all_signal_handlers();
+        (void) 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_errno(errno, "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);
+}
+
+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;
+}
+
+/* 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;
+}
+
+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 == ENOENT ? true : -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 (hidden_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;
+}
+
+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 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);
+
+        if (!machine_name_is_valid(machine))
+                return -EINVAL;
+
+        p = strjoina("/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 *userns_fd, int *root_fd) {
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -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 (userns_fd) {
+                const char *userns;
+
+                userns = procfs_file_alloca(pid, "ns/user");
+                usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                if (usernsfd < 0 && errno != ENOENT)
+                        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 (userns_fd)
+                *userns_fd = usernsfd;
+
+        if (root_fd)
+                *root_fd = rfd;
+
+        pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
+
+        return 0;
+}
+
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
+        if (userns_fd >= 0) {
+                /* Can't setns to your own userns, since then you could
+                 * escalate from non-root to root in your own namespace, so
+                 * check if namespaces equal before attempting to enter. */
+                _cleanup_free_ char *userns_fd_path = NULL;
+                int r;
+                if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
+                        return -ENOMEM;
+
+                r = files_same(userns_fd_path, "/proc/self/ns/user");
+                if (r < 0)
+                        return r;
+                if (r)
+                        userns_fd = -1;
+        }
+
+        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 (userns_fd >= 0)
+                if (setns(userns_fd, CLONE_NEWUSER) < 0)
+                        return -errno;
+
+        if (root_fd >= 0) {
+                if (fchdir(root_fd) < 0)
+                        return -errno;
+
+                if (chroot(".") < 0)
+                        return -errno;
+        }
+
+        return reset_uid_gid();
+}
+
+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();
+}
+
+int update_reboot_param_file(const char *param) {
+        int r = 0;
+
+        if (param) {
+                r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
+        } else
+                (void) unlink(REBOOT_PARAM_FILE);
+
+        return 0;
+}
+
+int version(void) {
+        puts(PACKAGE_STRING "\n"
+             SYSTEMD_FEATURES);
+        return 0;
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h
new file mode 100644
index 00000000..9393919d
--- /dev/null
+++ b/src/systemd/src/basic/util.h
@@ -0,0 +1,186 @@
+/*-*- 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#if 0 /* NM_IGNORED */
+#include "formats-util.h"
+#endif /* NM_IGNORED */
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#endif /* NM_IGNORED */
+#include "time-util.h"
+
+size_t page_size(void) _pure_;
+#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+
+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* one_zero(bool b) {
+        return b ? "1" : "0";
+}
+
+void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
+
+bool plymouth_running(void);
+
+bool display_is_local(const char *display) _pure_;
+int socket_from_display(const char *display, char **path);
+
+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))
+
+extern int saved_argc;
+extern char **saved_argv;
+
+bool kexec_loaded(void);
+
+int prot_from_flags(int flags) _const_;
+
+int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...);
+
+bool in_initrd(void);
+
+void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
+                 int (*compar) (const void *, const void *, void *),
+                 void *arg);
+
+/**
+ * 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, comparison_fn_t compar) {
+        if (nmemb <= 1)
+                return;
+
+        assert(base);
+        qsort(base, nmemb, size, compar);
+}
+
+int on_ac_power(void);
+
+#define memzero(x,l) (memset((x), 0, (l)))
+#define zero(x) (memzero(&(x), sizeof(x)))
+
+static inline void *mempset(void *s, int c, size_t n) {
+        memset(s, c, n);
+        return (uint8_t*)s + n;
+}
+
+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;
+}
+
+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 unsigned 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;
+}
+
+bool id128_is_valid(const char *s) _pure_;
+
+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 *userns_fd, int *root_fd);
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
+
+uint64_t physical_memory(void);
+
+int update_reboot_param_file(const char *param);
+
+int version(void);
diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c
new file mode 100644
index 00000000..0c898145
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/arp-util.c
@@ -0,0 +1,156 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Axis Communications AB. All rights reserved.
+  Copyright (C) 2015 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 <linux/filter.h>
+#include <arpa/inet.h>
+
+#include "arp-util.h"
+#include "fd-util.h"
+#include "util.h"
+
+int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
+        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(struct ether_arp), 1, 0),           /* packet >= arp packet ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),                       /* header == ethernet ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),                       /* protocol == IP ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
+                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0),          /* length == sizeof(ether_addr)? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
+                BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0),             /* length == sizeof(in_addr) ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)),  /* A <- operation */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),                      /* protocol == request ? */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),                        /* protocol == reply ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
+                /* Sender Hardware Address must be different from our own */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) 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(struct ether_arp, arp_sha)),       /* A <- 4 bytes of SHA */
+                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6),                                  /* A == 0 ? */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((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(struct ether_arp, arp_sha) + 4),   /* A <- remainder of SHA */
+                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
+                /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/
+                BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)),                                  /* A <- clients IP */
+                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)),       /* A <- SPA */
+                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* X xor A */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
+                BPF_STMT(BPF_RET + BPF_K, 65535),                                              /* return all */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)),                                  /* A <- clients IP */
+                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)),       /* A <- TPA */
+                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* X xor A */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
+                BPF_STMT(BPF_RET + BPF_K, 65535),                                              /* return all */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
+        };
+        struct sock_fprog fprog = {
+                .len = ELEMENTSOF(filter),
+                .filter = (struct sock_filter*) filter
+        };
+        union sockaddr_union link = {
+                .ll.sll_family = AF_PACKET,
+                .ll.sll_protocol = htons(ETH_P_ARP),
+                .ll.sll_ifindex = ifindex,
+                .ll.sll_halen = ETH_ALEN,
+                .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+        };
+        _cleanup_close_ int s = -1;
+        int r;
+
+        assert(ifindex > 0);
+
+        s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+        if (s < 0)
+                return -errno;
+
+        r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+        if (r < 0)
+                return -errno;
+
+        r = bind(s, &link.sa, sizeof(link.ll));
+        if (r < 0)
+                return -errno;
+
+        r = s;
+        s = -1;
+
+        return r;
+}
+
+static int arp_send_packet(int fd, int ifindex,
+                           be32_t pa, const struct ether_addr *ha,
+                           bool announce) {
+        union sockaddr_union link = {
+                .ll.sll_family = AF_PACKET,
+                .ll.sll_protocol = htons(ETH_P_ARP),
+                .ll.sll_ifindex = ifindex,
+                .ll.sll_halen = ETH_ALEN,
+                .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+        };
+        struct ether_arp arp = {
+                .ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */
+                .ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */
+                .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
+                .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
+                .ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */
+        };
+        int r;
+
+        assert(fd >= 0);
+        assert(pa != 0);
+        assert(ha);
+
+        memcpy(&arp.arp_sha, ha, ETH_ALEN);
+        memcpy(&arp.arp_tpa, &pa, sizeof(pa));
+
+        if (announce)
+                memcpy(&arp.arp_spa, &pa, sizeof(pa));
+
+        r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
+int arp_send_probe(int fd, int ifindex,
+                    be32_t pa, const struct ether_addr *ha) {
+        return arp_send_packet(fd, ifindex, pa, ha, false);
+}
+
+int arp_send_announcement(int fd, int ifindex,
+                          be32_t pa, const struct ether_addr *ha) {
+        return arp_send_packet(fd, ifindex, pa, ha, true);
+}
diff --git a/src/systemd/src/libsystemd-network/arp-util.h b/src/systemd/src/libsystemd-network/arp-util.h
new file mode 100644
index 00000000..16af8962
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/arp-util.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Axis Communications AB. 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 <netinet/if_ether.h>
+
+#include "socket-util.h"
+#include "sparse-endian.h"
+
+int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
+
+int arp_send_probe(int fd, int ifindex,
+                   be32_t pa, const struct ether_addr *ha);
+int arp_send_announcement(int fd, int ifindex,
+                          be32_t pa, const struct ether_addr *ha);
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c
new file mode 100644
index 00000000..732199de
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c
@@ -0,0 +1,116 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2015 Tom Gundersen <teg@jklmen>
+
+  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"
+
+#if 0 /* NM_IGNORED */
+#include "libudev.h"
+#endif /* NM_IGNORED */
+#include "sd-id128.h"
+
+#include "dhcp-identifier.h"
+#include "dhcp6-protocol.h"
+#include "network-internal.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#if 0 /* NM_IGNORED */
+#include "udev-util.h"
+#include "virt.h"
+#else /* NM_IGNORED */
+#include <net/if.h>
+#endif /* NM_IGNORED */
+
+#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)
+
+int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
+        sd_id128_t machine_id;
+        uint64_t hash;
+        int r;
+
+        assert(duid);
+        assert(len);
+
+        r = sd_id128_get_machine(&machine_id);
+        if (r < 0)
+                return r;
+
+        unaligned_write_be16(&duid->type, DHCP6_DUID_EN);
+        unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
+
+        *len = sizeof(duid->type) + sizeof(duid->en);
+
+        /* a bit of snake-oil perhaps, but no need to expose the machine-id
+           directly; duid->en.id might not be aligned, so we need to copy */
+        hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
+        memcpy(duid->en.id, &hash, sizeof(duid->en.id));
+
+        return 0;
+}
+
+int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) {
+#if 0 /* NM_IGNORED */
+        /* name is a pointer to memory in the udev_device struct, so must
+           have the same scope */
+        _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+#else /* NM_IGNORED */
+        char name_buf[IF_NAMESIZE];
+#endif /* NM_IGNORED */
+        const char *name = NULL;
+        uint64_t id;
+
+#if 0 /* NM_IGNORED */
+        if (detect_container() <= 0) {
+                /* not in a container, udev will be around */
+                _cleanup_udev_unref_ struct udev *udev;
+                char ifindex_str[2 + DECIMAL_STR_MAX(int)];
+
+                udev = udev_new();
+                if (!udev)
+                        return -ENOMEM;
+
+                sprintf(ifindex_str, "n%d", ifindex);
+                device = udev_device_new_from_device_id(udev, ifindex_str);
+                if (device) {
+                        if (udev_device_get_is_initialized(device) <= 0)
+                                /* not yet ready */
+                                return -EBUSY;
+
+                        name = net_get_name(device);
+                }
+        }
+#else /* NM_IGNORED */
+        name = if_indextoname(ifindex, name_buf);
+#endif /* NM_IGNORED */
+
+        if (name)
+                id = siphash24(name, strlen(name), HASH_KEY.bytes);
+        else
+                /* fall back to MAC address if no predictable name available */
+                id = siphash24(mac, mac_len, HASH_KEY.bytes);
+
+        id = htole64(id);
+
+        /* fold into 32 bits */
+        unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
+
+        return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h
new file mode 100644
index 00000000..106dba39
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h
@@ -0,0 +1,67 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2015 Tom Gundersen <teg@jklmen>
+
+  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 "sd-id128.h"
+
+#include "macro.h"
+#include "sparse-endian.h"
+#include "unaligned.h"
+
+/* 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
+
+struct duid {
+        uint16_t type;
+        union {
+                struct {
+                        /* DHCP6_DUID_LLT */
+                        uint16_t htype;
+                        uint32_t time;
+                        uint8_t haddr[0];
+                } _packed_ llt;
+                struct {
+                        /* DHCP6_DUID_EN */
+                        uint32_t pen;
+                        uint8_t id[8];
+                } _packed_ en;
+                struct {
+                        /* DHCP6_DUID_LL */
+                        int16_t htype;
+                        uint8_t haddr[0];
+                } _packed_ ll;
+                struct {
+                        /* DHCP6_DUID_UUID */
+                        sd_id128_t uuid;
+                } _packed_ uuid;
+                struct {
+                        uint8_t data[MAX_DUID_LEN];
+                } _packed_ raw;
+        };
+} _packed_;
+
+int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
+int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id);
diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h
new file mode 100644
index 00000000..09dcf7e9
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp-internal.h
@@ -0,0 +1,72 @@
+/*-*- 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 <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <stdint.h>
+
+#include "sd-dhcp-client.h"
+
+#include "dhcp-protocol.h"
+#include "socket-util.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 void *option, void *userdata);
+
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message);
+
+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);
+
+/* 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_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
+
+#define log_dhcp_client(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
new file mode 100644
index 00000000..8ce52a7d
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
@@ -0,0 +1,106 @@
+/*-*- 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 "sd-dhcp-client.h"
+
+#include "dhcp-protocol.h"
+#include "list.h"
+#include "util.h"
+
+struct sd_dhcp_route {
+        struct in_addr dst_addr;
+        struct in_addr gw_addr;
+        unsigned char dst_prefixlen;
+};
+
+struct sd_dhcp_raw_option {
+        LIST_FIELDS(struct sd_dhcp_raw_option, options);
+
+        uint8_t tag;
+        uint8_t length;
+        void *data;
+};
+
+struct sd_dhcp_lease {
+        unsigned n_ref;
+
+        /* each 0 if unset */
+        uint32_t t1;
+        uint32_t t2;
+        uint32_t lifetime;
+
+        /* each 0 if unset */
+        be32_t address;
+        be32_t server_address;
+        be32_t router;
+        be32_t next_server;
+
+        bool have_subnet_mask;
+        be32_t subnet_mask;
+
+        bool have_broadcast;
+        be32_t broadcast;
+
+        struct in_addr *dns;
+        size_t dns_size;
+
+        struct in_addr *ntp;
+        size_t ntp_size;
+
+        struct sd_dhcp_route *static_route;
+        size_t static_route_size, static_route_allocated;
+
+        uint16_t mtu; /* 0 if unset */
+
+        char *domainname;
+        char *hostname;
+        char *root_path;
+
+        void *client_id;
+        size_t client_id_len;
+
+        void *vendor_specific;
+        size_t vendor_specific_len;
+
+        char *timezone;
+
+        LIST_HEAD(struct sd_dhcp_raw_option, private_options);
+};
+
+int dhcp_lease_new(sd_dhcp_lease **ret);
+
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata);
+int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len);
+
+int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
+
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
+
+int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
+int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c
new file mode 100644
index 00000000..15a0bb7b
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp-network.c
@@ -0,0 +1,237 @@
+/***
+  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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/filter.h>
+#include <linux/if_infiniband.h>
+#include <linux/if_packet.h>
+
+#include "dhcp-internal.h"
+#include "fd-util.h"
+#include "socket-util.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/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c
new file mode 100644
index 00000000..8c359a48
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp-option.c
@@ -0,0 +1,272 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "utf8.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, char **error_message, dhcp_option_cb_t cb,
+                         void *userdata) {
+        uint8_t code, len;
+        const uint8_t *option;
+        size_t offset = 0;
+
+        while (offset < buflen) {
+                code = options[offset ++];
+
+                switch (code) {
+                case DHCP_OPTION_PAD:
+                        continue;
+
+                case DHCP_OPTION_END:
+                        return 0;
+                }
+
+                if (buflen < offset + 1)
+                        return -ENOBUFS;
+
+                len = options[offset ++];
+
+                if (buflen < offset + len)
+                        return -EINVAL;
+
+                option = &options[offset];
+
+                switch (code) {
+                case DHCP_OPTION_MESSAGE_TYPE:
+                        if (len != 1)
+                                return -EINVAL;
+
+                        if (message_type)
+                                *message_type = *option;
+
+                        break;
+
+                case DHCP_OPTION_ERROR_MESSAGE:
+                        if (len == 0)
+                                return -EINVAL;
+
+                        if (error_message) {
+                                _cleanup_free_ char *string = NULL;
+
+                                /* Accept a trailing NUL byte */
+                                if (memchr(option, 0, len - 1))
+                                        return -EINVAL;
+
+                                string = strndup((const char *) option, len);
+                                if (!string)
+                                        return -ENOMEM;
+
+                                if (!ascii_is_valid(string))
+                                        return -EINVAL;
+
+                                free(*error_message);
+                                *error_message = string;
+                                string = NULL;
+                        }
+
+                        break;
+                case DHCP_OPTION_OVERLOAD:
+                        if (len != 1)
+                                return -EINVAL;
+
+                        if (overload)
+                                *overload = *option;
+
+                        break;
+
+                default:
+                        if (cb)
+                                cb(code, len, option, userdata);
+
+                        break;
+                }
+
+                offset += len;
+        }
+
+        if (offset < buflen)
+                return -EINVAL;
+
+        return 0;
+}
+
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) {
+        _cleanup_free_ char *error_message = NULL;
+        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, &error_message, cb, userdata);
+        if (r < 0)
+                return r;
+
+        if (overload & DHCP_OVERLOAD_FILE) {
+                r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata);
+                if (r < 0)
+                        return r;
+        }
+
+        if (overload & DHCP_OVERLOAD_SNAME) {
+                r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata);
+                if (r < 0)
+                        return r;
+        }
+
+        if (message_type == 0)
+                return -ENOMSG;
+
+        if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) {
+                *_error_message = error_message;
+                error_message = NULL;
+        }
+
+        return message_type;
+}
diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c
new file mode 100644
index 00000000..88a2161d
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp-packet.c
@@ -0,0 +1,193 @@
+/***
+  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 <errno.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <string.h>
+
+#include "dhcp-internal.h"
+#include "dhcp-protocol.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/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h
new file mode 100644
index 00000000..0d80a916
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h
@@ -0,0 +1,159 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <netinet/ip.h>
+#include <netinet/udp.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_VENDOR_SPECIFIC             = 43,
+        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_ERROR_MESSAGE               = 56,
+        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_FQDN                        = 81,
+        DHCP_OPTION_NEW_POSIX_TIMEZONE          = 100,
+        DHCP_OPTION_NEW_TZDB_TIMEZONE           = 101,
+        DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121,
+        DHCP_OPTION_PRIVATE_BASE                = 224,
+        DHCP_OPTION_PRIVATE_LAST                = 254,
+        DHCP_OPTION_END                         = 255,
+};
+
+#define DHCP_MAX_FQDN_LENGTH 255
+
+enum {
+        DHCP_FQDN_FLAG_S = (1 << 0),
+        DHCP_FQDN_FLAG_O = (1 << 1),
+        DHCP_FQDN_FLAG_E = (1 << 2),
+        DHCP_FQDN_FLAG_N = (1 << 3),
+};
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
new file mode 100644
index 00000000..09cff3c4
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -0,0 +1,84 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014-2015 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 "sd-event.h"
+
+#include "list.h"
+#include "macro.h"
+#include "sparse-endian.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_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
+
+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_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
+                                struct in6_addr **addrs, size_t count,
+                                size_t *allocated);
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
+                                  char ***str_arr);
+
+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/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
new file mode 100644
index 00000000..6b51658c
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp6-lease-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) 2014 Tom Gundersen
+  Copyright (C) 2014-2015 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 "sd-dhcp6-lease.h"
+
+#include "dhcp6-internal.h"
+
+struct sd_dhcp6_lease {
+        unsigned n_ref;
+
+        uint8_t *serverid;
+        size_t serverid_len;
+        uint8_t preference;
+        bool rapid_commit;
+
+        DHCP6IA ia;
+
+        DHCP6Address *addr_iter;
+
+        struct in6_addr *dns;
+        size_t dns_count;
+        size_t dns_allocated;
+        char **domains;
+        size_t domains_count;
+        struct in6_addr *ntp;
+        size_t ntp_count;
+        size_t ntp_allocated;
+        char **ntp_fqdn;
+        size_t ntp_fqdn_count;
+};
+
+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_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
+int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
+                            size_t optlen);
+int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
+int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
+                         size_t optlen) ;
+
+int dhcp6_lease_new(sd_dhcp6_lease **ret);
diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c
new file mode 100644
index 00000000..e4883019
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp6-network.c
@@ -0,0 +1,93 @@
+/***
+  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 <errno.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <linux/if_packet.h>
+
+#include "dhcp6-internal.h"
+#include "dhcp6-protocol.h"
+#include "fd-util.h"
+#include "socket-util.h"
+
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+        union sockaddr_union src = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
+                .in6.sin6_scope_id = index,
+        };
+        _cleanup_close_ int s = -1;
+        int r, off = 0, on = 1;
+
+        assert(index > 0);
+        assert(local_address);
+
+        src.in6.sin6_addr = *local_address;
+
+        s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
+        if (s < 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 = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+        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/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
new file mode 100644
index 00000000..0d61eddb
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -0,0 +1,416 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014-2015 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 <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include "alloc-util.h"
+#include "dhcp6-internal.h"
+#include "dhcp6-protocol.h"
+#include "dns-domain.h"
+#include "sparse-endian.h"
+#include "strv.h"
+#include "unaligned.h"
+#include "util.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;
+}
+
+int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
+                                struct in6_addr **addrs, size_t count,
+                                size_t *allocated) {
+
+        if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
+                return -EINVAL;
+
+        if (!GREEDY_REALLOC(*addrs, *allocated,
+                            count * sizeof(struct in6_addr) + optlen))
+                return -ENOMEM;
+
+        memcpy(*addrs + count, optval, optlen);
+
+        count += optlen / sizeof(struct in6_addr);
+
+        return count;
+}
+
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
+        size_t pos = 0, idx = 0;
+        _cleanup_free_ char **names = NULL;
+        int r;
+
+        assert_return(optlen > 1, -ENODATA);
+        assert_return(optval[optlen - 1] == '\0', -EINVAL);
+
+        while (pos < optlen) {
+                _cleanup_free_ char *ret = NULL;
+                size_t n = 0, allocated = 0;
+                bool first = true;
+
+                for (;;) {
+                        uint8_t c;
+
+                        c = optval[pos++];
+
+                        if (c == 0)
+                                /* End of name */
+                                break;
+                        else if (c <= 63) {
+                                const char *label;
+
+                                /* Literal label */
+                                label = (const char *)&optval[pos];
+                                pos += c;
+                                if (pos > optlen)
+                                        return -EMSGSIZE;
+
+                                if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                if (first)
+                                        first = false;
+                                else
+                                        ret[n++] = '.';
+
+                                r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+                                if (r < 0)
+                                        goto fail;
+
+                                n += r;
+                                continue;
+                        } else {
+                                r = -EBADMSG;
+                                goto fail;
+                        }
+                }
+
+                if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+
+                ret[n] = 0;
+
+                r = strv_extend(&names, ret);
+                if (r < 0)
+                        goto fail;
+
+                idx++;
+        }
+
+        *str_arr = names;
+        names = NULL;
+
+        return idx;
+
+fail:
+        return r;
+}
diff --git a/src/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
new file mode 100644
index 00000000..1318c816
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
@@ -0,0 +1,152 @@
+/*-*- 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 <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_INF_TIMEOUT                       1 * USEC_PER_SEC
+#define DHCP6_INF_MAX_RT                        120 * USEC_PER_SEC
+#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_INFORMATION_REQUEST         = 1,
+        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, deprecated */
+
+        /* 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_NTP_SUBOPTION_SRV_ADDR            = 1,
+        DHCP6_NTP_SUBOPTION_MC_ADDR             = 2,
+        DHCP6_NTP_SUBOPTION_SRV_FQDN            = 3,
+};
+
+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/systemd/src/libsystemd-network/lldp-internal.c b/src/systemd/src/libsystemd-network/lldp-internal.c
new file mode 100644
index 00000000..87fecd0a
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-internal.c
@@ -0,0 +1,364 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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 "sd-lldp.h"
+
+#include "alloc-util.h"
+#include "lldp-internal.h"
+
+/* We store maximum 1K chassis entries */
+#define LLDP_MIB_MAX_CHASSIS 1024
+
+/* Maximum Ports can be attached to any chassis */
+#define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
+
+/* 10.5.5.2.2 mibUpdateObjects ()
+ * The mibUpdateObjects () procedure updates the MIB objects corresponding to
+ * the TLVs contained in the received LLDPDU for the LLDP remote system
+ * indicated by the LLDP remote systems update process defined in 10.3.5 */
+
+int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
+        lldp_neighbour_port *p;
+        uint16_t length, ttl;
+        uint8_t *data;
+        uint8_t type;
+        int r;
+
+        assert_return(c, -EINVAL);
+        assert_return(tlv, -EINVAL);
+
+        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
+        if (r < 0)
+                return r;
+
+        /* Update the packet if we already have */
+        LIST_FOREACH(port, p, c->ports) {
+
+                if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
+
+                        r = sd_lldp_packet_read_ttl(tlv, &ttl);
+                        if (r < 0)
+                                return r;
+
+                        p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
+
+                        sd_lldp_packet_unref(p->packet);
+                        p->packet = tlv;
+
+                        prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
+
+                        return 0;
+                }
+        }
+
+        return -1;
+}
+
+int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
+        lldp_neighbour_port *p, *q;
+        uint8_t *data;
+        uint16_t length;
+        uint8_t type;
+        int r;
+
+        assert_return(c, -EINVAL);
+        assert_return(tlv, -EINVAL);
+
+        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
+        if (r < 0)
+                return r;
+
+        LIST_FOREACH_SAFE(port, p, q, c->ports) {
+
+                /* Find the port */
+                if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
+                        lldp_neighbour_port_remove_and_free(p);
+                        break;
+                }
+        }
+
+        return 0;
+}
+
+int lldp_mib_add_objects(Prioq *by_expiry,
+                         Hashmap *neighbour_mib,
+                         tlv_packet *tlv) {
+        _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
+        _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
+        lldp_chassis_id chassis_id;
+        bool new_chassis = false;
+        uint8_t subtype, *data;
+        uint16_t ttl, length;
+        int r;
+
+        assert_return(by_expiry, -EINVAL);
+        assert_return(neighbour_mib, -EINVAL);
+        assert_return(tlv, -EINVAL);
+
+        r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
+        if (r < 0)
+                goto drop;
+
+        r = sd_lldp_packet_read_ttl(tlv, &ttl);
+        if (r < 0)
+                goto drop;
+
+        /* Make hash key */
+        chassis_id.type = subtype;
+        chassis_id.length = length;
+        chassis_id.data = data;
+
+        /* Try to find the Chassis */
+        c = hashmap_get(neighbour_mib, &chassis_id);
+        if (!c) {
+
+                /* Don't create chassis if ttl 0 is received . Silently drop it */
+                if (ttl == 0) {
+                        log_lldp("TTL value 0 received. Skiping Chassis creation.");
+                        goto drop;
+                }
+
+                /* Admission Control: Can we store this packet ? */
+                if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
+
+                        log_lldp("Exceeding number of chassie: %d. Dropping ...",
+                                 hashmap_size(neighbour_mib));
+                        goto drop;
+                }
+
+                r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
+                if (r < 0)
+                        goto drop;
+
+                new_chassis = true;
+
+                r = hashmap_put(neighbour_mib, &c->chassis_id, c);
+                if (r < 0)
+                        goto drop;
+
+        } else {
+
+                /* When the TTL field is set to zero, the receiving LLDP agent is notified all
+                 * system information associated with the LLDP agent/port is to be deleted */
+                if (ttl == 0) {
+                        log_lldp("TTL value 0 received . Deleting associated Port ...");
+
+                        lldp_mib_remove_objects(c, tlv);
+
+                        c = NULL;
+                        goto drop;
+                }
+
+                /* if we already have this port just update it */
+                r = lldp_mib_update_objects(c, tlv);
+                if (r >= 0) {
+                        c = NULL;
+                        return r;
+                }
+
+                /* Admission Control: Can this port attached to the existing chassis ? */
+                if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
+                        log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
+
+                        c = NULL;
+                        goto drop;
+                }
+        }
+
+        /* This is a new port */
+        r = lldp_neighbour_port_new(c, tlv, &p);
+        if (r < 0)
+                goto drop;
+
+        r = prioq_put(c->by_expiry, p, &p->prioq_idx);
+        if (r < 0)
+                goto drop;
+
+        /* Attach new port to chassis */
+        LIST_PREPEND(port, c->ports, p);
+        c->n_ref ++;
+
+        p = NULL;
+        c = NULL;
+
+        return 0;
+
+ drop:
+        sd_lldp_packet_unref(tlv);
+
+        if (new_chassis)
+                hashmap_remove(neighbour_mib, &c->chassis_id);
+
+        return r;
+}
+
+void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
+        lldp_chassis *c;
+
+        assert(p);
+        assert(p->c);
+
+        c = p->c;
+
+        prioq_remove(c->by_expiry, p, &p->prioq_idx);
+
+        LIST_REMOVE(port, c->ports, p);
+        lldp_neighbour_port_free(p);
+
+        /* Drop the Chassis if no port is attached  */
+        c->n_ref --;
+        if (c->n_ref <= 1) {
+                hashmap_remove(c->neighbour_mib, &c->chassis_id);
+                lldp_chassis_free(c);
+        }
+}
+
+void lldp_neighbour_port_free(lldp_neighbour_port *p) {
+
+        if(!p)
+                return;
+
+        sd_lldp_packet_unref(p->packet);
+
+        free(p->data);
+        free(p);
+}
+
+int lldp_neighbour_port_new(lldp_chassis *c,
+                            tlv_packet *tlv,
+                            lldp_neighbour_port **ret) {
+        _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
+        uint16_t length, ttl;
+        uint8_t *data;
+        uint8_t type;
+        int r;
+
+        assert(tlv);
+
+        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
+        if (r < 0)
+                return r;
+
+        r = sd_lldp_packet_read_ttl(tlv, &ttl);
+        if (r < 0)
+                return r;
+
+        p = new0(lldp_neighbour_port, 1);
+        if (!p)
+                return -ENOMEM;
+
+        p->c = c;
+        p->type = type;
+        p->length = length;
+        p->packet = tlv;
+        p->prioq_idx = PRIOQ_IDX_NULL;
+        p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
+
+        p->data = memdup(data, length);
+        if (!p->data)
+                return -ENOMEM;
+
+        *ret = p;
+        p = NULL;
+
+        return 0;
+}
+
+void lldp_chassis_free(lldp_chassis *c) {
+
+        if (!c)
+                return;
+
+        if (c->n_ref > 1)
+                return;
+
+        free(c->chassis_id.data);
+        free(c);
+}
+
+int lldp_chassis_new(tlv_packet *tlv,
+                     Prioq *by_expiry,
+                     Hashmap *neighbour_mib,
+                     lldp_chassis **ret) {
+        _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
+        uint16_t length;
+        uint8_t *data;
+        uint8_t type;
+        int r;
+
+        assert(tlv);
+
+        r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
+        if (r < 0)
+                return r;
+
+        c = new0(lldp_chassis, 1);
+        if (!c)
+                return -ENOMEM;
+
+        c->n_ref = 1;
+        c->chassis_id.type = type;
+        c->chassis_id.length = length;
+
+        c->chassis_id.data = memdup(data, length);
+        if (!c->chassis_id.data)
+                return -ENOMEM;
+
+        LIST_HEAD_INIT(c->ports);
+
+        c->by_expiry = by_expiry;
+        c->neighbour_mib = neighbour_mib;
+
+        *ret = c;
+        c = NULL;
+
+        return 0;
+}
+
+int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(sd_lldp_packet_unrefp) tlv_packet *packet = NULL;
+        tlv_packet *p;
+        uint16_t length;
+        int r;
+
+        assert(fd);
+        assert(userdata);
+
+        r = tlv_packet_new(&packet);
+        if (r < 0)
+                return r;
+
+        length = read(fd, &packet->pdu, sizeof(packet->pdu));
+
+        /* Silently drop the packet */
+        if ((size_t) length > ETHER_MAX_LEN)
+                return 0;
+
+        packet->userdata = userdata;
+
+        p = packet;
+        packet = NULL;
+
+        return lldp_handle_packet(p, (uint16_t) length);
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h
new file mode 100644
index 00000000..5496fae7
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-internal.h
@@ -0,0 +1,95 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include "sd-event.h"
+
+#include "list.h"
+#include "lldp-tlv.h"
+#include "log.h"
+#include "prioq.h"
+
+typedef struct lldp_neighbour_port lldp_neighbour_port;
+typedef struct lldp_chassis lldp_chassis;
+typedef struct lldp_chassis_id lldp_chassis_id;
+typedef struct lldp_agent_statistics lldp_agent_statistics;
+
+struct lldp_neighbour_port {
+        uint8_t type;
+        uint8_t *data;
+
+        uint16_t length;
+        usec_t until;
+
+        unsigned prioq_idx;
+
+        lldp_chassis *c;
+        tlv_packet *packet;
+
+        LIST_FIELDS(lldp_neighbour_port, port);
+};
+
+int lldp_neighbour_port_new(lldp_chassis *c, tlv_packet *tlv, lldp_neighbour_port **ret);
+void lldp_neighbour_port_free(lldp_neighbour_port *p);
+void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_neighbour_port *, lldp_neighbour_port_free);
+#define _cleanup_lldp_neighbour_port_free_ _cleanup_(lldp_neighbour_port_freep)
+
+struct lldp_chassis_id {
+        uint8_t type;
+        uint16_t length;
+
+        uint8_t *data;
+};
+
+struct lldp_chassis {
+        unsigned n_ref;
+
+        lldp_chassis_id chassis_id;
+
+        Prioq *by_expiry;
+        Hashmap *neighbour_mib;
+
+        LIST_HEAD(lldp_neighbour_port, ports);
+};
+
+int lldp_chassis_new(tlv_packet *tlv,
+                     Prioq *by_expiry,
+                     Hashmap *neighbour_mib,
+                     lldp_chassis **ret);
+
+void lldp_chassis_free(lldp_chassis *c);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_chassis *, lldp_chassis_free);
+#define _cleanup_lldp_chassis_free_ _cleanup_(lldp_chassis_freep)
+
+int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv);
+int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv);
+int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv);
+
+int lldp_handle_packet(tlv_packet *m, uint16_t length);
+int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
+#define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c
new file mode 100644
index 00000000..e7f575a0
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-network.c
@@ -0,0 +1,87 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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 <linux/filter.h>
+#include <linux/if_ether.h>
+
+#include "fd-util.h"
+#include "lldp-internal.h"
+#include "lldp-network.h"
+#include "lldp-tlv.h"
+#include "socket-util.h"
+
+int lldp_network_bind_raw_socket(int ifindex) {
+        typedef struct LLDPFrame {
+                struct ethhdr hdr;
+                uint8_t tlvs[0];
+        } LLDPFrame;
+
+        struct sock_filter filter[] = {
+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest)),      /* A <- 4 bytes of destination MAC */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0),                    /* A != 01:80:c2:00 */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest) + 4),  /* A <- remaining 2 bytes of destination MAC */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0),                        /* A != 00:00 */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0),                        /* A != 00:03 */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0),                        /* A != 00:0e */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_proto)),     /* A <- protocol */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0),                /* A != ETHERTYPE_LLDP */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
+                BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1),                                 /* accept packet */
+        };
+
+        struct sock_fprog fprog = {
+                .len = ELEMENTSOF(filter),
+                .filter = filter
+        };
+
+        _cleanup_close_ int s = -1;
+
+        union sockaddr_union saddrll = {
+                .ll.sll_family = AF_PACKET,
+                .ll.sll_ifindex = ifindex,
+        };
+
+        int r;
+
+        assert(ifindex > 0);
+
+        s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+        if (s < 0)
+                return -errno;
+
+        r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+        if (r < 0)
+                return -errno;
+
+        r = bind(s, &saddrll.sa, sizeof(saddrll.ll));
+        if (r < 0)
+                return -errno;
+
+        r = s;
+        s = -1;
+
+        return r;
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-network.h b/src/systemd/src/libsystemd-network/lldp-network.h
new file mode 100644
index 00000000..b1e428e7
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-network.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include "sd-event.h"
+
+int lldp_network_bind_raw_socket(int ifindex);
diff --git a/src/systemd/src/libsystemd-network/lldp-port.c b/src/systemd/src/libsystemd-network/lldp-port.c
new file mode 100644
index 00000000..7374f18d
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-port.c
@@ -0,0 +1,120 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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 "alloc-util.h"
+#include "async.h"
+#include "lldp-internal.h"
+#include "lldp-network.h"
+#include "lldp-port.h"
+
+int lldp_port_start(lldp_port *p) {
+        int r;
+
+        assert_return(p, -EINVAL);
+
+        r = lldp_network_bind_raw_socket(p->ifindex);
+        if (r < 0)
+                return r;
+
+        p->rawfd = r;
+
+        r = sd_event_add_io(p->event, &p->lldp_port_rx,
+                            p->rawfd, EPOLLIN, lldp_receive_packet, p);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to allocate event source: %m");
+                goto fail;
+        }
+
+        r = sd_event_source_set_priority(p->lldp_port_rx, p->event_priority);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to set event priority: %m");
+                goto fail;
+        }
+
+        r = sd_event_source_set_description(p->lldp_port_rx, "lldp-port-rx");
+        if (r < 0) {
+                log_debug_errno(r, "Failed to set event name: %m");
+                goto fail;
+        }
+
+        return 0;
+
+fail:
+        lldp_port_stop(p);
+
+        return r;
+}
+
+int lldp_port_stop(lldp_port *p) {
+
+        assert_return(p, -EINVAL);
+
+        p->rawfd = asynchronous_close(p->rawfd);
+        p->lldp_port_rx = sd_event_source_unref(p->lldp_port_rx);
+
+        return 0;
+}
+
+void lldp_port_free(lldp_port *p) {
+        if (!p)
+                return;
+
+        lldp_port_stop(p);
+
+        free(p->ifname);
+        free(p);
+}
+
+int lldp_port_new(int ifindex,
+                  const char *ifname,
+                  const struct ether_addr *addr,
+                  void *userdata,
+                  lldp_port **ret) {
+        _cleanup_free_ lldp_port *p = NULL;
+
+        assert_return(ifindex, -EINVAL);
+        assert_return(ifname, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        p = new0(lldp_port, 1);
+        if (!p)
+                return -ENOMEM;
+
+        p->rawfd = -1;
+        p->ifindex = ifindex;
+
+        p->ifname = strdup(ifname);
+        if (!p->ifname)
+                return -ENOMEM;
+
+        memcpy(&p->mac, addr, ETH_ALEN);
+
+        p->userdata = userdata;
+
+        *ret = p;
+
+        p = NULL;
+
+        return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-port.h b/src/systemd/src/libsystemd-network/lldp-port.h
new file mode 100644
index 00000000..063eaa27
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-port.h
@@ -0,0 +1,73 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+#include "sd-lldp.h"
+
+#include "util.h"
+
+typedef struct lldp_port lldp_port;
+
+typedef enum LLDPPortStatus {
+        LLDP_PORT_STATUS_NONE,
+        LLDP_PORT_STATUS_ENABLED,
+        LLDP_PORT_STATUS_DISABLED,
+        _LLDP_PORT_STATUS_MAX,
+        _LLDP_PORT_STATUS_INVALID = -1,
+} LLDPPortStatus;
+
+struct lldp_port {
+        LLDPPortStatus status;
+
+        int ifindex;
+        char *ifname;
+
+        struct ether_addr mac;
+
+        int rawfd;
+
+        sd_event *event;
+        sd_event_source *lldp_port_rx;
+
+        int event_priority;
+
+        void *userdata;
+};
+
+int lldp_port_new(int ifindex,
+                  const char *ifname,
+                  const struct ether_addr *addr,
+                  void *userdata,
+                  lldp_port **ret);
+void lldp_port_free(lldp_port *p);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_port*, lldp_port_free);
+#define _cleanup_lldp_port_free_ _cleanup_(lldp_port_freep)
+
+int lldp_port_start(lldp_port *p);
+int lldp_port_stop(lldp_port *p);
diff --git a/src/systemd/src/libsystemd-network/lldp-tlv.c b/src/systemd/src/libsystemd-network/lldp-tlv.c
new file mode 100644
index 00000000..b589402f
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-tlv.c
@@ -0,0 +1,642 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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 <arpa/inet.h>
+#include <net/ethernet.h>
+
+#include "alloc-util.h"
+#include "lldp-tlv.h"
+#include "macro.h"
+
+int tlv_section_new(tlv_section **ret) {
+        tlv_section *s;
+
+        s = new0(tlv_section, 1);
+        if (!s)
+                return -ENOMEM;
+
+        *ret = s;
+
+        return 0;
+}
+
+void tlv_section_free(tlv_section *m) {
+
+        if (!m)
+                return;
+
+        free(m);
+}
+
+int tlv_packet_new(tlv_packet **ret) {
+        tlv_packet *m;
+
+        m = new0(tlv_packet, 1);
+        if (!m)
+                return -ENOMEM;
+
+        LIST_HEAD_INIT(m->sections);
+        m->n_ref = 1;
+
+        *ret = m;
+
+        return 0;
+}
+
+tlv_packet *sd_lldp_packet_ref(tlv_packet *m) {
+
+        if (!m)
+                return NULL;
+
+        assert(m->n_ref > 0);
+        m->n_ref++;
+
+        return m;
+}
+
+tlv_packet *sd_lldp_packet_unref(tlv_packet *m) {
+        tlv_section *s, *n;
+
+        if (!m)
+                return NULL;
+
+        assert(m->n_ref > 0);
+        m->n_ref--;
+
+        if (m->n_ref > 0)
+                return m;
+
+        LIST_FOREACH_SAFE(section, s, n, m->sections)
+                tlv_section_free(s);
+
+        free(m);
+        return NULL;
+}
+
+int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
+        uint8_t *p;
+
+        assert_return(m, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(data_length, -EINVAL);
+
+        if (m->length + data_length > ETHER_MAX_LEN)
+                return -ENOMEM;
+
+        p = m->pdu + m->length;
+        memcpy(p, data, data_length);
+        m->length += data_length;
+
+        return 0;
+}
+
+int tlv_packet_append_u8(tlv_packet *m, uint8_t data) {
+
+        assert_return(m, -EINVAL);
+
+        return tlv_packet_append_bytes(m, &data, sizeof(uint8_t));
+}
+
+int tlv_packet_append_u16(tlv_packet *m, uint16_t data) {
+        uint16_t type;
+
+        assert_return(m, -EINVAL);
+
+        type = htons(data);
+
+        return tlv_packet_append_bytes(m, &type, sizeof(uint16_t));
+}
+
+int tlv_packet_append_u32(tlv_packet *m, uint32_t data) {
+        uint32_t type;
+
+        assert_return(m, -EINVAL);
+
+        type = htonl(data);
+
+        return tlv_packet_append_bytes(m, &type, sizeof(uint32_t));
+}
+
+int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size) {
+
+        assert_return(m, -EINVAL);
+
+        return tlv_packet_append_bytes(m, data, size);
+}
+
+int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type) {
+
+        assert_return(m, -EINVAL);
+
+        m->container_pos = m->pdu + m->length;
+
+        return tlv_packet_append_u16(m, type << 9);
+}
+
+int lldp_tlv_packet_close_container(tlv_packet *m) {
+        uint16_t type;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->container_pos, -EINVAL);
+
+        memcpy(&type, m->container_pos, sizeof(uint16_t));
+
+        type |= htons(((m->pdu + m->length) - (m->container_pos + 2)) & 0x01ff);
+        memcpy(m->container_pos, &type, sizeof(uint16_t));
+
+        return 0;
+}
+
+static inline int tlv_packet_read_internal(tlv_section *m, void **data) {
+
+        assert_return(m->read_pos, -EINVAL);
+
+        *data = m->read_pos;
+
+        return 0;
+}
+
+int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
+        void *val = NULL;
+        int r;
+
+        assert_return(m, -EINVAL);
+
+        r = tlv_packet_read_internal(m->container,  &val);
+        if (r < 0)
+                return r;
+
+        memcpy(data, val, sizeof(uint8_t));
+
+        m->container->read_pos ++;
+
+        return 0;
+}
+
+int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) {
+        uint16_t t;
+        void *val = NULL;
+        int r;
+
+        assert_return(m, -EINVAL);
+
+        r = tlv_packet_read_internal(m->container, &val);
+        if (r < 0)
+                return r;
+
+        memcpy(&t, val, sizeof(uint16_t));
+        *data = ntohs(t);
+
+        m->container->read_pos += 2;
+
+        return 0;
+}
+
+int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) {
+        uint32_t t;
+        void *val;
+        int r;
+
+        assert_return(m, -EINVAL);
+
+        r = tlv_packet_read_internal(m->container, &val);
+        if (r < 0)
+                return r;
+
+        memcpy(&t, val, sizeof(uint32_t));
+        *data = ntohl(t);
+
+        m->container->read_pos += 4;
+
+        return r;
+}
+
+int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
+        void *val = NULL;
+        int r;
+
+        assert_return(m, -EINVAL);
+
+        r = tlv_packet_read_internal(m->container, &val);
+        if (r < 0)
+                return r;
+
+        *data = (char *) val;
+        *data_length = m->container->data + m->container->length - m->container->read_pos;
+
+        m->container->read_pos += *data_length;
+
+        return 0;
+}
+
+int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) {
+        void *val = NULL;
+        int r;
+
+        assert_return(m, -EINVAL);
+
+        r = tlv_packet_read_internal(m->container, &val);
+        if (r < 0)
+                return r;
+
+        *data = (uint8_t *) val;
+        *data_length = m->container->data + m->container->length - m->container->read_pos;
+
+        m->container->read_pos += *data_length;
+
+        return 0;
+}
+
+/* parse raw TLV packet */
+int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
+        tlv_section *section, *tail;
+        uint16_t t, l;
+        uint8_t *p;
+        int r;
+
+        assert_return(m, -EINVAL);
+        assert_return(size, -EINVAL);
+
+        p = m->pdu;
+
+        /* extract Ethernet header */
+        memcpy(&m->mac, p, ETH_ALEN);
+        p += sizeof(struct ether_header);
+
+        for (l = 0; l <= size; ) {
+                r = tlv_section_new(&section);
+                if (r < 0)
+                        return r;
+
+                memcpy(&t, p, sizeof(uint16_t));
+
+                section->type = ntohs(t) >> 9;
+                section->length = ntohs(t) & 0x01ff;
+
+                if (section->type == LLDP_TYPE_END || section->type >=_LLDP_TYPE_MAX) {
+                        tlv_section_free(section);
+                        break;
+                }
+
+                p += 2;
+
+                if (section->type == LLDP_TYPE_PRIVATE &&
+                    section->length >= LLDP_OUI_LEN + 1) {
+                        section->oui = p;
+                        p += LLDP_OUI_LEN;
+                        section->subtype = *p++;
+
+                        section->length -= LLDP_OUI_LEN + 1;
+                        l += LLDP_OUI_LEN + 1;
+                }
+
+                section->data = p;
+
+                LIST_FIND_TAIL(section, m->sections, tail);
+                LIST_INSERT_AFTER(section, m->sections, tail, section);
+
+                p += section->length;
+                l += (section->length + 2);
+        }
+
+        return 0;
+}
+
+int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
+        tlv_section *s;
+
+        assert_return(m, -EINVAL);
+        assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
+
+        LIST_FOREACH(section, s, m->sections)
+                if (s->type == type)
+                        break;
+        if (!s)
+                return -1;
+
+        m->container = s;
+
+        m->container->read_pos = s->data;
+        if (!m->container->read_pos) {
+                m->container = NULL;
+                return -1;
+        }
+
+        return 0;
+}
+
+int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) {
+        tlv_section *s;
+
+        assert_return(m, -EINVAL);
+        assert_return(oui, -EINVAL);
+
+        LIST_FOREACH(section, s, m->sections) {
+                if (s->type == LLDP_TYPE_PRIVATE &&
+                    s->oui &&
+                    s->subtype == subtype &&
+                    !memcmp(s->oui, oui, LLDP_OUI_LEN))
+                        break;
+        }
+
+        if (!s)
+                return -1;
+
+        m->container = s;
+
+        m->container->read_pos = s->data;
+        if (!m->container->read_pos) {
+                m->container = NULL;
+                return -1;
+        }
+
+        return 0;
+}
+
+int lldp_tlv_packet_exit_container(tlv_packet *m) {
+        assert_return(m, -EINVAL);
+
+        m->container = 0;
+
+        return 0;
+}
+
+static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container(tlv, type);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_u16(tlv, value);
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) {
+        char *s;
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container(tlv, type);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_string(tlv, &s, length);
+        if (r < 0)
+                goto out;
+
+        *data = (char *) s;
+
+ out:
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_chassis_id(tlv_packet *tlv,
+                                   uint8_t *type,
+                                   uint8_t **data,
+                                   uint16_t *length) {
+        uint8_t subtype;
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_u8(tlv, &subtype);
+        if (r < 0)
+                goto out;
+
+        switch (subtype) {
+        case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+
+                r = tlv_packet_read_bytes(tlv, data, length);
+                if (r < 0)
+                        goto out;
+
+                break;
+        default:
+                r = -EOPNOTSUPP;
+                break;
+        }
+
+        *type = subtype;
+
+ out:
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_port_id(tlv_packet *tlv,
+                                uint8_t *type,
+                                uint8_t **data,
+                                uint16_t *length) {
+        uint8_t subtype;
+        char *s;
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_u8(tlv, &subtype);
+        if (r < 0)
+                goto out;
+
+        switch (subtype) {
+        case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+        case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+        case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+        case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+
+                r = tlv_packet_read_string(tlv, &s, length);
+                if (r < 0)
+                        goto out;
+
+                *data = (uint8_t *) s;
+
+                break;
+        case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+
+                r = tlv_packet_read_bytes(tlv, data, length);
+                if (r < 0)
+                        goto out;
+
+                break;
+        default:
+                r = -EOPNOTSUPP;
+                break;
+        }
+
+        *type = subtype;
+
+ out:
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
+        return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl);
+}
+
+int sd_lldp_packet_read_system_name(tlv_packet *tlv,
+                                    char **data,
+                                    uint16_t *length) {
+        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length);
+}
+
+int sd_lldp_packet_read_system_description(tlv_packet *tlv,
+                                           char **data,
+                                           uint16_t *length) {
+        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length);
+}
+
+int sd_lldp_packet_read_port_description(tlv_packet *tlv,
+                                         char **data,
+                                         uint16_t *length) {
+        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length);
+}
+
+int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) {
+        return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data);
+}
+
+int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_u16(tlv, id);
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_u8(tlv, flags);
+        if (r >= 0)
+                r = tlv_packet_read_u16(tlv, id);
+
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) {
+        int r, r2;
+        uint8_t len = 0;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_u16(tlv, vlan_id);
+        if (r >= 0)
+                r = tlv_packet_read_u8(tlv, &len);
+        if (r >= 0)
+                r = tlv_packet_read_string(tlv, name, length);
+
+        if (r >= 0 && len < *length)
+                *length = len;
+
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_u16(tlv, id);
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_u8(tlv, status);
+        if (r >= 0)
+                r = tlv_packet_read_u32(tlv, id);
+
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) {
+        assert_return(tlv, -EINVAL);
+        assert_return(dest, -EINVAL);
+
+        /* 802.1AB-2009, Table 7-1 */
+        if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN))
+                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE;
+        else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN))
+                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE;
+        else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN))
+                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE;
+        else
+                return -EINVAL;
+
+        return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-tlv.h b/src/systemd/src/libsystemd-network/lldp-tlv.h
new file mode 100644
index 00000000..7d7459e9
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-tlv.h
@@ -0,0 +1,98 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include <net/ethernet.h>
+
+#include "sd-lldp.h"
+
+#include "list.h"
+#include "lldp.h"
+#include "util.h"
+
+typedef struct sd_lldp_packet tlv_packet;
+typedef struct sd_lldp_section tlv_section;
+
+#define LLDP_OUI_LEN 3
+
+struct sd_lldp_section {
+        uint16_t type;
+        uint16_t length;
+        uint8_t *oui;
+        uint8_t subtype;
+
+        uint8_t *read_pos;
+        uint8_t *data;
+
+        LIST_FIELDS(tlv_section, section);
+};
+
+#define LLDP_MAC_NEAREST_BRIDGE          (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
+#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }
+#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }
+
+int tlv_section_new(tlv_section **ret);
+void tlv_section_free(tlv_section *ret);
+
+struct sd_lldp_packet {
+        unsigned n_ref;
+
+        uint16_t type;
+        uint16_t length;
+        usec_t ts;
+
+        uint8_t *container_pos;
+        uint8_t pdu[ETHER_MAX_LEN];
+
+        void *userdata;
+
+        struct ether_addr mac;
+        tlv_section *container;
+
+        LIST_HEAD(tlv_section, sections);
+};
+
+int tlv_packet_new(tlv_packet **ret);
+
+int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type);
+int lldp_tlv_packet_close_container(tlv_packet *m);
+
+int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length);
+int tlv_packet_append_u8(tlv_packet *m, uint8_t data);
+int tlv_packet_append_u16(tlv_packet *m, uint16_t data);
+int tlv_packet_append_u32(tlv_packet *m, uint32_t data);
+int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size);
+
+int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type);
+int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype);
+int lldp_tlv_packet_exit_container(tlv_packet *m);
+
+int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length);
+int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length);
+int tlv_packet_read_u8(tlv_packet *m, uint8_t *data);
+int tlv_packet_read_u16(tlv_packet *m, uint16_t *data);
+int tlv_packet_read_u32(tlv_packet *m, uint32_t *data);
+
+int tlv_packet_parse_pdu(tlv_packet *t, uint16_t size);
diff --git a/src/systemd/src/libsystemd-network/lldp.h b/src/systemd/src/libsystemd-network/lldp.h
new file mode 100644
index 00000000..974fd8b0
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp.h
@@ -0,0 +1,130 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#define LLDP_MULTICAST_ADDR     { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
+
+#define ETHERTYPE_LLDP          0x88cc
+
+/* IEEE 802.3AB Clause 9: TLV Types */
+typedef enum LLDPTypes {
+        LLDP_TYPE_END                  =   0,
+        LLDP_TYPE_CHASSIS_ID           =   1,
+        LLDP_TYPE_PORT_ID              =   2,
+        LLDP_TYPE_TTL                  =   3,
+        LLDP_TYPE_PORT_DESCRIPTION     =   4,
+        LLDP_TYPE_SYSTEM_NAME          =   5,
+        LLDP_TYPE_SYSTEM_DESCRIPTION   =   6,
+        LLDP_TYPE_SYSTEM_CAPABILITIES  =   7,
+        LLDP_TYPE_MGMT_ADDRESS         =   8,
+        LLDP_TYPE_PRIVATE              =   127,
+        _LLDP_TYPE_MAX,
+        _LLDP_TYPE_INVALID             = -1,
+} LLDPTypes;
+
+/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
+typedef enum LLDPChassisSubtypes {
+        LLDP_CHASSIS_SUBTYPE_RESERVED            = 0,
+        LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT   = 1,
+        LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS     = 2,
+        LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT      = 3,
+        LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS         = 4,
+        LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS     = 5,
+        LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME      = 6,
+        LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED    = 7,
+        _LLDP_CHASSIS_SUBTYPE_MAX,
+        _LLDP_CHASSIS_SUBTYPE_INVALID            = -1,
+} LLDPChassisSubtypes;
+
+/* IEEE 802.3AB Clause 9.5.3: Port subtype */
+typedef enum LLDPPortSubtypes  {
+        LLDP_PORT_SUBTYPE_RESERVED           = 0,
+        LLDP_PORT_SUBTYPE_INTERFACE_ALIAS    = 1,
+        LLDP_PORT_SUBTYPE_PORT_COMPONENT     = 2,
+        LLDP_PORT_SUBTYPE_MAC_ADDRESS        = 3,
+        LLDP_PORT_SUBTYPE_NETWORK            = 4,
+        LLDP_PORT_SUBTYPE_INTERFACE_NAME     = 5,
+        LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID   = 6,
+        LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED   = 7,
+        _LLDP_PORT_SUBTYPE_MAX,
+        _LLDP_PORT_SUBTYPE_INVALID           = -1
+} LLDPPortSubtypes;
+
+typedef enum LLDPSystemCapabilities {
+        LLDP_SYSTEM_CAPABILITIES_OTHER        = 1 << 0,
+        LLDP_SYSTEM_CAPABILITIES_REPEATER     = 1 << 1,
+        LLDP_SYSTEM_CAPABILITIES_BRIDGE       = 1 << 2,
+        LLDP_SYSTEM_CAPABILITIES_WLAN_AP      = 1 << 3,
+        LLDP_SYSTEM_CAPABILITIES_ROUTER       = 1 << 4,
+        LLDP_SYSTEM_CAPABILITIES_PHONE        = 1 << 5,
+        LLDP_SYSTEM_CAPABILITIES_DOCSIS       = 1 << 6,
+        LLDP_SYSTEM_CAPABILITIES_STATION      = 1 << 7,
+        LLDP_SYSTEM_CAPABILITIES_CVLAN        = 1 << 8,
+        LLDP_SYSTEM_CAPABILITIES_SVLAN        = 1 << 9,
+        LLDP_SYSTEM_CAPABILITIES_TPMR         = 1 << 10,
+        _LLDP_SYSTEM_CAPABILITIES_MAX,
+        _LLDP_SYSTEM_CAPABILITIES_INVALID     = -1,
+} LLDPSystemCapabilities;
+
+typedef enum LLDPMedSubtype {
+        LLDP_MED_SUBTYPE_RESERVED          = 0,
+        LLDP_MED_SUBTYPE_CAPABILITIES      = 1,
+        LLDP_MED_SUBTYPE_NETWORK_POLICY    = 2,
+        LLDP_MED_SUBTYPE_LOCATION_ID       = 3,
+        LLDP_MED_SUBTYPE_EXTENDED_PVMDI    = 4,
+        LLDP_MED_SUBTYPE_INV_HWREV         = 5,
+        LLDP_MED_SUBTYPE_INV_FWREV         = 6,
+        LLDP_MED_SUBTYPE_INV_SWREV         = 7,
+        LLDP_MED_SUBTYPE_INV_SERIAL        = 8,
+        LLDP_MED_SUBTYPE_INV_MANUFACTURER  = 9,
+        LLDP_MED_SUBTYPE_INV_MODELNAME     = 10,
+        LLDP_MED_SUBTYPE_INV_ASSETID       = 11,
+        _LLDP_MED_SUBTYPE_MAX,
+        _LLDP_MED_SUBTYPE_INVALID          = -1,
+} LLDPMedSubtype;
+
+typedef enum LLDPMedCapability {
+        LLDP_MED_CAPABILITY_CAPAPILITIES   = 1 << 0,
+        LLDP_MED_CAPABILITY_NETWORK_POLICY = 1 << 1,
+        LLDP_MED_CAPABILITY_LOCATION_ID    = 1 << 2,
+        LLDP_MED_CAPABILITY_EXTENDED_PSE   = 1 << 3,
+        LLDP_MED_CAPABILITY_EXTENDED_PD    = 1 << 4,
+        LLDP_MED_CAPABILITY_INVENTORY      = 1 << 5,
+        LLDP_MED_CAPABILITY_MAX,
+        LLDP_MED_CAPABILITY_INVALID        = -1,
+} LLDPMedCapability;
+
+#define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
+#define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
+
+enum {
+        LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID            = 1,
+        LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID   = 2,
+        LLDP_OUI_SUBTYPE_802_1_VLAN_NAME               = 3,
+        LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY       = 4,
+        LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST        = 5,
+        LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID          = 6,
+        LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION        = 7,
+};
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
new file mode 100644
index 00000000..579329c7
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -0,0 +1,560 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <netinet/ether.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#if 0 /* NM_IGNORED */
+#include "condition.h"
+#include "conf-parser.h"
+#endif /* NM_IGNORED */
+#include "dhcp-lease-internal.h"
+#include "hexdecoct.h"
+#include "log.h"
+#include "network-internal.h"
+#include "parse-util.h"
+#include "siphash24.h"
+#include "string-util.h"
+#include "strv.h"
+#include "utf8.h"
+#include "util.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, uint64_t *result) {
+        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. */
+        *result = htole64(siphash24(v, sz, HASH_KEY.bytes));
+
+        return 0;
+}
+
+bool net_match_config(const struct ether_addr *match_mac,
+                      char * const *match_paths,
+                      char * const *match_drivers,
+                      char * const *match_types,
+                      char * const *match_names,
+                      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 false;
+
+        if (match_virt && !condition_test(match_virt))
+                return false;
+
+        if (match_kernel && !condition_test(match_kernel))
+                return false;
+
+        if (match_arch && !condition_test(match_arch))
+                return false;
+
+        if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
+                return false;
+
+        if (!strv_isempty(match_paths) &&
+            (!dev_path || !strv_fnmatch(match_paths, dev_path, 0)))
+                return false;
+
+        if (!strv_isempty(match_drivers) &&
+            (!dev_driver || !strv_fnmatch(match_drivers, dev_driver, 0)))
+                return false;
+
+        if (!strv_isempty(match_types) &&
+            (!dev_type || !strv_fnmatch_or_empty(match_types, dev_type, 0)))
+                return false;
+
+        if (!strv_isempty(match_names) &&
+            (!dev_name || !strv_fnmatch_or_empty(match_names, dev_name, 0)))
+                return false;
+
+        return true;
+}
+
+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, 0, "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_ifnames(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 ***sv = data;
+        const char *word, *state;
+        size_t l;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        FOREACH_WORD(word, l, rvalue, state) {
+                char *n;
+
+                n = strndup(word, l);
+                if (!n)
+                        return log_oom();
+
+                if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+                        free(n);
+                        return 0;
+                }
+
+                r = strv_consume(sv, n);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        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;
+        _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) >= IFALIASZ) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias 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_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, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
+                free(n);
+                return 0;
+        }
+
+        free(*hwaddr);
+        *hwaddr = n;
+
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+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;
+}
+
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
+                         size_t size) {
+        unsigned i;
+
+        assert(f);
+        assert(addresses);
+        assert(size);
+
+        for (i = 0; i < size; i++)
+                fprintf(f, SD_NDISC_ADDRESS_FORMAT_STR"%s",
+                        SD_NDISC_ADDRESS_FORMAT_VAL(addresses[i]),
+                        (i < (size - 1)) ? " ": "");
+}
+
+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;
+}
+
+int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) {
+        _cleanup_free_ char *hex_buf = NULL;
+
+        assert(f);
+        assert(key);
+        assert(data);
+
+        hex_buf = hexmem(data, size);
+        if (hex_buf == NULL)
+                return -ENOMEM;
+
+        fprintf(f, "%s=%s\n", key, hex_buf);
+
+        return 0;
+}
+
+int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) {
+        assert(data);
+        assert(data_len);
+        assert(string);
+
+        if (strlen(string) % 2)
+                return -EINVAL;
+
+        return unhexmem(string, strlen(string), (void **)data, data_len);
+}
diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h
new file mode 100644
index 00000000..17c2f4fb
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/network-internal.h
@@ -0,0 +1,85 @@
+/*-*- 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 <stdbool.h>
+
+#if 0 /* NM_IGNORED */
+#include "condition.h"
+#include "udev.h"
+
+bool net_match_config(const struct ether_addr *match_mac,
+                      char * const *match_path,
+                      char * const *match_driver,
+                      char * const *match_type,
+                      char * const *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_ifnames(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, uint64_t *result);
+const char *net_get_name(struct udev_device *device);
+#endif /* NM_IGNORED */
+
+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);
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
+                         size_t size);
+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);
+
+int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
+int deserialize_dhcp_option(void **data, size_t *data_len, const char *string);
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
new file mode 100644
index 00000000..23d2c4b0
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -0,0 +1,1781 @@
+/***
+  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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <linux/if_infiniband.h>
+
+#include "sd-dhcp-client.h"
+
+#include "alloc-util.h"
+#include "async.h"
+#include "dhcp-identifier.h"
+#include "dhcp-internal.h"
+#include "dhcp-lease-internal.h"
+#include "dhcp-protocol.h"
+#include "dns-domain.h"
+#include "hostname-util.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "util.h"
+
+#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN)  /* Arbitrary limit */
+#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
+
+struct sd_dhcp_client {
+        unsigned 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;
+        struct {
+                uint8_t type;
+                union {
+                        struct {
+                                /* 0: Generic (non-LL) (RFC 2132) */
+                                uint8_t data[MAX_CLIENT_ID_LEN];
+                        } _packed_ gen;
+                        struct {
+                                /* 1: Ethernet Link-Layer (RFC 2132) */
+                                uint8_t haddr[ETH_ALEN];
+                        } _packed_ eth;
+                        struct {
+                                /* 2 - 254: ARP/Link-Layer (RFC 2132) */
+                                uint8_t haddr[0];
+                        } _packed_ ll;
+                        struct {
+                                /* 255: Node-specific (RFC 4361) */
+                                uint32_t iaid;
+                                struct duid duid;
+                        } _packed_ ns;
+                        struct {
+                                uint8_t data[MAX_CLIENT_ID_LEN];
+                        } _packed_ raw;
+                };
+        } _packed_ 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,
+};
+
+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, SD_DHCP_CLIENT_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.type;
+                *data = client->client_id.raw.data;
+                *data_len = client->client_id_len - sizeof(client->client_id.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.type) &&
+            client->client_id.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, SD_DHCP_CLIENT_EVENT_STOP);
+        }
+
+        client->client_id.type = type;
+        memcpy(&client->client_id.raw.data, data, data_len);
+        client->client_id_len = data_len + sizeof (client->client_id.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 (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname))
+                return -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 = 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;
+
+        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 == SD_DHCP_CLIENT_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);
+
+        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 an RFC 4361-compliant one */
+        if (client->client_id_len == 0) {
+                size_t duid_len;
+
+                client->client_id.type = 255;
+
+                r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
+                if (r < 0)
+                        return r;
+
+                r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
+                if (r < 0)
+                        return r;
+
+                client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
+        }
+
+        /* 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);
+                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 explicitly 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 client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset,
+                                     const char *fqdn) {
+        uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
+        int r;
+
+        buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
+                    DHCP_FQDN_FLAG_E;  /* Canonical wire format */
+        buffer[1] = 0;                 /* RCODE1 (deprecated) */
+        buffer[2] = 0;                 /* RCODE2 (deprecated) */
+
+        r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false);
+        if (r > 0)
+                r = dhcp_option_append(message, optlen, optoffset, 0,
+                                       DHCP_OPTION_FQDN, 3 + r, buffer);
+
+        return r;
+}
+
+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;
+        }
+
+        if (client->hostname) {
+                /* According to RFC 4702 "clients that send the Client FQDN option in
+                   their messages MUST NOT also send the Host Name option". Just send
+                   one of the two depending on the hostname type.
+                */
+                if (dns_name_is_single_label(client->hostname)) {
+                        /* it is unclear from RFC 2131 if client should send hostname in
+                           DHCPDISCOVER but dhclient does and so we do as well
+                        */
+                        r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                               DHCP_OPTION_HOST_NAME,
+                                               strlen(client->hostname), client->hostname);
+                } else
+                        r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+                                                      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) {
+                if (dns_name_is_single_label(client->hostname))
+                        r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+                                               DHCP_OPTION_HOST_NAME,
+                                               strlen(client->hostname), client->hostname);
+                else
+                        r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
+                                                      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);
+        if (r < 0)
+                goto error;
+
+        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, SD_DHCP_CLIENT_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_(sd_dhcp_lease_unrefp) 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,
+                                             client->client_id_len);
+                if (r < 0)
+                        return r;
+        }
+
+        r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
+        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 == 0 ||
+            lease->server_address == 0 ||
+            lease->lifetime == 0) {
+                log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
+                return -ENOMSG;
+        }
+
+        if (!lease->have_subnet_mask) {
+                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, 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_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
+        _cleanup_free_ char *error_message = 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,
+                                             client->client_id_len);
+                if (r < 0)
+                        return r;
+        }
+
+        r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
+        if (r == DHCP_NAK) {
+                log_dhcp_client(client, "NAK: %s", strna(error_message));
+                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 = SD_DHCP_CLIENT_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 = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
+                } else
+                        r = SD_DHCP_CLIENT_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 > 0);
+
+        if (lifetime > 3)
+                lifetime -= 3;
+        else
+                lifetime = 0;
+
+        return client->request_sent + (lifetime * 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 > 0 && client->lease->t2 > 0) {
+                /* 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 > 0 && 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 > 0 && 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, USEC_PER_SEC));
+
+        /* 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, USEC_PER_SEC));
+
+        /* 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, USEC_PER_SEC));
+
+        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 = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
+                        else if (r != SD_DHCP_CLIENT_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) {
+                                log_dhcp_client(client, "could not set lease timeouts");
+                                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 -errno;
+        else 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) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                log_dhcp_client(client, "Could not receive message from UDP socket: %m");
+                return -errno;
+        } 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 -errno;
+        else 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) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                log_dhcp_client(client, "Could not receive message from raw socket: %m");
+
+                return -errno;
+        } else if ((size_t)len < sizeof(DHCPPacket))
+                return 0;
+
+        CMSG_FOREACH(cmsg, &msg) {
+                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 %i", 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, SD_DHCP_CLIENT_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)
+                return NULL;
+
+        assert(client->n_ref >= 1);
+        client->n_ref++;
+
+        return client;
+}
+
+sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
+
+        if (!client)
+                return NULL;
+
+        assert(client->n_ref >= 1);
+        client->n_ref--;
+
+        if (client->n_ref > 0)
+                return NULL;
+
+        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_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        client = new0(sd_dhcp_client, 1);
+        if (!client)
+                return -ENOMEM;
+
+        client->n_ref = 1;
+        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/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
new file mode 100644
index 00000000..e6caab54
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
@@ -0,0 +1,1145 @@
+/***
+  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 <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sd-dhcp-lease.h"
+
+#include "alloc-util.h"
+#include "dhcp-lease-internal.h"
+#include "dhcp-protocol.h"
+#include "dns-domain.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hexdecoct.h"
+#include "hostname-util.h"
+#include "in-addr-util.h"
+#include "network-internal.h"
+#include "parse-util.h"
+#include "string-util.h"
+#include "unaligned.h"
+
+int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        if (lease->address == 0)
+                return -ENODATA;
+
+        addr->s_addr = lease->address;
+        return 0;
+}
+
+int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        if (!lease->have_broadcast)
+                return -ENODATA;
+
+        addr->s_addr = lease->broadcast;
+        return 0;
+}
+
+int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
+        assert_return(lease, -EINVAL);
+        assert_return(lifetime, -EINVAL);
+
+        if (lease->lifetime <= 0)
+                return -ENODATA;
+
+        *lifetime = lease->lifetime;
+        return 0;
+}
+
+int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
+        assert_return(lease, -EINVAL);
+        assert_return(t1, -EINVAL);
+
+        if (lease->t1 <= 0)
+                return -ENODATA;
+
+        *t1 = lease->t1;
+        return 0;
+}
+
+int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
+        assert_return(lease, -EINVAL);
+        assert_return(t2, -EINVAL);
+
+        if (lease->t2 <= 0)
+                return -ENODATA;
+
+        *t2 = lease->t2;
+        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 <= 0)
+                return -ENODATA;
+
+        *mtu = lease->mtu;
+        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 <= 0)
+                return -ENODATA;
+
+        *addr = lease->dns;
+        return (int) lease->dns_size;
+}
+
+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 <= 0)
+                return -ENODATA;
+
+        *addr = lease->ntp;
+        return (int) lease->ntp_size;
+}
+
+int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
+        assert_return(lease, -EINVAL);
+        assert_return(domainname, -EINVAL);
+
+        if (!lease->domainname)
+                return -ENODATA;
+
+        *domainname = lease->domainname;
+        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)
+                return -ENODATA;
+
+        *hostname = lease->hostname;
+        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)
+                return -ENODATA;
+
+        *root_path = lease->root_path;
+        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 == 0)
+                return -ENODATA;
+
+        addr->s_addr = lease->router;
+        return 0;
+}
+
+int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        if (!lease->have_subnet_mask)
+                return -ENODATA;
+
+        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);
+
+        if (lease->server_address == 0)
+                return -ENODATA;
+
+        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);
+
+        if (lease->next_server == 0)
+                return -ENODATA;
+
+        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 <= 0)
+                return -ENODATA;
+
+        *routes = lease->static_route;
+        return (int) lease->static_route_size;
+}
+
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
+        assert_return(lease, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(data_len, -EINVAL);
+
+        if (lease->vendor_specific_len <= 0)
+                return -ENODATA;
+
+        *data = lease->vendor_specific;
+        *data_len = lease->vendor_specific_len;
+        return 0;
+}
+
+sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
+
+        if (!lease)
+                return NULL;
+
+        assert(lease->n_ref >= 1);
+        lease->n_ref++;
+
+        return lease;
+}
+
+sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
+
+        if (!lease)
+                return NULL;
+
+        assert(lease->n_ref >= 1);
+        lease->n_ref--;
+
+        if (lease->n_ref > 0)
+                return NULL;
+
+        while (lease->private_options) {
+                struct sd_dhcp_raw_option *option = lease->private_options;
+
+                LIST_REMOVE(options, lease->private_options, option);
+
+                free(option->data);
+                free(option);
+        }
+
+        free(lease->hostname);
+        free(lease->domainname);
+        free(lease->dns);
+        free(lease->ntp);
+        free(lease->static_route);
+        free(lease->client_id);
+        free(lease->vendor_specific);
+        free(lease);
+
+        return NULL;
+}
+
+static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
+        assert(option);
+        assert(ret);
+
+        if (len != 4)
+                return -EINVAL;
+
+        *ret = unaligned_read_be32((be32_t*) option);
+        if (*ret < min)
+                *ret = min;
+
+        return 0;
+}
+
+static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
+        assert(option);
+        assert(ret);
+
+        if (len != 2)
+                return -EINVAL;
+
+        *ret = unaligned_read_be16((be16_t*) option);
+        if (*ret < min)
+                *ret = min;
+
+        return 0;
+}
+
+static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
+        assert(option);
+        assert(ret);
+
+        if (len != 4)
+                return -EINVAL;
+
+        memcpy(ret, option, 4);
+        return 0;
+}
+
+static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
+        assert(option);
+        assert(ret);
+
+        if (len <= 0)
+                *ret = mfree(*ret);
+        else {
+                char *string;
+
+                /*
+                 * One trailing NUL byte is OK, we don't mind. See:
+                 * https://github.com/systemd/systemd/issues/1337
+                 */
+                if (memchr(option, 0, len - 1))
+                        return -EINVAL;
+
+                string = strndup((const char *) option, len);
+                if (!string)
+                        return -ENOMEM;
+
+                free(*ret);
+                *ret = string;
+        }
+
+        return 0;
+}
+
+static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
+        assert(option);
+        assert(ret);
+        assert(n_ret);
+
+        if (len <= 0) {
+                *ret = mfree(*ret);
+                *n_ret = 0;
+        } else {
+                size_t n_addresses;
+                struct in_addr *addresses;
+
+                if (len % 4 != 0)
+                        return -EINVAL;
+
+                n_addresses = len / 4;
+
+                addresses = newdup(struct in_addr, option, n_addresses);
+                if (!addresses)
+                        return -ENOMEM;
+
+                free(*ret);
+                *ret = addresses;
+                *n_ret = n_addresses;
+        }
+
+        return 0;
+}
+
+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 || len <= 0);
+        assert(routes);
+        assert(routes_size);
+        assert(routes_allocated);
+
+        if (len <= 0)
+                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_debug("Failed to determine destination prefix length from class based IP, ignoring");
+                        continue;
+                }
+
+                assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
+                route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
+                option += 4;
+
+                assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
+                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 || len <= 0);
+        assert(routes);
+        assert(routes_size);
+        assert(routes_allocated);
+
+        if (len <= 0)
+                return 0;
+
+        /* 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 void *option, void *userdata) {
+        sd_dhcp_lease *lease = userdata;
+        int r;
+
+        assert(lease);
+
+        switch(code) {
+
+        case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+                r = lease_parse_u32(option, len, &lease->lifetime, 1);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
+
+                break;
+
+        case DHCP_OPTION_SERVER_IDENTIFIER:
+                r = lease_parse_be32(option, len, &lease->server_address);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
+
+                break;
+
+        case DHCP_OPTION_SUBNET_MASK:
+                r = lease_parse_be32(option, len, &lease->subnet_mask);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
+                else
+                        lease->have_subnet_mask = true;
+                break;
+
+        case DHCP_OPTION_BROADCAST:
+                r = lease_parse_be32(option, len, &lease->broadcast);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
+                else
+                        lease->have_broadcast = true;
+                break;
+
+        case DHCP_OPTION_ROUTER:
+                if (len >= 4) {
+                        r = lease_parse_be32(option, 4, &lease->router);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to parse router address, ignoring: %m");
+                }
+                break;
+
+        case DHCP_OPTION_DOMAIN_NAME_SERVER:
+                r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
+                break;
+
+        case DHCP_OPTION_NTP_SERVER:
+                r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
+                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)
+                        log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
+                break;
+
+        case DHCP_OPTION_INTERFACE_MTU:
+                r = lease_parse_u16(option, len, &lease->mtu, 68);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
+                break;
+
+        case DHCP_OPTION_DOMAIN_NAME: {
+                _cleanup_free_ char *domainname = NULL, *normalized = NULL;
+
+                r = lease_parse_string(option, len, &domainname);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
+                        return 0;
+                }
+
+                r = dns_name_normalize(domainname, &normalized);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to normalize domain name '%s': %m", domainname);
+                        return 0;
+                }
+
+                if (is_localhost(normalized)) {
+                        log_debug_errno(r, "Detected 'localhost' as suggested domain name, ignoring.");
+                        break;
+                }
+
+                free(lease->domainname);
+                lease->domainname = normalized;
+                normalized = NULL;
+
+                break;
+        }
+
+        case DHCP_OPTION_HOST_NAME: {
+                _cleanup_free_ char *hostname = NULL, *normalized = NULL;
+
+                r = lease_parse_string(option, len, &hostname);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to parse host name, ignoring: %m");
+                        return 0;
+                }
+
+                r = dns_name_normalize(hostname, &normalized);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to normalize host name '%s', ignoring: %m", hostname);
+                        return 0;
+                }
+
+                if (is_localhost(normalized)) {
+                        log_debug_errno(r, "Detected 'localhost' as suggested host name, ignoring.");
+                        return 0;
+                }
+
+                free(lease->hostname);
+                lease->hostname = normalized;
+                normalized = NULL;
+
+                break;
+        }
+
+        case DHCP_OPTION_ROOT_PATH:
+                r = lease_parse_string(option, len, &lease->root_path);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse root path, ignoring: %m");
+                break;
+
+        case DHCP_OPTION_RENEWAL_T1_TIME:
+                r = lease_parse_u32(option, len, &lease->t1, 1);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
+                break;
+
+        case DHCP_OPTION_REBINDING_T2_TIME:
+                r = lease_parse_u32(option, len, &lease->t2, 1);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
+                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)
+                        log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
+                break;
+
+        case DHCP_OPTION_NEW_TZDB_TIMEZONE: {
+                _cleanup_free_ char *tz = NULL;
+
+                r = lease_parse_string(option, len, &tz);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
+                        return 0;
+                }
+
+                if (!timezone_is_valid(tz)) {
+                        log_debug_errno(r, "Timezone is not valid, ignoring: %m");
+                        return 0;
+                }
+
+                free(lease->timezone);
+                lease->timezone = tz;
+                tz = NULL;
+
+                break;
+        }
+
+        case DHCP_OPTION_VENDOR_SPECIFIC:
+
+                if (len <= 0)
+                        lease->vendor_specific = mfree(lease->vendor_specific);
+                else {
+                        void *p;
+
+                        p = memdup(option, len);
+                        if (!p)
+                                return -ENOMEM;
+
+                        free(lease->vendor_specific);
+                        lease->vendor_specific = p;
+                }
+
+                lease->vendor_specific_len = len;
+                break;
+
+        case DHCP_OPTION_PRIVATE_BASE ... DHCP_OPTION_PRIVATE_LAST:
+                r = dhcp_lease_insert_private_option(lease, code, option, len);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        default:
+                log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code);
+                break;
+        }
+
+        return 0;
+}
+
+int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
+        struct sd_dhcp_raw_option *cur, *option;
+
+        assert(lease);
+
+        LIST_FOREACH(options, cur, lease->private_options) {
+                if (tag < cur->tag)
+                        break;
+                if (tag == cur->tag) {
+                        log_debug("Ignoring duplicate option, tagged %i.", tag);
+                        return 0;
+                }
+        }
+
+        option = new(struct sd_dhcp_raw_option, 1);
+        if (!option)
+                return -ENOMEM;
+
+        option->tag = tag;
+        option->length = len;
+        option->data = memdup(data, len);
+        if (!option->data) {
+                free(option);
+                return -ENOMEM;
+        }
+
+        LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
+        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 = 1;
+
+        *ret = lease;
+        return 0;
+}
+
+int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        struct sd_dhcp_raw_option *option;
+        struct in_addr address;
+        const struct in_addr *addresses;
+        const void *client_id, *data;
+        size_t client_id_len, data_len;
+        const char *string;
+        uint16_t mtu;
+        struct sd_dhcp_route *routes;
+        uint32_t t1, t2, lifetime;
+        int r;
+
+        assert(lease);
+        assert(lease_file);
+
+        r = fopen_temporary(lease_file, &f, &temp_path);
+        if (r < 0)
+                goto fail;
+
+        fchmod(fileno(f), 0644);
+
+        fprintf(f,
+                "# This is private data. Do not parse.\n");
+
+        r = sd_dhcp_lease_get_address(lease, &address);
+        if (r >= 0)
+                fprintf(f, "ADDRESS=%s\n", inet_ntoa(address));
+
+        r = sd_dhcp_lease_get_netmask(lease, &address);
+        if (r >= 0)
+                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_broadcast(lease, &address);
+        if (r >= 0)
+                fprintf(f, "BROADCAST=%s\n", inet_ntoa(address));
+
+        r = sd_dhcp_lease_get_mtu(lease, &mtu);
+        if (r >= 0)
+                fprintf(f, "MTU=%" PRIu16 "\n", mtu);
+
+        r = sd_dhcp_lease_get_t1(lease, &t1);
+        if (r >= 0)
+                fprintf(f, "T1=%" PRIu32 "\n", t1);
+
+        r = sd_dhcp_lease_get_t2(lease, &t2);
+        if (r >= 0)
+                fprintf(f, "T2=%" PRIu32 "\n", t2);
+
+        r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
+        if (r >= 0)
+                fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime);
+
+        r = sd_dhcp_lease_get_dns(lease, &addresses);
+        if (r > 0) {
+                fputs("DNS=", f);
+                serialize_in_addrs(f, addresses, r);
+                fputs("\n", f);
+        }
+
+        r = sd_dhcp_lease_get_ntp(lease, &addresses);
+        if (r > 0) {
+                fputs("NTP=", f);
+                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_timezone(lease, &string);
+        if (r >= 0)
+                fprintf(f, "TIMEZONE=%s\n", string);
+
+        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 fail;
+                }
+                fprintf(f, "CLIENTID=%s\n", client_id_hex);
+        }
+
+        r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
+        if (r >= 0) {
+                _cleanup_free_ char *option_hex = NULL;
+
+                option_hex = hexmem(data, data_len);
+                if (!option_hex) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+                fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
+        }
+
+        LIST_FOREACH(options, option, lease->private_options) {
+                char key[strlen("OPTION_000")+1];
+
+                snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
+                r = serialize_dhcp_option(f, key, option->data, option->length);
+                if (r < 0)
+                        goto fail;
+        }
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                goto fail;
+
+        if (rename(temp_path, lease_file) < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        return 0;
+
+fail:
+        if (temp_path)
+                (void) unlink(temp_path);
+
+        return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
+}
+
+int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
+
+        _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
+        _cleanup_free_ char
+                *address = NULL,
+                *router = NULL,
+                *netmask = NULL,
+                *server_address = NULL,
+                *next_server = NULL,
+                *broadcast = NULL,
+                *dns = NULL,
+                *ntp = NULL,
+                *mtu = NULL,
+                *routes = NULL,
+                *client_id_hex = NULL,
+                *vendor_specific_hex = NULL,
+                *lifetime = NULL,
+                *t1 = NULL,
+                *t2 = NULL,
+                *options[DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE + 1] = {};
+
+        int r, i;
+
+        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,
+                           "BROADCAST", &broadcast,
+                           "DNS", &dns,
+                           "NTP", &ntp,
+                           "MTU", &mtu,
+                           "DOMAINNAME", &lease->domainname,
+                           "HOSTNAME", &lease->hostname,
+                           "ROOT_PATH", &lease->root_path,
+                           "ROUTES", &routes,
+                           "CLIENTID", &client_id_hex,
+                           "TIMEZONE", &lease->timezone,
+                           "VENDOR_SPECIFIC", &vendor_specific_hex,
+                           "LIFETIME", &lifetime,
+                           "T1", &t1,
+                           "T2", &t2,
+                           "OPTION_224", &options[0],
+                           "OPTION_225", &options[1],
+                           "OPTION_226", &options[2],
+                           "OPTION_227", &options[3],
+                           "OPTION_228", &options[4],
+                           "OPTION_229", &options[5],
+                           "OPTION_230", &options[6],
+                           "OPTION_231", &options[7],
+                           "OPTION_232", &options[8],
+                           "OPTION_233", &options[9],
+                           "OPTION_234", &options[10],
+                           "OPTION_235", &options[11],
+                           "OPTION_236", &options[12],
+                           "OPTION_237", &options[13],
+                           "OPTION_238", &options[14],
+                           "OPTION_239", &options[15],
+                           "OPTION_240", &options[16],
+                           "OPTION_241", &options[17],
+                           "OPTION_242", &options[18],
+                           "OPTION_243", &options[19],
+                           "OPTION_244", &options[20],
+                           "OPTION_245", &options[21],
+                           "OPTION_246", &options[22],
+                           "OPTION_247", &options[23],
+                           "OPTION_248", &options[24],
+                           "OPTION_249", &options[25],
+                           "OPTION_250", &options[26],
+                           "OPTION_251", &options[27],
+                           "OPTION_252", &options[28],
+                           "OPTION_253", &options[29],
+                           "OPTION_254", &options[30],
+                           NULL);
+        if (r < 0)
+                return r;
+
+        if (address) {
+                r = inet_pton(AF_INET, address, &lease->address);
+                if (r <= 0)
+                        log_debug("Failed to parse address %s, ignoring.", address);
+        }
+
+        if (router) {
+                r = inet_pton(AF_INET, router, &lease->router);
+                if (r <= 0)
+                        log_debug("Failed to parse router %s, ignoring.", router);
+        }
+
+        if (netmask) {
+                r = inet_pton(AF_INET, netmask, &lease->subnet_mask);
+                if (r <= 0)
+                        log_debug("Failed to parse netmask %s, ignoring.", netmask);
+                else
+                        lease->have_subnet_mask = true;
+        }
+
+        if (server_address) {
+                r = inet_pton(AF_INET, server_address, &lease->server_address);
+                if (r <= 0)
+                        log_debug("Failed to parse server address %s, ignoring.", server_address);
+        }
+
+        if (next_server) {
+                r = inet_pton(AF_INET, next_server, &lease->next_server);
+                if (r <= 0)
+                        log_debug("Failed to parse next server %s, ignoring.", next_server);
+        }
+
+        if (broadcast) {
+                r = inet_pton(AF_INET, broadcast, &lease->broadcast);
+                if (r <= 0)
+                        log_debug("Failed to parse broadcast address %s, ignoring.", broadcast);
+                else
+                        lease->have_broadcast = true;
+        }
+
+        if (dns) {
+                r = deserialize_in_addrs(&lease->dns, dns);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
+                else
+                        lease->dns_size = r;
+        }
+
+        if (ntp) {
+                r = deserialize_in_addrs(&lease->ntp, ntp);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
+                else
+                        lease->ntp_size = r;
+        }
+
+        if (mtu) {
+                r = safe_atou16(mtu, &lease->mtu);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
+        }
+
+        if (routes) {
+                r = deserialize_dhcp_routes(
+                                &lease->static_route,
+                                &lease->static_route_size,
+                                &lease->static_route_allocated,
+                                routes);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes);
+        }
+
+        if (lifetime) {
+                r = safe_atou32(lifetime, &lease->lifetime);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime);
+        }
+
+        if (t1) {
+                r = safe_atou32(t1, &lease->t1);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1);
+        }
+
+        if (t2) {
+                r = safe_atou32(t2, &lease->t2);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2);
+        }
+
+        if (client_id_hex) {
+                r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
+        }
+
+        if (vendor_specific_hex) {
+                r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
+        }
+
+        for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
+                _cleanup_free_ void *data = NULL;
+                size_t len;
+
+                if (!options[i])
+                        continue;
+
+                r = deserialize_dhcp_option(&data, &len, options[i]);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
+                        continue;
+                }
+
+                r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = lease;
+        lease = NULL;
+
+        return 0;
+}
+
+int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
+        struct in_addr address, mask;
+        int r;
+
+        assert(lease);
+
+        if (lease->address == 0)
+                return -ENODATA;
+
+        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;
+        lease->have_subnet_mask = true;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
+        assert_return(lease, -EINVAL);
+        assert_return(client_id, -EINVAL);
+        assert_return(client_id_len, -EINVAL);
+
+        if (!lease->client_id)
+                return -ENODATA;
+
+        *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 void *client_id, size_t client_id_len) {
+        assert_return(lease, -EINVAL);
+        assert_return(client_id || client_id_len <= 0, -EINVAL);
+
+        if (client_id_len <= 0)
+                lease->client_id = mfree(lease->client_id);
+        else {
+                void *p;
+
+                p = memdup(client_id, client_id_len);
+                if (!p)
+                        return -ENOMEM;
+
+                free(lease->client_id);
+                lease->client_id = p;
+                lease->client_id_len = client_id_len;
+        }
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
+        assert_return(lease, -EINVAL);
+        assert_return(tz, -EINVAL);
+
+        if (!lease->timezone)
+                return -ENODATA;
+
+        *tz = lease->timezone;
+        return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
new file mode 100644
index 00000000..64793fc6
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -0,0 +1,1309 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014-2015 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 <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <linux/if_infiniband.h>
+
+#include "sd-dhcp6-client.h"
+
+#include "alloc-util.h"
+#include "dhcp-identifier.h"
+#include "dhcp6-internal.h"
+#include "dhcp6-lease-internal.h"
+#include "dhcp6-protocol.h"
+#include "fd-util.h"
+#include "in-addr-util.h"
+#include "network-internal.h"
+#include "random-util.h"
+#include "string-table.h"
+#include "util.h"
+
+#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
+
+struct sd_dhcp6_client {
+        unsigned n_ref;
+
+        enum DHCP6State state;
+        sd_event *event;
+        int event_priority;
+        int index;
+        struct in6_addr local_address;
+        uint8_t mac_addr[MAX_MAC_ADDR_LEN];
+        size_t mac_addr_len;
+        uint16_t arp_type;
+        DHCP6IA ia_na;
+        be32_t transaction_id;
+        usec_t transaction_start;
+        struct sd_dhcp6_lease *lease;
+        int fd;
+        bool information_request;
+        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;
+        struct duid duid;
+        size_t duid_len;
+};
+
+static const uint16_t default_req_opts[] = {
+        DHCP6_OPTION_DNS_SERVERS,
+        DHCP6_OPTION_DOMAIN_LIST,
+        DHCP6_OPTION_NTP_SERVER,
+        DHCP6_OPTION_SNTP_SERVERS,
+};
+
+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 DHCP6_CLIENT_DONT_DESTROY(client) \
+        _cleanup_(sd_dhcp6_client_unrefp) _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);
+
+        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+        client->index = interface_index;
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) {
+        assert_return(client, -EINVAL);
+        assert_return(local_address, -EINVAL);
+        assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
+
+        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+        client->local_address = *local_address;
+
+        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);
+
+        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+        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;
+}
+
+static int client_ensure_duid(sd_dhcp6_client *client) {
+        if (client->duid_len != 0)
+                return 0;
+
+        return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+}
+
+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);
+
+        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+        switch (type) {
+        case DHCP6_DUID_LLT:
+                if (duid_len <= sizeof(client->duid.llt))
+                        return -EINVAL;
+                break;
+        case DHCP6_DUID_EN:
+                if (duid_len != sizeof(client->duid.en))
+                        return -EINVAL;
+                break;
+        case DHCP6_DUID_LL:
+                if (duid_len <= sizeof(client->duid.ll))
+                        return -EINVAL;
+                break;
+        case DHCP6_DUID_UUID:
+                if (duid_len != sizeof(client->duid.uuid))
+                        return -EINVAL;
+                break;
+        default:
+                /* accept unknown type in order to be forward compatible */
+                break;
+        }
+
+        client->duid.type = htobe16(type);
+        memcpy(&client->duid.raw.data, duid, duid_len);
+        client->duid_len = duid_len + sizeof(client->duid.type);
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
+        assert_return(client, -EINVAL);
+        assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+        client->information_request = enabled;
+
+        return 0;
+}
+
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
+        assert_return(client, -EINVAL);
+        assert_return(enabled, -EINVAL);
+
+        *enabled = client->information_request;
+
+        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);
+
+        if (!client->lease)
+                return -ENOMSG;
+
+        if (ret)
+                *ret = client->lease;
+
+        return 0;
+}
+
+static void client_notify(sd_dhcp6_client *client, int event) {
+        if (client->cb)
+                client->cb(client, event, client->userdata);
+}
+
+static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
+        if (client->lease) {
+                dhcp6_lease_clear_timers(&client->lease->ia);
+                sd_dhcp6_lease_unref(client->lease);
+        }
+        client->lease = lease;
+}
+
+static int client_reset(sd_dhcp6_client *client) {
+        assert_return(client, -EINVAL);
+
+        client_set_lease(client, NULL);
+
+        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_INFORMATION_REQUEST:
+                message->type = DHCP6_INFORMATION_REQUEST;
+
+                break;
+
+        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;
+
+        assert (client->duid_len);
+        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, SD_DHCP6_CLIENT_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_INFORMATION_REQUEST:
+                init_retransmit_time = DHCP6_INF_TIMEOUT;
+                max_retransmit_time = DHCP6_INF_MAX_RT;
+
+                break;
+
+        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, SD_DHCP6_CLIENT_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, USEC_PER_SEC));
+
+        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) {
+        int r;
+
+        assert(client);
+
+        if (client->ia_na.id)
+                return 0;
+
+        r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
+        if (r < 0)
+                return r;
+
+        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:
+                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+                                log_dhcp6_client(client, "Information request ignoring IA NA option");
+
+                                break;
+                        }
+
+                        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;
+
+                case DHCP6_OPTION_DNS_SERVERS:
+                        r = dhcp6_lease_set_dns(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case DHCP6_OPTION_DOMAIN_LIST:
+                        r = dhcp6_lease_set_domains(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case DHCP6_OPTION_NTP_SERVER:
+                        r = dhcp6_lease_set_ntp(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case DHCP6_OPTION_SNTP_SERVERS:
+                        r = dhcp6_lease_set_sntp(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
+
+                        break;
+                }
+
+        }
+
+        if (r == -ENOMSG)
+                r = 0;
+
+        if (r < 0 || !clientid) {
+                log_dhcp6_client(client, "%s has incomplete options",
+                                 dhcp6_message_type_to_string(message->type));
+                return -EINVAL;
+        }
+
+        if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
+                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_(sd_dhcp6_lease_unrefp) 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;
+        }
+
+        client_set_lease(client, lease);
+        lease = NULL;
+
+        return DHCP6_STATE_BOUND;
+}
+
+static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
+        int r;
+        _cleanup_(sd_dhcp6_lease_unrefp) 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 (r < 0 || pref_advertise > pref_lease) {
+                client_set_lease(client, 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)
+                return -errno;
+        else if (buflen < 0)
+                /* This really should not happen */
+                return -EIO;
+
+        message = malloc(buflen);
+        if (!message)
+                return -ENOMEM;
+
+        len = read(fd, message, buflen);
+        if (len < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                log_dhcp6_client(client, "Could not receive message from UDP socket: %m");
+
+                return -errno;
+        } else if ((size_t)len < sizeof(DHCP6Message))
+                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_INFORMATION_REQUEST:
+                r = client_receive_reply(client, message, len);
+                if (r < 0)
+                        return 0;
+
+                client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
+
+                client_start(client, DHCP6_STATE_STOPPED);
+
+                break;
+
+        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, SD_DHCP6_CLIENT_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;
+
+        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                return r;
+
+        switch (state) {
+        case DHCP6_STATE_STOPPED:
+                if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+                        client->state = DHCP6_STATE_STOPPED;
+
+                        return 0;
+                }
+
+                /* fall through */
+        case DHCP6_STATE_SOLICITATION:
+                client->state = DHCP6_STATE_SOLICITATION;
+
+                break;
+
+        case DHCP6_STATE_INFORMATION_REQUEST:
+        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, USEC_PER_SEC));
+
+                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, USEC_PER_SEC));
+
+                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) {
+        assert_return(client, -EINVAL);
+
+        client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
+
+        return 0;
+}
+
+int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
+        assert_return(client, -EINVAL);
+
+        return client->state != DHCP6_STATE_STOPPED;
+}
+
+int sd_dhcp6_client_start(sd_dhcp6_client *client) {
+        int r = 0;
+        enum DHCP6State state = DHCP6_STATE_SOLICITATION;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->event, -EINVAL);
+        assert_return(client->index > 0, -EINVAL);
+        assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
+
+        if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
+                return -EBUSY;
+
+        r = client_reset(client);
+        if (r < 0)
+                return r;
+
+        r = client_ensure_iaid(client);
+        if (r < 0)
+                return r;
+
+        r = client_ensure_duid(client);
+        if (r < 0)
+                return r;
+
+        r = dhcp6_network_bind_udp_socket(client->index, &client->local_address);
+        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)
+                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,
+                                        "dhcp6-receive-message");
+        if (r < 0)
+                goto error;
+
+        if (client->information_request)
+                state = DHCP6_STATE_INFORMATION_REQUEST;
+
+        log_dhcp6_client(client, "Started in %s mode",
+                        client->information_request? "Information request":
+                        "Managed");
+
+        return client_start(client, state);
+
+error:
+        client_reset(client);
+        return r;
+}
+
+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)
+                return NULL;
+
+        assert(client->n_ref >= 1);
+        client->n_ref++;
+
+        return client;
+}
+
+sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
+
+        if (!client)
+                return NULL;
+
+        assert(client->n_ref >= 1);
+        client->n_ref--;
+
+        if (client->n_ref > 0)
+                return NULL;
+
+        client_reset(client);
+
+        sd_dhcp6_client_detach_event(client);
+
+        free(client->req_opts);
+        free(client);
+
+        return NULL;
+}
+
+int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
+        _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
+        size_t t;
+
+        assert_return(ret, -EINVAL);
+
+        client = new0(sd_dhcp6_client, 1);
+        if (!client)
+                return -ENOMEM;
+
+        client->n_ref = 1;
+
+        client->ia_na.type = DHCP6_OPTION_IA_NA;
+
+        client->index = -1;
+
+        client->fd = -1;
+
+        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;
+}
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
new file mode 100644
index 00000000..6a575aac
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
@@ -0,0 +1,414 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014-2015 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 <errno.h>
+
+#include "alloc-util.h"
+#include "dhcp6-lease-internal.h"
+#include "dhcp6-protocol.h"
+#include "strv.h"
+#include "util.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(preference, -EINVAL);
+
+        if (!lease)
+                return -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_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;
+}
+
+void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
+        if (lease)
+                lease->addr_iter = lease->ia.addresses;
+}
+
+int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
+        int r;
+
+        assert_return(lease, -EINVAL);
+        assert_return(optval, -EINVAL);
+
+        if (!optlen)
+                return 0;
+
+        r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
+                                        lease->dns_count,
+                                        &lease->dns_allocated);
+        if (r < 0) {
+                log_dhcp6_client(client, "Invalid DNS server option: %s",
+                                 strerror(-r));
+
+                return r;
+        }
+
+        lease->dns_count = r;
+
+        return 0;
+}
+
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) {
+        assert_return(lease, -EINVAL);
+        assert_return(addrs, -EINVAL);
+
+        if (lease->dns_count) {
+                *addrs = lease->dns;
+                return lease->dns_count;
+        }
+
+        return -ENOENT;
+}
+
+int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
+                            size_t optlen) {
+        int r;
+        char **domains;
+
+        assert_return(lease, -EINVAL);
+        assert_return(optval, -EINVAL);
+
+        if (!optlen)
+                return 0;
+
+        r = dhcp6_option_parse_domainname(optval, optlen, &domains);
+        if (r < 0)
+                return 0;
+
+        free(lease->domains);
+        lease->domains = domains;
+        lease->domains_count = r;
+
+        return r;
+}
+
+int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) {
+        assert_return(lease, -EINVAL);
+        assert_return(domains, -EINVAL);
+
+        if (lease->domains_count) {
+                *domains = lease->domains;
+                return lease->domains_count;
+        }
+
+        return -ENOENT;
+}
+
+int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
+        int r;
+        uint16_t subopt;
+        size_t sublen;
+        uint8_t *subval;
+
+        assert_return(lease, -EINVAL);
+        assert_return(optval, -EINVAL);
+
+        free(lease->ntp);
+        lease->ntp_count = 0;
+        lease->ntp_allocated = 0;
+
+        while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
+                                       &subval)) >= 0) {
+                int s;
+                char **servers;
+
+                switch(subopt) {
+                case DHCP6_NTP_SUBOPTION_SRV_ADDR:
+                case DHCP6_NTP_SUBOPTION_MC_ADDR:
+                        if (sublen != 16)
+                                return 0;
+
+                        s = dhcp6_option_parse_ip6addrs(subval, sublen,
+                                                        &lease->ntp,
+                                                        lease->ntp_count,
+                                                        &lease->ntp_allocated);
+                        if (s < 0)
+                                return s;
+
+                        lease->ntp_count = s;
+
+                        break;
+
+                case DHCP6_NTP_SUBOPTION_SRV_FQDN:
+                        r = dhcp6_option_parse_domainname(subval, sublen,
+                                                          &servers);
+                        if (r < 0)
+                                return 0;
+
+                        lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
+                        lease->ntp_fqdn = servers;
+                        lease->ntp_fqdn_count = r;
+
+                        break;
+                }
+        }
+
+        if (r != -ENOMSG)
+                return r;
+
+        return 0;
+}
+
+int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
+        int r;
+
+        assert_return(lease, -EINVAL);
+        assert_return(optval, -EINVAL);
+
+        if (!optlen)
+                return 0;
+
+        if (lease->ntp || lease->ntp_fqdn) {
+                log_dhcp6_client(client, "NTP information already provided");
+
+                return 0;
+        }
+
+        log_dhcp6_client(client, "Using deprecated SNTP information");
+
+        r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
+                                        lease->ntp_count,
+                                        &lease->ntp_allocated);
+        if (r < 0) {
+                log_dhcp6_client(client, "Invalid SNTP server option: %s",
+                                 strerror(-r));
+
+                return r;
+        }
+
+        lease->ntp_count = r;
+
+        return 0;
+}
+
+int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
+                                 struct in6_addr **addrs) {
+        assert_return(lease, -EINVAL);
+        assert_return(addrs, -EINVAL);
+
+        if (lease->ntp_count) {
+                *addrs = lease->ntp;
+                return lease->ntp_count;
+        }
+
+        return -ENOENT;
+}
+
+int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
+        assert_return(lease, -EINVAL);
+        assert_return(ntp_fqdn, -EINVAL);
+
+        if (lease->ntp_fqdn_count) {
+                *ntp_fqdn = lease->ntp_fqdn;
+                return lease->ntp_fqdn_count;
+        }
+
+        return -ENOENT;
+}
+
+sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
+
+        if (!lease)
+                return NULL;
+
+        assert(lease->n_ref >= 1);
+        lease->n_ref++;
+
+        return lease;
+}
+
+sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
+
+        if (!lease)
+                return NULL;
+
+        assert(lease->n_ref >= 1);
+        lease->n_ref--;
+
+        if (lease->n_ref > 0)
+                return NULL;
+
+        free(lease->serverid);
+        dhcp6_lease_free_ia(&lease->ia);
+
+        free(lease->dns);
+
+        lease->domains = strv_free(lease->domains);
+
+        free(lease->ntp);
+
+        lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
+        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 = 1;
+
+        LIST_HEAD_INIT(lease->ia.addresses);
+
+        *ret = lease;
+        return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
new file mode 100644
index 00000000..99e663c1
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
@@ -0,0 +1,529 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Axis Communications AB. All rights reserved.
+  Copyright (C) 2015 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 <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sd-ipv4acd.h"
+
+#include "alloc-util.h"
+#include "arp-util.h"
+#include "fd-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "random-util.h"
+#include "refcnt.h"
+#include "siphash24.h"
+#include "util.h"
+
+/* Constants from the RFC */
+#define PROBE_WAIT 1
+#define PROBE_NUM 3
+#define PROBE_MIN 1
+#define PROBE_MAX 2
+#define ANNOUNCE_WAIT 2
+#define ANNOUNCE_NUM 2
+#define ANNOUNCE_INTERVAL 2
+#define MAX_CONFLICTS 10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL 10
+
+#define IPV4ACD_NETWORK 0xA9FE0000L
+#define IPV4ACD_NETMASK 0xFFFF0000L
+
+#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug(ll, ...)   log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_ipv4acd_info(ll, ...)    log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_ipv4acd_notice(ll, ...)  log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_ipv4acd_error(ll, ...)   log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug_errno(ll, error, ...)   log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_ipv4acd_info_errno(ll, error, ...)    log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__)
+#define log_ipv4acd_notice_errno(ll, error, ...)  log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_ipv4acd_error_errno(ll, error, ...)   log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__)
+
+typedef enum IPv4ACDState {
+        IPV4ACD_STATE_INIT,
+        IPV4ACD_STATE_WAITING_PROBE,
+        IPV4ACD_STATE_PROBING,
+        IPV4ACD_STATE_WAITING_ANNOUNCE,
+        IPV4ACD_STATE_ANNOUNCING,
+        IPV4ACD_STATE_RUNNING,
+        _IPV4ACD_STATE_MAX,
+        _IPV4ACD_STATE_INVALID = -1
+} IPv4ACDState;
+
+struct sd_ipv4acd {
+        RefCount n_ref;
+
+        IPv4ACDState state;
+        int index;
+        int fd;
+        int iteration;
+        int conflict;
+        sd_event_source *receive_message;
+        sd_event_source *timer;
+        usec_t defend_window;
+        be32_t address;
+        /* External */
+        struct ether_addr mac_addr;
+        sd_event *event;
+        int event_priority;
+        sd_ipv4acd_cb_t cb;
+        void* userdata;
+};
+
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
+        if (ll)
+                assert_se(REFCNT_INC(ll->n_ref) >= 2);
+
+        return ll;
+}
+
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
+        if (!ll || REFCNT_DEC(ll->n_ref) > 0)
+                return NULL;
+
+        ll->receive_message = sd_event_source_unref(ll->receive_message);
+        ll->fd = safe_close(ll->fd);
+
+        ll->timer = sd_event_source_unref(ll->timer);
+
+        sd_ipv4acd_detach_event(ll);
+
+        free(ll);
+
+        return NULL;
+}
+
+int sd_ipv4acd_new(sd_ipv4acd **ret) {
+        _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *ll = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        ll = new0(sd_ipv4acd, 1);
+        if (!ll)
+                return -ENOMEM;
+
+        ll->n_ref = REFCNT_INIT;
+        ll->state = IPV4ACD_STATE_INIT;
+        ll->index = -1;
+        ll->fd = -1;
+
+        *ret = ll;
+        ll = NULL;
+
+        return 0;
+}
+
+static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
+
+        assert(ll);
+        assert(st < _IPV4ACD_STATE_MAX);
+
+        if (st == ll->state && !reset_counter)
+                ll->iteration++;
+        else {
+                ll->state = st;
+                ll->iteration = 0;
+        }
+}
+
+static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
+        assert(ll);
+
+        if (ll->cb)
+                ll->cb(ll, event, ll->userdata);
+}
+
+static void ipv4acd_stop(sd_ipv4acd *ll) {
+        assert(ll);
+
+        ll->receive_message = sd_event_source_unref(ll->receive_message);
+        ll->fd = safe_close(ll->fd);
+
+        ll->timer = sd_event_source_unref(ll->timer);
+
+        log_ipv4acd_debug(ll, "STOPPED");
+
+        ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
+}
+
+int sd_ipv4acd_stop(sd_ipv4acd *ll) {
+        assert_return(ll, -EINVAL);
+
+        ipv4acd_stop(ll);
+
+        ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_STOP);
+
+        return 0;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
+
+static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
+        _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
+        usec_t next_timeout;
+        usec_t time_now;
+        int r;
+
+        assert(sec >= 0);
+        assert(random_sec >= 0);
+        assert(ll);
+
+        next_timeout = sec * USEC_PER_SEC;
+
+        if (random_sec)
+                next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
+
+        assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+        r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
+                              time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(timer, ll->event_priority);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_description(timer, "ipv4acd-timer");
+        if (r < 0)
+                return r;
+
+        ll->timer = sd_event_source_unref(ll->timer);
+        ll->timer = timer;
+        timer = NULL;
+
+        return 0;
+}
+
+static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
+        assert(ll);
+        assert(arp);
+
+        /* see the BPF */
+        if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
+                return true;
+
+        /* the TPA matched instead of the SPA, this is not a conflict */
+        return false;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+        sd_ipv4acd *ll = userdata;
+        int r = 0;
+
+        assert(ll);
+
+        switch (ll->state) {
+        case IPV4ACD_STATE_INIT:
+
+                ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
+
+                if (ll->conflict >= MAX_CONFLICTS) {
+                        log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
+                        r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
+                        if (r < 0)
+                                goto out;
+
+                        ll->conflict = 0;
+                } else {
+                        r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
+                        if (r < 0)
+                                goto out;
+                }
+
+                break;
+        case IPV4ACD_STATE_WAITING_PROBE:
+        case IPV4ACD_STATE_PROBING:
+                /* Send a probe */
+                r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                if (r < 0) {
+                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
+                        goto out;
+                } else {
+                        _cleanup_free_ char *address = NULL;
+                        union in_addr_union addr = { .in.s_addr = ll->address };
+
+                        r = in_addr_to_string(AF_INET, &addr, &address);
+                        if (r >= 0)
+                                log_ipv4acd_debug(ll, "Probing %s", address);
+                }
+
+                if (ll->iteration < PROBE_NUM - 2) {
+                        ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
+
+                        r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
+                        if (r < 0)
+                                goto out;
+                } else {
+                        ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
+
+                        r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
+                        if (r < 0)
+                                goto out;
+                }
+
+                break;
+
+        case IPV4ACD_STATE_ANNOUNCING:
+                if (ll->iteration >= ANNOUNCE_NUM - 1) {
+                        ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
+
+                        break;
+                }
+        case IPV4ACD_STATE_WAITING_ANNOUNCE:
+                /* Send announcement packet */
+                r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                if (r < 0) {
+                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+                        goto out;
+                } else
+                        log_ipv4acd_debug(ll, "ANNOUNCE");
+
+                ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
+
+                r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
+                if (r < 0)
+                        goto out;
+
+                if (ll->iteration == 0) {
+                        ll->conflict = 0;
+                        ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND);
+                }
+
+                break;
+        default:
+                assert_not_reached("Invalid state.");
+        }
+
+out:
+        if (r < 0)
+                sd_ipv4acd_stop(ll);
+
+        return 1;
+}
+
+static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
+        _cleanup_free_ char *address = NULL;
+        union in_addr_union addr = { .in.s_addr = ll->address };
+        int r;
+
+        assert(ll);
+
+        ll->conflict++;
+
+        r = in_addr_to_string(AF_INET, &addr, &address);
+        if (r >= 0)
+                log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
+
+        ipv4acd_stop(ll);
+
+        ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT);
+}
+
+static int ipv4acd_on_packet(sd_event_source *s, int fd,
+                            uint32_t revents, void *userdata) {
+        sd_ipv4acd *ll = userdata;
+        struct ether_arp packet;
+        int r;
+
+        assert(ll);
+        assert(fd >= 0);
+
+        r = read(fd, &packet, sizeof(struct ether_arp));
+        if (r < (int) sizeof(struct ether_arp))
+                goto out;
+
+        switch (ll->state) {
+        case IPV4ACD_STATE_ANNOUNCING:
+        case IPV4ACD_STATE_RUNNING:
+                if (ipv4acd_arp_conflict(ll, &packet)) {
+                        usec_t ts;
+
+                        assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+                        /* Defend address */
+                        if (ts > ll->defend_window) {
+                                ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
+                                r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                                if (r < 0) {
+                                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+                                        goto out;
+                                } else
+                                        log_ipv4acd_debug(ll, "DEFEND");
+
+                        } else
+                                ipv4acd_on_conflict(ll);
+                }
+
+                break;
+        case IPV4ACD_STATE_WAITING_PROBE:
+        case IPV4ACD_STATE_PROBING:
+        case IPV4ACD_STATE_WAITING_ANNOUNCE:
+                /* BPF ensures this packet indicates a conflict */
+                ipv4acd_on_conflict(ll);
+
+                break;
+        default:
+                assert_not_reached("Invalid state.");
+        }
+
+out:
+        if (r < 0)
+                sd_ipv4acd_stop(ll);
+
+        return 1;
+}
+
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
+        assert_return(ll, -EINVAL);
+        assert_return(interface_index > 0, -EINVAL);
+        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+        ll->index = interface_index;
+
+        return 0;
+}
+
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
+        assert_return(ll, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+        memcpy(&ll->mac_addr, addr, ETH_ALEN);
+
+        return 0;
+}
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
+        assert_return(ll, -EINVAL);
+
+        ll->event = sd_event_unref(ll->event);
+
+        return 0;
+}
+
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+        assert_return(!ll->event, -EBUSY);
+
+        if (event)
+                ll->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&ll->event);
+                if (r < 0)
+                        return r;
+        }
+
+        ll->event_priority = priority;
+
+        return 0;
+}
+
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
+        assert_return(ll, -EINVAL);
+
+        ll->cb = cb;
+        ll->userdata = userdata;
+
+        return 0;
+}
+
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
+        assert_return(ll, -EINVAL);
+        assert_return(address, -EINVAL);
+        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+        ll->address = address->s_addr;
+
+        return 0;
+}
+
+int sd_ipv4acd_is_running(sd_ipv4acd *ll) {
+        assert_return(ll, false);
+
+        return ll->state != IPV4ACD_STATE_INIT;
+}
+
+static bool ether_addr_is_nul(const struct ether_addr *addr) {
+        const struct ether_addr nul_addr = {};
+
+        assert(addr);
+
+        return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
+}
+
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
+int sd_ipv4acd_start(sd_ipv4acd *ll) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+        assert_return(ll->event, -EINVAL);
+        assert_return(ll->index > 0, -EINVAL);
+        assert_return(ll->address != 0, -EINVAL);
+        assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
+        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+        ll->defend_window = 0;
+
+        r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
+        if (r < 0)
+                goto out;
+
+        ll->fd = safe_close(ll->fd);
+        ll->fd = r;
+
+        r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
+                            EPOLLIN, ipv4acd_on_packet, ll);
+        if (r < 0)
+                goto out;
+
+        r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
+        if (r < 0)
+                goto out;
+
+        r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");
+        if (r < 0)
+                goto out;
+
+        r = ipv4acd_set_next_wakeup(ll, 0, 0);
+        if (r < 0)
+                goto out;
+out:
+        if (r < 0) {
+                ipv4acd_stop(ll);
+                return r;
+        }
+
+        return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
new file mode 100644
index 00000000..119d00fc
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -0,0 +1,366 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Axis Communications AB. All rights reserved.
+  Copyright (C) 2015 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 <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sd-ipv4acd.h"
+#include "sd-ipv4ll.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "random-util.h"
+#include "refcnt.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "util.h"
+
+#define IPV4LL_NETWORK 0xA9FE0000L
+#define IPV4LL_NETMASK 0xFFFF0000L
+
+#define IPV4LL_DONT_DESTROY(ll) \
+        _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
+
+struct sd_ipv4ll {
+        unsigned n_ref;
+
+        sd_ipv4acd *acd;
+        be32_t address; /* the address pushed to ACD */
+        struct random_data *random_data;
+        char *random_data_state;
+
+        /* External */
+        be32_t claimed_address;
+        sd_ipv4ll_cb_t cb;
+        void* userdata;
+};
+
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
+        if (!ll)
+                return NULL;
+
+        assert(ll->n_ref >= 1);
+        ll->n_ref++;
+
+        return ll;
+}
+
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
+        if (!ll)
+                return NULL;
+
+        assert(ll->n_ref >= 1);
+        ll->n_ref--;
+
+        if (ll->n_ref > 0)
+                return NULL;
+
+        sd_ipv4acd_unref(ll->acd);
+
+        free(ll->random_data);
+        free(ll->random_data_state);
+        free(ll);
+
+        return NULL;
+}
+
+static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
+
+int sd_ipv4ll_new(sd_ipv4ll **ret) {
+        _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        ll = new0(sd_ipv4ll, 1);
+        if (!ll)
+                return -ENOMEM;
+
+        ll->n_ref = 1;
+
+        r = sd_ipv4acd_new(&ll->acd);
+        if (r < 0)
+                return r;
+
+        r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
+        if (r < 0)
+                return r;
+
+        *ret = ll;
+        ll = NULL;
+
+        return 0;
+}
+
+int sd_ipv4ll_stop(sd_ipv4ll *ll) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+
+        r = sd_ipv4acd_stop(ll->acd);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
+        assert_return(ll, -EINVAL);
+
+        return sd_ipv4acd_set_index(ll->acd, interface_index);
+}
+
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
+int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+
+        if (!ll->random_data) {
+                uint64_t seed;
+
+                /* If no random data is set, generate some from the MAC */
+                seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes);
+
+                assert_cc(sizeof(unsigned) <= 8);
+
+                r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed));
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_ipv4acd_set_mac(ll->acd, addr);
+}
+
+int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
+        assert_return(ll, -EINVAL);
+
+        return sd_ipv4acd_detach_event(ll->acd);
+}
+
+int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+
+        r = sd_ipv4acd_attach_event(ll->acd, event, priority);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
+        assert_return(ll, -EINVAL);
+
+        ll->cb = cb;
+        ll->userdata = userdata;
+
+        return 0;
+}
+
+int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
+        assert_return(ll, -EINVAL);
+        assert_return(address, -EINVAL);
+
+        if (ll->claimed_address == 0)
+                return -ENOENT;
+
+        address->s_addr = ll->claimed_address;
+
+        return 0;
+}
+
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
+        _cleanup_free_ struct random_data *random_data = NULL;
+        _cleanup_free_ char *random_data_state = NULL;
+        int r;
+
+        assert_return(ll, -EINVAL);
+
+        random_data = new0(struct random_data, 1);
+        if (!random_data)
+                return -ENOMEM;
+
+        random_data_state = new0(char, 128);
+        if (!random_data_state)
+                return -ENOMEM;
+
+        r = initstate_r(seed, random_data_state, 128, random_data);
+        if (r < 0)
+                return r;
+
+        free(ll->random_data);
+        ll->random_data = random_data;
+        random_data = NULL;
+
+        free(ll->random_data_state);
+        ll->random_data_state = random_data_state;
+        random_data_state = NULL;
+
+        return 0;
+}
+
+int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
+        assert_return(ll, false);
+
+        return sd_ipv4acd_is_running(ll->acd);
+}
+
+static bool ipv4ll_address_is_valid(const struct in_addr *address) {
+        uint32_t addr;
+
+        assert(address);
+
+        if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
+                return false;
+
+        addr = be32toh(address->s_addr);
+
+        if ((addr & 0x0000FF00) == 0x0000 ||
+            (addr & 0x0000FF00) == 0xFF00)
+                return false;
+
+        return true;
+}
+
+int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+        assert_return(address, -EINVAL);
+        assert_return(ipv4ll_address_is_valid(address), -EINVAL);
+
+        r = sd_ipv4acd_set_address(ll->acd, address);
+        if (r < 0)
+                return r;
+
+        ll->address = address->s_addr;
+
+        return 0;
+}
+
+static int ipv4ll_pick_address(sd_ipv4ll *ll) {
+        struct in_addr in_addr;
+        be32_t addr;
+        int r;
+        int32_t random;
+
+        assert(ll);
+        assert(ll->random_data);
+
+        do {
+                r = random_r(ll->random_data, &random);
+                if (r < 0)
+                        return r;
+                addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
+        } while (addr == ll->address ||
+                (ntohl(addr) & 0x0000FF00) == 0x0000 ||
+                (ntohl(addr) & 0x0000FF00) == 0xFF00);
+
+        in_addr.s_addr = addr;
+
+        r = sd_ipv4ll_set_address(ll, &in_addr);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int sd_ipv4ll_start(sd_ipv4ll *ll) {
+        int r;
+
+        assert_return(ll, -EINVAL);
+        assert_return(ll->random_data, -EINVAL);
+
+        if (ll->address == 0) {
+                r = ipv4ll_pick_address(ll);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_ipv4acd_start(ll->acd);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
+        assert(ll);
+
+        if (ll->cb)
+                ll->cb(ll, event, ll->userdata);
+}
+
+void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
+        sd_ipv4ll *ll = userdata;
+        IPV4LL_DONT_DESTROY(ll);
+        int r;
+
+        assert(acd);
+        assert(ll);
+
+        switch (event) {
+        case SD_IPV4ACD_EVENT_STOP:
+                ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
+
+                ll->claimed_address = 0;
+
+                break;
+        case SD_IPV4ACD_EVENT_BIND:
+                ll->claimed_address = ll->address;
+                ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
+
+                break;
+        case SD_IPV4ACD_EVENT_CONFLICT:
+                /* if an address was already bound we must call up to the
+                   user to handle this, otherwise we just try again */
+                if (ll->claimed_address != 0) {
+                        ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
+
+                        ll->claimed_address = 0;
+                } else {
+                        r = ipv4ll_pick_address(ll);
+                        if (r < 0)
+                                goto error;
+
+                        r = sd_ipv4acd_start(ll->acd);
+                        if (r < 0)
+                                goto error;
+                }
+
+                break;
+        default:
+                assert_not_reached("Invalid IPv4ACD event.");
+        }
+
+        return;
+
+error:
+        ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
+}
diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c
new file mode 100644
index 00000000..882f130a
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-lldp.c
@@ -0,0 +1,760 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+    This file is part of systemd.
+
+    Copyright (C) 2014 Tom Gundersen
+    Copyright (C) 2014 Susant Sahani
+
+    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 <arpa/inet.h>
+
+#include "sd-lldp.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "lldp-internal.h"
+#include "lldp-port.h"
+#include "lldp-tlv.h"
+#include "prioq.h"
+#include "siphash24.h"
+#include "string-util.h"
+
+typedef enum LLDPAgentRXState {
+        LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4,
+        LLDP_AGENT_RX_DELETE_AGED_INFO,
+        LLDP_AGENT_RX_LLDP_INITIALIZE,
+        LLDP_AGENT_RX_WAIT_FOR_FRAME,
+        LLDP_AGENT_RX_RX_FRAME,
+        LLDP_AGENT_RX_DELETE_INFO,
+        LLDP_AGENT_RX_UPDATE_INFO,
+        _LLDP_AGENT_RX_STATE_MAX,
+        _LLDP_AGENT_RX_INVALID = -1,
+} LLDPAgentRXState;
+
+/* Section 10.5.2.2 Reception counters */
+struct lldp_agent_statistics {
+        uint64_t stats_ageouts_total;
+        uint64_t stats_frames_discarded_total;
+        uint64_t stats_frames_in_errors_total;
+        uint64_t stats_frames_in_total;
+        uint64_t stats_tlvs_discarded_total;
+        uint64_t stats_tlvs_unrecognized_total;
+};
+
+struct sd_lldp {
+        lldp_port *port;
+
+        Prioq *by_expiry;
+        Hashmap *neighbour_mib;
+
+        sd_lldp_cb_t cb;
+
+        void *userdata;
+
+        LLDPAgentRXState rx_state;
+        lldp_agent_statistics statistics;
+};
+
+static void chassis_id_hash_func(const void *p, struct siphash *state) {
+        const lldp_chassis_id *id = p;
+
+        assert(id);
+        assert(id->data);
+
+        siphash24_compress(&id->length, sizeof(id->length), state);
+        siphash24_compress(id->data, id->length, state);
+}
+
+static int chassis_id_compare_func(const void *_a, const void *_b) {
+        const lldp_chassis_id *a, *b;
+
+        a = _a;
+        b = _b;
+
+        assert(!a->length || a->data);
+        assert(!b->length || b->data);
+
+        if (a->type != b->type)
+                return -1;
+
+        if (a->length != b->length)
+                return a->length < b->length ? -1 : 1;
+
+        return memcmp(a->data, b->data, a->length);
+}
+
+static const struct hash_ops chassis_id_hash_ops = {
+        .hash = chassis_id_hash_func,
+        .compare = chassis_id_compare_func
+};
+
+static void lldp_mib_delete_objects(sd_lldp *lldp);
+static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state);
+static void lldp_run_state_machine(sd_lldp *ll);
+
+static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
+        int r;
+
+        assert(lldp);
+        assert(tlv);
+
+        /* Remove expired packets */
+        if (prioq_size(lldp->by_expiry) > 0) {
+
+                lldp_set_state(lldp, LLDP_AGENT_RX_DELETE_INFO);
+
+                lldp_mib_delete_objects(lldp);
+        }
+
+        r = lldp_mib_add_objects(lldp->by_expiry, lldp->neighbour_mib, tlv);
+        if (r < 0)
+                goto out;
+
+        lldp_set_state(lldp, LLDP_AGENT_RX_UPDATE_INFO);
+
+        log_lldp("Packet added. MIB size: %d , PQ size: %d",
+                 hashmap_size(lldp->neighbour_mib),
+                 prioq_size(lldp->by_expiry));
+
+        lldp->statistics.stats_frames_in_total ++;
+
+ out:
+        if (r < 0)
+                log_lldp("Receive frame failed: %s", strerror(-r));
+
+        lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+
+        return 0;
+}
+
+/* 10.3.2 LLDPDU validation: rxProcessFrame() */
+int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
+        bool system_description = false, system_name = false, chassis_id = false;
+        bool malformed = false, port_id = false, ttl = false, end = false;
+        uint16_t type, len, i, l, t;
+        lldp_port *port;
+        uint8_t *p, *q;
+        sd_lldp *lldp;
+        int r;
+
+        assert(tlv);
+        assert(length > 0);
+
+        port = (lldp_port *) tlv->userdata;
+        lldp = (sd_lldp *) port->userdata;
+
+        if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
+                log_lldp("Port: %s is disabled. Dropping.", lldp->port->ifname);
+                goto out;
+        }
+
+        lldp_set_state(lldp, LLDP_AGENT_RX_RX_FRAME);
+
+        p = tlv->pdu;
+        p += sizeof(struct ether_header);
+
+        for (i = 1, l = 0; l <= length; i++) {
+
+                memcpy(&t, p, sizeof(uint16_t));
+
+                type = ntohs(t) >> 9;
+                len = ntohs(t) & 0x01ff;
+
+                if (type == LLDP_TYPE_END) {
+                        if (len != 0) {
+                                log_lldp("TLV type end must be length 0 (not %d). Dropping.", len);
+
+                                malformed = true;
+                                goto out;
+                        }
+
+                        end = true;
+
+                        break;
+                } else if (type >=_LLDP_TYPE_MAX) {
+                        log_lldp("TLV type: %d not recognized. Dropping.", type);
+
+                        malformed = true;
+                        goto out;
+                }
+
+                /* skip type and length encoding */
+                p += 2;
+                q = p;
+
+                p += len;
+                l += (len + 2);
+
+                if (i <= 3) {
+                        if (i != type) {
+                                log_lldp("TLV missing or out of order. Dropping.");
+
+                                malformed = true;
+                                goto out;
+                        }
+                }
+
+                switch(type) {
+                case LLDP_TYPE_CHASSIS_ID:
+
+                        if (len < 2) {
+                                log_lldp("Received malformed Chassis ID TLV length: %d. Dropping.", len);
+
+                                malformed = true;
+                                goto out;
+                        }
+
+                        if (chassis_id) {
+                                log_lldp("Duplicate Chassis ID TLV found. Dropping.");
+
+                                malformed = true;
+                                goto out;
+                        }
+
+                        /* Look what subtype it has */
+                        if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED || *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
+                                log_lldp("Unknown subtype: %d found in Chassis ID TLV. Dropping.", *q);
+
+                                malformed = true;
+                                goto out;
+
+                        }
+
+                        chassis_id = true;
+
+                        break;
+                case LLDP_TYPE_PORT_ID:
+
+                        if (len < 2) {
+                                log_lldp("Received malformed Port ID TLV length: %d. Dropping.", len);
+
+                                malformed = true;
+                                goto out;
+                        }
+
+                        if (port_id) {
+                                log_lldp("Duplicate Port ID TLV found. Dropping.");
+
+                                malformed = true;
+                                goto out;
+                        }
+
+                        /* Look what subtype it has */
+                        if (*q == LLDP_PORT_SUBTYPE_RESERVED || *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
+                                log_lldp("Unknown subtype: %d found in Port ID TLV. Dropping.", *q);
+
+                                malformed = true;
+                                goto out;
+
+                        }
+
+                        port_id = true;
+
+                        break;
+                case LLDP_TYPE_TTL:
+
+                        if(len != 2) {
+                                log_lldp("Received invalid TTL TLV lenth: %d. Dropping.", len);
+
+                                malformed = true;
+                                goto out;
+                        }
+
+                        if (ttl) {
+                                log_lldp("Duplicate TTL TLV found. Dropping.");
+
+                                malformed = true;
+                                goto out;
+                        }
+
+                        ttl = true;
+
+                        break;
+                case LLDP_TYPE_SYSTEM_NAME:
+
+                        /* According to RFC 1035 the length of a FQDN is limited to 255 characters */
+                        if (len > 255) {
+                                log_lldp("Received invalid system name length: %d. Dropping.", len);
+                                malformed = true;
+                                goto out;
+                        }
+
+                        if (system_name) {
+                                log_lldp("Duplicate system name found. Dropping.");
+                                malformed = true;
+                                goto out;
+                        }
+
+                        system_name = true;
+
+                        break;
+                case LLDP_TYPE_SYSTEM_DESCRIPTION:
+
+                        /* 0 <= n <= 255 octets */
+                        if (len > 255) {
+                                log_lldp("Received invalid system description length: %d. Dropping.", len);
+                                malformed = true;
+                                goto out;
+                        }
+
+                        if (system_description) {
+                                log_lldp("Duplicate system description found. Dropping.");
+                                malformed = true;
+                                goto out;
+                        }
+
+                        system_description = true;
+                        break;
+                default:
+
+                        if (len == 0) {
+                                log_lldp("TLV type: %d length 0 received. Dropping.", type);
+
+                                malformed = true;
+                                goto out;
+                        }
+                        break;
+                }
+        }
+
+        if(!chassis_id || !port_id || !ttl || !end) {
+                log_lldp("One or more mandatory TLV missing. Dropping.");
+
+                malformed = true;
+                goto out;
+
+        }
+
+        r = tlv_packet_parse_pdu(tlv, length);
+        if (r < 0) {
+                log_lldp("Failed to parse the TLV. Dropping.");
+
+                malformed = true;
+                goto out;
+        }
+
+        return lldp_receive_frame(lldp, tlv);
+
+ out:
+        lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+
+        if (malformed) {
+                lldp->statistics.stats_frames_discarded_total ++;
+                lldp->statistics.stats_frames_in_errors_total ++;
+        }
+
+        sd_lldp_packet_unref(tlv);
+
+        return 0;
+}
+
+static int ttl_expiry_item_prioq_compare_func(const void *a, const void *b) {
+        const lldp_neighbour_port *p = a, *q = b;
+
+        if (p->until < q->until)
+                return -1;
+
+        if (p->until > q->until)
+                return 1;
+
+        return 0;
+}
+
+static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state) {
+
+        assert(lldp);
+        assert(state < _LLDP_AGENT_RX_STATE_MAX);
+
+        lldp->rx_state = state;
+
+        lldp_run_state_machine(lldp);
+}
+
+static void lldp_run_state_machine(sd_lldp *lldp) {
+        if (!lldp->cb)
+                return;
+
+        switch (lldp->rx_state) {
+        case LLDP_AGENT_RX_UPDATE_INFO:
+                lldp->cb(lldp, SD_LLDP_EVENT_UPDATE_INFO, lldp->userdata);
+                break;
+        default:
+                break;
+        }
+}
+
+/* 10.5.5.2.1 mibDeleteObjects ()
+ * The mibDeleteObjects () procedure deletes all information in the LLDP remote
+ * systems MIB associated with the MSAP identifier if an LLDPDU is received with
+ * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
+
+static void lldp_mib_delete_objects(sd_lldp *lldp) {
+        lldp_neighbour_port *p;
+        usec_t t = 0;
+
+        /* Remove all entries that are past their TTL */
+        for (;;) {
+
+                if (prioq_size(lldp->by_expiry) <= 0)
+                        break;
+
+                p = prioq_peek(lldp->by_expiry);
+                if (!p)
+                        break;
+
+                if (t <= 0)
+                        t = now(clock_boottime_or_monotonic());
+
+                if (p->until > t)
+                        break;
+
+                lldp_neighbour_port_remove_and_free(p);
+
+                lldp->statistics.stats_ageouts_total ++;
+        }
+}
+
+static void lldp_mib_objects_flush(sd_lldp *lldp) {
+        lldp_neighbour_port *p, *q;
+        lldp_chassis *c;
+
+        assert(lldp);
+        assert(lldp->neighbour_mib);
+        assert(lldp->by_expiry);
+
+        /* Drop all packets */
+        while ((c = hashmap_steal_first(lldp->neighbour_mib))) {
+
+                LIST_FOREACH_SAFE(port, p, q, c->ports) {
+                        lldp_neighbour_port_remove_and_free(p);
+                }
+        }
+
+        assert(hashmap_size(lldp->neighbour_mib) == 0);
+        assert(prioq_size(lldp->by_expiry) == 0);
+}
+
+int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        uint8_t *mac, *port_id, type;
+        lldp_neighbour_port *p;
+        uint16_t data = 0, length = 0;
+        char buf[LINE_MAX];
+        lldp_chassis *c;
+        usec_t time;
+        Iterator i;
+        int r;
+
+        assert(lldp);
+        assert(lldp_file);
+
+        r = fopen_temporary(lldp_file, &f, &temp_path);
+        if (r < 0)
+                goto fail;
+
+        fchmod(fileno(f), 0644);
+
+        HASHMAP_FOREACH(c, lldp->neighbour_mib, i) {
+                LIST_FOREACH(port, p, c->ports) {
+                        _cleanup_free_ char *s = NULL;
+                        char *k, *t;
+
+                        r = sd_lldp_packet_read_chassis_id(p->packet, &type, &mac, &length);
+                        if (r < 0)
+                                continue;
+
+                        sprintf(buf, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
+                                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
+
+                        s = strdup(buf);
+                        if (!s) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        r = sd_lldp_packet_read_port_id(p->packet, &type, &port_id, &length);
+                        if (r < 0)
+                                continue;
+
+                        if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) {
+                                k = strndup((char *) port_id, length -1);
+                                if (!k) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type);
+                                free(k);
+                        } else {
+                                mac = port_id;
+                                sprintf(buf, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
+                                        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
+                        }
+
+                        k = strappend(s, buf);
+                        if (!k) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        free(s);
+                        s = k;
+
+                        time = now(clock_boottime_or_monotonic());
+
+                        /* Don't write expired packets */
+                        if (time - p->until <= 0)
+                                continue;
+
+                        sprintf(buf, "'_TTL="USEC_FMT"' ", p->until);
+
+                        k = strappend(s, buf);
+                        if (!k) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        free(s);
+                        s = k;
+
+                        r = sd_lldp_packet_read_system_name(p->packet, &k, &length);
+                        if (r < 0)
+                                k = strappend(s, "'_NAME=N/A' ");
+                        else {
+                                t = strndup(k, length);
+                                if (!t) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                k = strjoin(s, "'_NAME=", t, "' ", NULL);
+                                free(t);
+                        }
+
+                        if (!k) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        free(s);
+                        s = k;
+
+                        (void) sd_lldp_packet_read_system_capability(p->packet, &data);
+
+                        sprintf(buf, "'_CAP=%x'", data);
+
+                        k = strappend(s, buf);
+                        if (!k) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        free(s);
+                        s = k;
+
+                        fprintf(f, "%s\n", s);
+                }
+        }
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                goto fail;
+
+        if (rename(temp_path, lldp_file) < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        return 0;
+
+ fail:
+        if (temp_path)
+                (void) unlink(temp_path);
+
+        return log_error_errno(r, "Failed to save lldp data %s: %m", lldp_file);
+}
+
+int sd_lldp_start(sd_lldp *lldp) {
+        int r;
+
+        assert_return(lldp, -EINVAL);
+        assert_return(lldp->port, -EINVAL);
+
+        lldp->port->status = LLDP_PORT_STATUS_ENABLED;
+
+        lldp_set_state(lldp, LLDP_AGENT_RX_LLDP_INITIALIZE);
+
+        r = lldp_port_start(lldp->port);
+        if (r < 0) {
+                log_lldp("Failed to start Port : %s , %s",
+                         lldp->port->ifname,
+                         strerror(-r));
+
+                lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL);
+
+                return r;
+        }
+
+        lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+
+        return 0;
+}
+
+int sd_lldp_stop(sd_lldp *lldp) {
+        int r;
+
+        assert_return(lldp, -EINVAL);
+        assert_return(lldp->port, -EINVAL);
+
+        lldp->port->status = LLDP_PORT_STATUS_DISABLED;
+
+        r = lldp_port_stop(lldp->port);
+        if (r < 0)
+                return r;
+
+        lldp_mib_objects_flush(lldp);
+
+        return 0;
+}
+
+int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) {
+        int r;
+
+        assert_return(lldp, -EINVAL);
+        assert_return(!lldp->port->event, -EBUSY);
+
+        if (event)
+                lldp->port->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&lldp->port->event);
+                if (r < 0)
+                        return r;
+        }
+
+        lldp->port->event_priority = priority;
+
+        return 0;
+}
+
+int sd_lldp_detach_event(sd_lldp *lldp) {
+
+        assert_return(lldp, -EINVAL);
+
+        lldp->port->event = sd_event_unref(lldp->port->event);
+
+        return 0;
+}
+
+int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) {
+        assert_return(lldp, -EINVAL);
+
+        lldp->cb = cb;
+        lldp->userdata = userdata;
+
+        return 0;
+}
+
+sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
+
+        if (!lldp)
+                return NULL;
+
+        /* Drop all packets */
+        lldp_mib_objects_flush(lldp);
+
+        lldp_port_free(lldp->port);
+
+        hashmap_free(lldp->neighbour_mib);
+        prioq_free(lldp->by_expiry);
+
+        free(lldp);
+        return NULL;
+}
+
+int sd_lldp_new(int ifindex,
+                const char *ifname,
+                const struct ether_addr *mac,
+                sd_lldp **ret) {
+        _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
+        assert_return(ifname, -EINVAL);
+        assert_return(mac, -EINVAL);
+
+        lldp = new0(sd_lldp, 1);
+        if (!lldp)
+                return -ENOMEM;
+
+        r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port);
+        if (r < 0)
+                return r;
+
+        lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops);
+        if (!lldp->neighbour_mib)
+                return -ENOMEM;
+
+        r = prioq_ensure_allocated(&lldp->by_expiry,
+                                   ttl_expiry_item_prioq_compare_func);
+        if (r < 0)
+                return r;
+
+        lldp->rx_state = LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL;
+
+        *ret = lldp;
+        lldp = NULL;
+
+        return 0;
+}
+
+int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs) {
+        lldp_neighbour_port *p;
+        lldp_chassis *c;
+        Iterator iter;
+        unsigned count = 0, i;
+
+        assert_return(lldp, -EINVAL);
+        assert_return(tlvs, -EINVAL);
+
+        HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
+                LIST_FOREACH(port, p, c->ports)
+                        count++;
+        }
+
+        if (!count) {
+                *tlvs = NULL;
+                return 0;
+        }
+
+        *tlvs = new(sd_lldp_packet *, count);
+        if (!*tlvs)
+                return -ENOMEM;
+
+        i = 0;
+        HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
+                LIST_FOREACH(port, p, c->ports)
+                        (*tlvs)[i++] = sd_lldp_packet_ref(p->packet);
+        }
+
+        return count;
+}
diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
new file mode 100644
index 00000000..bda3b735
--- /dev/null
+++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
@@ -0,0 +1,233 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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 "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "sd-id128.h"
+
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "io-util.h"
+#include "macro.h"
+#include "random-util.h"
+#include "util.h"
+
+#if 0 /* NM_IGNORED */
+_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
+        unsigned n;
+
+        assert_return(s, NULL);
+
+        for (n = 0; n < 16; n++) {
+                s[n*2] = hexchar(id.bytes[n] >> 4);
+                s[n*2+1] = hexchar(id.bytes[n] & 0xF);
+        }
+
+        s[32] = 0;
+
+        return s;
+}
+
+_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
+        unsigned n, i;
+        sd_id128_t t;
+        bool is_guid = false;
+
+        assert_return(s, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        for (n = 0, i = 0; n < 16;) {
+                int a, b;
+
+                if (s[i] == '-') {
+                        /* Is this a GUID? Then be nice, and skip over
+                         * the dashes */
+
+                        if (i == 8)
+                                is_guid = true;
+                        else if (i == 13 || i == 18 || i == 23) {
+                                if (!is_guid)
+                                        return -EINVAL;
+                        } else
+                                return -EINVAL;
+
+                        i++;
+                        continue;
+                }
+
+                a = unhexchar(s[i++]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unhexchar(s[i++]);
+                if (b < 0)
+                        return -EINVAL;
+
+                t.bytes[n++] = (a << 4) | b;
+        }
+
+        if (i != (is_guid ? 36 : 32))
+                return -EINVAL;
+
+        if (s[i] != 0)
+                return -EINVAL;
+
+        *ret = t;
+        return 0;
+}
+
+static sd_id128_t make_v4_uuid(sd_id128_t id) {
+        /* Stolen from generate_random_uuid() of drivers/char/random.c
+         * in the kernel sources */
+
+        /* Set UUID version to 4 --- truly random generation */
+        id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
+
+        /* Set the UUID variant to DCE */
+        id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
+
+        return id;
+}
+#endif
+
+_public_ int sd_id128_get_machine(sd_id128_t *ret) {
+        static thread_local sd_id128_t saved_machine_id;
+        static thread_local bool saved_machine_id_valid = false;
+        _cleanup_close_ int fd = -1;
+        char buf[33];
+        unsigned j;
+        sd_id128_t t;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        if (saved_machine_id_valid) {
+                *ret = saved_machine_id;
+                return 0;
+        }
+
+        fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return -errno;
+
+        r = loop_read_exact(fd, buf, 33, false);
+        if (r < 0)
+                return r;
+        if (buf[32] !='\n')
+                return -EIO;
+
+        for (j = 0; j < 16; j++) {
+                int a, b;
+
+                a = unhexchar(buf[j*2]);
+                b = unhexchar(buf[j*2+1]);
+
+                if (a < 0 || b < 0)
+                        return -EIO;
+
+                t.bytes[j] = a << 4 | b;
+        }
+
+        saved_machine_id = t;
+        saved_machine_id_valid = true;
+
+        *ret = t;
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+_public_ int sd_id128_get_boot(sd_id128_t *ret) {
+        static thread_local sd_id128_t saved_boot_id;
+        static thread_local bool saved_boot_id_valid = false;
+        _cleanup_close_ int fd = -1;
+        char buf[36];
+        unsigned j;
+        sd_id128_t t;
+        char *p;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        if (saved_boot_id_valid) {
+                *ret = saved_boot_id;
+                return 0;
+        }
+
+        fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return -errno;
+
+        r = loop_read_exact(fd, buf, 36, false);
+        if (r < 0)
+                return r;
+
+        for (j = 0, p = buf; j < 16; j++) {
+                int a, b;
+
+                if (p >= buf + 35)
+                        return -EIO;
+
+                if (*p == '-') {
+                        p++;
+                        if (p >= buf + 35)
+                                return -EIO;
+                }
+
+                a = unhexchar(p[0]);
+                b = unhexchar(p[1]);
+
+                if (a < 0 || b < 0)
+                        return -EIO;
+
+                t.bytes[j] = a << 4 | b;
+
+                p += 2;
+        }
+
+        saved_boot_id = t;
+        saved_boot_id_valid = true;
+
+        *ret = t;
+        return 0;
+}
+
+_public_ int sd_id128_randomize(sd_id128_t *ret) {
+        sd_id128_t t;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        r = dev_urandom(&t, sizeof(t));
+        if (r < 0)
+                return r;
+
+        /* Turn this into a valid v4 UUID, to be nice. Note that we
+         * only guarantee this for newly generated UUIDs, not for
+         * pre-existing ones. */
+
+        *ret = make_v4_uuid(t);
+        return 0;
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c
new file mode 100644
index 00000000..3a0d9e86
--- /dev/null
+++ b/src/systemd/src/shared/dns-domain.c
@@ -0,0 +1,1249 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#ifdef HAVE_LIBIDN
+#include <idna.h>
+#include <stringprep.h>
+#endif
+
+#include <endian.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "hashmap.h"
+#include "hexdecoct.h"
+#include "in-addr-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "utf8.h"
+
+int dns_label_unescape(const char **name, char *dest, size_t sz) {
+        const char *n;
+        char *d;
+        int r = 0;
+
+        assert(name);
+        assert(*name);
+
+        n = *name;
+        d = dest;
+
+        for (;;) {
+                if (*n == '.') {
+                        n++;
+                        break;
+                }
+
+                if (*n == 0)
+                        break;
+
+                if (r >= DNS_LABEL_MAX)
+                        return -EINVAL;
+
+                if (sz <= 0)
+                        return -ENOBUFS;
+
+                if (*n == '\\') {
+                        /* Escaped character */
+
+                        n++;
+
+                        if (*n == 0)
+                                /* Ending NUL */
+                                return -EINVAL;
+
+                        else if (*n == '\\' || *n == '.') {
+                                /* Escaped backslash or dot */
+
+                                if (d)
+                                        *(d++) = *n;
+                                sz--;
+                                r++;
+                                n++;
+
+                        } else if (n[0] >= '0' && n[0] <= '9') {
+                                unsigned k;
+
+                                /* Escaped literal ASCII character */
+
+                                if (!(n[1] >= '0' && n[1] <= '9') ||
+                                    !(n[2] >= '0' && n[2] <= '9'))
+                                        return -EINVAL;
+
+                                k = ((unsigned) (n[0] - '0') * 100) +
+                                        ((unsigned) (n[1] - '0') * 10) +
+                                        ((unsigned) (n[2] - '0'));
+
+                                /* Don't allow anything that doesn't
+                                 * fit in 8bit. Note that we do allow
+                                 * control characters, as some servers
+                                 * (e.g. cloudflare) are happy to
+                                 * generate labels with them
+                                 * inside. */
+                                if (k > 255)
+                                        return -EINVAL;
+
+                                if (d)
+                                        *(d++) = (char) k;
+                                sz--;
+                                r++;
+
+                                n += 3;
+                        } else
+                                return -EINVAL;
+
+                } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
+
+                        /* Normal character */
+
+                        if (d)
+                                *(d++) = *n;
+                        sz--;
+                        r++;
+                        n++;
+                } else
+                        return -EINVAL;
+        }
+
+        /* Empty label that is not at the end? */
+        if (r == 0 && *n)
+                return -EINVAL;
+
+        if (sz >= 1 && d)
+                *d = 0;
+
+        *name = n;
+        return r;
+}
+
+/* @label_terminal: terminal character of a label, updated to point to the terminal character of
+ *                  the previous label (always skipping one dot) or to NULL if there are no more
+ *                  labels. */
+int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
+        const char *terminal;
+        int r;
+
+        assert(name);
+        assert(label_terminal);
+        assert(dest);
+
+        /* no more labels */
+        if (!*label_terminal) {
+                if (sz >= 1)
+                        *dest = 0;
+
+                return 0;
+        }
+
+        terminal = *label_terminal;
+        assert(*terminal == '.' || *terminal == 0);
+
+        /* Skip current terminal character (and accept domain names ending it ".") */
+        if (*terminal == 0)
+                terminal--;
+        if (terminal >= name && *terminal == '.')
+                terminal--;
+
+        /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
+        for (;;) {
+                if (terminal < name) {
+                        /* Reached the first label, so indicate that there are no more */
+                        terminal = NULL;
+                        break;
+                }
+
+                /* Find the start of the last label */
+                if (*terminal == '.') {
+                        const char *y;
+                        unsigned slashes = 0;
+
+                        for (y = terminal - 1; y >= name && *y == '\\'; y--)
+                                slashes ++;
+
+                        if (slashes % 2 == 0) {
+                                /* The '.' was not escaped */
+                                name = terminal + 1;
+                                break;
+                        } else {
+                                terminal = y;
+                                continue;
+                        }
+                }
+
+                terminal --;
+        }
+
+        r = dns_label_unescape(&name, dest, sz);
+        if (r < 0)
+                return r;
+
+        *label_terminal = terminal;
+
+        return r;
+}
+
+int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
+        char *q;
+
+        /* DNS labels must be between 1 and 63 characters long. A
+         * zero-length label does not exist. See RFC 2182, Section
+         * 11. */
+
+        if (l <= 0 || l > DNS_LABEL_MAX)
+                return -EINVAL;
+        if (sz < 1)
+                return -ENOBUFS;
+
+        assert(p);
+        assert(dest);
+
+        q = dest;
+        while (l > 0) {
+
+                if (*p == '.' || *p == '\\') {
+
+                        /* Dot or backslash */
+
+                        if (sz < 3)
+                                return -ENOBUFS;
+
+                        *(q++) = '\\';
+                        *(q++) = *p;
+
+                        sz -= 2;
+
+                } else if (*p == '_' ||
+                           *p == '-' ||
+                           (*p >= '0' && *p <= '9') ||
+                           (*p >= 'a' && *p <= 'z') ||
+                           (*p >= 'A' && *p <= 'Z')) {
+
+                        /* Proper character */
+
+                        if (sz < 2)
+                                return -ENOBUFS;
+
+                        *(q++) = *p;
+                        sz -= 1;
+
+                } else {
+
+                        /* Everything else */
+
+                        if (sz < 5)
+                                return -ENOBUFS;
+
+                        *(q++) = '\\';
+                        *(q++) = '0' + (char) ((uint8_t) *p / 100);
+                        *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
+                        *(q++) = '0' + (char) ((uint8_t) *p % 10);
+
+                        sz -= 4;
+
+                }
+
+                p++;
+                l--;
+        }
+
+        *q = 0;
+        return (int) (q - dest);
+}
+
+int dns_label_escape_new(const char *p, size_t l, char **ret) {
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        assert(p);
+        assert(ret);
+
+        if (l <= 0 || l > DNS_LABEL_MAX)
+                return -EINVAL;
+
+        s = new(char, DNS_LABEL_ESCAPED_MAX);
+        if (!s)
+                return -ENOMEM;
+
+        r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
+        if (r < 0)
+                return r;
+
+        *ret = s;
+        s = NULL;
+
+        return r;
+}
+
+int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
+#ifdef HAVE_LIBIDN
+        _cleanup_free_ uint32_t *input = NULL;
+        size_t input_size, l;
+        const char *p;
+        bool contains_8bit = false;
+        char buffer[DNS_LABEL_MAX+1];
+
+        assert(encoded);
+        assert(decoded);
+
+        /* Converts an U-label into an A-label */
+
+        if (encoded_size <= 0)
+                return -EINVAL;
+
+        for (p = encoded; p < encoded + encoded_size; p++)
+                if ((uint8_t) *p > 127)
+                        contains_8bit = true;
+
+        if (!contains_8bit) {
+                if (encoded_size > DNS_LABEL_MAX)
+                        return -EINVAL;
+
+                return 0;
+        }
+
+        input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
+        if (!input)
+                return -ENOMEM;
+
+        if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0)
+                return -EINVAL;
+
+        l = strlen(buffer);
+
+        /* Verify that the the result is not longer than one DNS label. */
+        if (l <= 0 || l > DNS_LABEL_MAX)
+                return -EINVAL;
+        if (l > decoded_max)
+                return -ENOBUFS;
+
+        memcpy(decoded, buffer, l);
+
+        /* If there's room, append a trailing NUL byte, but only then */
+        if (decoded_max > l)
+                decoded[l] = 0;
+
+        return (int) l;
+#else
+        return 0;
+#endif
+}
+
+int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
+#ifdef HAVE_LIBIDN
+        size_t input_size, output_size;
+        _cleanup_free_ uint32_t *input = NULL;
+        _cleanup_free_ char *result = NULL;
+        uint32_t *output = NULL;
+        size_t w;
+
+        /* To be invoked after unescaping. Converts an A-label into an U-label. */
+
+        assert(encoded);
+        assert(decoded);
+
+        if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX)
+                return -EINVAL;
+
+        if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
+                return 0;
+
+        if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0)
+                return 0;
+
+        input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
+        if (!input)
+                return -ENOMEM;
+
+        output_size = input_size;
+        output = newa(uint32_t, output_size);
+
+        idna_to_unicode_44i(input, input_size, output, &output_size, 0);
+
+        result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
+        if (!result)
+                return -ENOMEM;
+        if (w <= 0)
+                return -EINVAL;
+        if (w > decoded_max)
+                return -ENOBUFS;
+
+        memcpy(decoded, result, w);
+
+        /* Append trailing NUL byte if there's space, but only then. */
+        if (decoded_max > w)
+                decoded[w] = 0;
+
+        return w;
+#else
+        return 0;
+#endif
+}
+
+int dns_name_concat(const char *a, const char *b, char **_ret) {
+        _cleanup_free_ char *ret = NULL;
+        size_t n = 0, allocated = 0;
+        const char *p = a;
+        bool first = true;
+        int r;
+
+        assert(a);
+
+        for (;;) {
+                char label[DNS_LABEL_MAX];
+                int k;
+
+                r = dns_label_unescape(&p, label, sizeof(label));
+                if (r < 0)
+                        return r;
+                if (r == 0) {
+                        if (*p != 0)
+                                return -EINVAL;
+
+                        if (b) {
+                                /* Now continue with the second string, if there is one */
+                                p = b;
+                                b = NULL;
+                                continue;
+                        }
+
+                        break;
+                }
+
+                k = dns_label_undo_idna(label, r, label, sizeof(label));
+                if (k < 0)
+                        return k;
+                if (k > 0)
+                        r = k;
+
+                if (_ret) {
+                        if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
+                                return -ENOMEM;
+
+                        r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
+                        if (r < 0)
+                                return r;
+
+                        if (!first)
+                                ret[n] = '.';
+                } else {
+                        char escaped[DNS_LABEL_ESCAPED_MAX];
+
+                        r = dns_label_escape(label, r, escaped, sizeof(escaped));
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!first)
+                        n++;
+                else
+                        first = false;
+
+                n += r;
+        }
+
+        if (n > DNS_HOSTNAME_MAX)
+                return -EINVAL;
+
+        if (_ret) {
+                if (!GREEDY_REALLOC(ret, allocated, n + 1))
+                        return -ENOMEM;
+
+                ret[n] = 0;
+                *_ret = ret;
+                ret = NULL;
+        }
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+void dns_name_hash_func(const void *s, struct siphash *state) {
+        const char *p = s;
+        int r;
+
+        assert(p);
+
+        while (*p) {
+                char label[DNS_LABEL_MAX+1];
+                int k;
+
+                r = dns_label_unescape(&p, label, sizeof(label));
+                if (r < 0)
+                        break;
+
+                k = dns_label_undo_idna(label, r, label, sizeof(label));
+                if (k < 0)
+                        break;
+                if (k > 0)
+                        r = k;
+
+                if (r == 0)
+                        break;
+
+                label[r] = 0;
+                ascii_strlower(label);
+
+                string_hash_func(label, state);
+        }
+
+        /* enforce that all names are terminated by the empty label */
+        string_hash_func("", state);
+}
+
+int dns_name_compare_func(const void *a, const void *b) {
+        const char *x, *y;
+        int r, q, k, w;
+
+        assert(a);
+        assert(b);
+
+        x = (const char *) a + strlen(a);
+        y = (const char *) b + strlen(b);
+
+        for (;;) {
+                char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
+
+                if (x == NULL && y == NULL)
+                        return 0;
+
+                r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
+                q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
+                if (r < 0 || q < 0)
+                        return r - q;
+
+                k = dns_label_undo_idna(la, r, la, sizeof(la));
+                w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
+                if (k < 0 || w < 0)
+                        return k - w;
+                if (k > 0)
+                        r = k;
+                if (w > 0)
+                        q = w;
+
+                la[r] = lb[q] = 0;
+                r = strcasecmp(la, lb);
+                if (r != 0)
+                        return r;
+        }
+}
+
+const struct hash_ops dns_name_hash_ops = {
+        .hash = dns_name_hash_func,
+        .compare = dns_name_compare_func
+};
+
+int dns_name_equal(const char *x, const char *y) {
+        int r, q, k, w;
+
+        assert(x);
+        assert(y);
+
+        for (;;) {
+                char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
+
+                if (*x == 0 && *y == 0)
+                        return true;
+
+                r = dns_label_unescape(&x, la, sizeof(la));
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        k = dns_label_undo_idna(la, r, la, sizeof(la));
+                        if (k < 0)
+                                return k;
+                        if (k > 0)
+                                r = k;
+                }
+
+                q = dns_label_unescape(&y, lb, sizeof(lb));
+                if (q < 0)
+                        return q;
+                if (q > 0) {
+                        w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
+                        if (w < 0)
+                                return w;
+                        if (w > 0)
+                                q = w;
+                }
+
+                /* If one name had fewer labels than the other, this
+                 * will show up as empty label here, which the
+                 * strcasecmp() below will properly consider different
+                 * from a non-empty label. */
+
+                la[r] = lb[q] = 0;
+                if (strcasecmp(la, lb) != 0)
+                        return false;
+        }
+}
+
+int dns_name_endswith(const char *name, const char *suffix) {
+        const char *n, *s, *saved_n = NULL;
+        int r, q, k, w;
+
+        assert(name);
+        assert(suffix);
+
+        n = name;
+        s = suffix;
+
+        for (;;) {
+                char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
+
+                r = dns_label_unescape(&n, ln, sizeof(ln));
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
+                        if (k < 0)
+                                return k;
+                        if (k > 0)
+                                r = k;
+                }
+
+                if (!saved_n)
+                        saved_n = n;
+
+                q = dns_label_unescape(&s, ls, sizeof(ls));
+                if (q < 0)
+                        return q;
+                if (q > 0) {
+                        w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
+                        if (w < 0)
+                                return w;
+                        if (w > 0)
+                                q = w;
+                }
+
+                if (r == 0 && q == 0)
+                        return true;
+                if (r == 0 && saved_n == n)
+                        return false;
+
+                ln[r] = ls[q] = 0;
+
+                if (r != q || strcasecmp(ln, ls)) {
+
+                        /* Not the same, let's jump back, and try with the next label again */
+                        s = suffix;
+                        n = saved_n;
+                        saved_n = NULL;
+                }
+        }
+}
+
+int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
+        const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
+        int r, q, k, w;
+
+        assert(name);
+        assert(old_suffix);
+        assert(new_suffix);
+        assert(ret);
+
+        n = name;
+        s = old_suffix;
+
+        for (;;) {
+                char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
+
+                if (!saved_before)
+                        saved_before = n;
+
+                r = dns_label_unescape(&n, ln, sizeof(ln));
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
+                        if (k < 0)
+                                return k;
+                        if (k > 0)
+                                r = k;
+                }
+
+                if (!saved_after)
+                        saved_after = n;
+
+                q = dns_label_unescape(&s, ls, sizeof(ls));
+                if (q < 0)
+                        return q;
+                if (q > 0) {
+                        w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
+                        if (w < 0)
+                                return w;
+                        if (w > 0)
+                                q = w;
+                }
+
+                if (r == 0 && q == 0)
+                        break;
+                if (r == 0 && saved_after == n) {
+                        *ret = NULL; /* doesn't match */
+                        return 0;
+                }
+
+                ln[r] = ls[q] = 0;
+
+                if (r != q || strcasecmp(ln, ls)) {
+
+                        /* Not the same, let's jump back, and try with the next label again */
+                        s = old_suffix;
+                        n = saved_after;
+                        saved_after = saved_before = NULL;
+                }
+        }
+
+        /* Found it! Now generate the new name */
+        prefix = strndupa(name, saved_before - name);
+
+        r = dns_name_concat(prefix, new_suffix, ret);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int dns_name_between(const char *a, const char *b, const char *c) {
+        int n;
+
+        /* Determine if b is strictly greater than a and strictly smaller than c.
+           We consider the order of names to be circular, so that if a is
+           strictly greater than c, we consider b to be between them if it is
+           either greater than a or smaller than c. This is how the canonical
+           DNS name order used in NSEC records work. */
+
+        n = dns_name_compare_func(a, c);
+        if (n == 0)
+                return -EINVAL;
+        else if (n < 0)
+                /*       a<---b--->c       */
+                return dns_name_compare_func(a, b) < 0 &&
+                       dns_name_compare_func(b, c) < 0;
+        else
+                /* <--b--c         a--b--> */
+                return dns_name_compare_func(b, c) < 0 ||
+                       dns_name_compare_func(a, b) < 0;
+}
+
+int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
+        const uint8_t *p;
+        int r;
+
+        assert(a);
+        assert(ret);
+
+        p = (const uint8_t*) a;
+
+        if (family == AF_INET)
+                r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
+        else if (family == AF_INET6)
+                r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa",
+                             hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
+                             hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
+                             hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
+                             hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
+                             hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
+                             hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
+                             hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
+                             hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
+        else
+                return -EAFNOSUPPORT;
+        if (r < 0)
+                return -ENOMEM;
+
+        return 0;
+}
+
+int dns_name_address(const char *p, int *family, union in_addr_union *address) {
+        int r;
+
+        assert(p);
+        assert(family);
+        assert(address);
+
+        r = dns_name_endswith(p, "in-addr.arpa");
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                uint8_t a[4];
+                unsigned i;
+
+                for (i = 0; i < ELEMENTSOF(a); i++) {
+                        char label[DNS_LABEL_MAX+1];
+
+                        r = dns_label_unescape(&p, label, sizeof(label));
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                return -EINVAL;
+                        if (r > 3)
+                                return -EINVAL;
+
+                        r = safe_atou8(label, &a[i]);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = dns_name_equal(p, "in-addr.arpa");
+                if (r <= 0)
+                        return r;
+
+                *family = AF_INET;
+                address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
+                                             ((uint32_t) a[2] << 16) |
+                                             ((uint32_t) a[1] << 8) |
+                                              (uint32_t) a[0]);
+
+                return 1;
+        }
+
+        r = dns_name_endswith(p, "ip6.arpa");
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                struct in6_addr a;
+                unsigned i;
+
+                for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
+                        char label[DNS_LABEL_MAX+1];
+                        int x, y;
+
+                        r = dns_label_unescape(&p, label, sizeof(label));
+                        if (r <= 0)
+                                return r;
+                        if (r != 1)
+                                return -EINVAL;
+                        x = unhexchar(label[0]);
+                        if (x < 0)
+                                return -EINVAL;
+
+                        r = dns_label_unescape(&p, label, sizeof(label));
+                        if (r <= 0)
+                                return r;
+                        if (r != 1)
+                                return -EINVAL;
+                        y = unhexchar(label[0]);
+                        if (y < 0)
+                                return -EINVAL;
+
+                        a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
+                }
+
+                r = dns_name_equal(p, "ip6.arpa");
+                if (r <= 0)
+                        return r;
+
+                *family = AF_INET6;
+                address->in6 = a;
+                return 1;
+        }
+
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+bool dns_name_is_root(const char *name) {
+
+        assert(name);
+
+        /* There are exactly two ways to encode the root domain name:
+         * as empty string, or with a single dot. */
+
+        return STR_IN_SET(name, "", ".");
+}
+
+bool dns_name_is_single_label(const char *name) {
+        char label[DNS_LABEL_MAX+1];
+        int r;
+
+        assert(name);
+
+        r = dns_label_unescape(&name, label, sizeof(label));
+        if (r <= 0)
+                return false;
+
+        return dns_name_is_root(name);
+}
+
+/* Encode a domain name according to RFC 1035 Section 3.1, without compression */
+int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
+        uint8_t *label_length, *out;
+        int r;
+
+        assert(domain);
+        assert(buffer);
+
+        out = buffer;
+
+        do {
+                /* Reserve a byte for label length */
+                if (len <= 0)
+                        return -ENOBUFS;
+                len--;
+                label_length = out;
+                out++;
+
+                /* Convert and copy a single label. Note that
+                 * dns_label_unescape() returns 0 when it hits the end
+                 * of the domain name, which we rely on here to encode
+                 * the trailing NUL byte. */
+                r = dns_label_unescape(&domain, (char *) out, len);
+                if (r < 0)
+                        return r;
+
+                if (canonical) {
+                        size_t i;
+
+                        /* Optionally, output the name in DNSSEC
+                         * canonical format, as described in RFC 4034,
+                         * section 6.2. Or in other words: in
+                         * lower-case. */
+
+                        for (i = 0; i < (size_t) r; i++) {
+                                if (out[i] >= 'A' && out[i] <= 'Z')
+                                        out[i] = out[i] - 'A' + 'a';
+                        }
+                }
+
+                /* Fill label length, move forward */
+                *label_length = r;
+                out += r;
+                len -= r;
+
+        } while (r != 0);
+
+        /* Verify the maximum size of the encoded name. The trailing
+         * dot + NUL byte account are included this time, hence
+         * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
+         * time. */
+        if (out - buffer > DNS_HOSTNAME_MAX + 2)
+                return -EINVAL;
+
+        return out - buffer;
+}
+
+#if 0 /* NM_IGNORED */
+static bool srv_type_label_is_valid(const char *label, size_t n) {
+        size_t k;
+
+        assert(label);
+
+        if (n < 2) /* Label needs to be at least 2 chars long */
+                return false;
+
+        if (label[0] != '_') /* First label char needs to be underscore */
+                return false;
+
+        /* Second char must be a letter */
+        if (!(label[1] >= 'A' && label[1] <= 'Z') &&
+            !(label[1] >= 'a' && label[1] <= 'z'))
+                return false;
+
+        /* Third and further chars must be alphanumeric or a hyphen */
+        for (k = 2; k < n; k++) {
+                if (!(label[k] >= 'A' && label[k] <= 'Z') &&
+                    !(label[k] >= 'a' && label[k] <= 'z') &&
+                    !(label[k] >= '0' && label[k] <= '9') &&
+                    label[k] != '-')
+                        return false;
+        }
+
+        return true;
+}
+
+bool dns_srv_type_is_valid(const char *name) {
+        unsigned c = 0;
+        int r;
+
+        if (!name)
+                return false;
+
+        for (;;) {
+                char label[DNS_LABEL_MAX];
+
+                /* This more or less implements RFC 6335, Section 5.1 */
+
+                r = dns_label_unescape(&name, label, sizeof(label));
+                if (r < 0)
+                        return false;
+                if (r == 0)
+                        break;
+
+                if (c >= 2)
+                        return false;
+
+                if (!srv_type_label_is_valid(label, r))
+                        return false;
+
+                c++;
+        }
+
+        return c == 2; /* exactly two labels */
+}
+
+bool dns_service_name_is_valid(const char *name) {
+        size_t l;
+
+        /* This more or less implements RFC 6763, Section 4.1.1 */
+
+        if (!name)
+                return false;
+
+        if (!utf8_is_valid(name))
+                return false;
+
+        if (string_has_cc(name, NULL))
+                return false;
+
+        l = strlen(name);
+        if (l <= 0)
+                return false;
+        if (l > 63)
+                return false;
+
+        return true;
+}
+
+int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
+        char escaped[DNS_LABEL_ESCAPED_MAX];
+        _cleanup_free_ char *n = NULL;
+        int r;
+
+        assert(type);
+        assert(domain);
+        assert(ret);
+
+        if (!dns_srv_type_is_valid(type))
+                return -EINVAL;
+
+        if (!name)
+                return dns_name_concat(type, domain, ret);
+
+        if (!dns_service_name_is_valid(name))
+                return -EINVAL;
+
+        r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
+        if (r < 0)
+                return r;
+
+        r = dns_name_concat(type, domain, &n);
+        if (r < 0)
+                return r;
+
+        return dns_name_concat(escaped, n, ret);
+}
+
+static bool dns_service_name_label_is_valid(const char *label, size_t n) {
+        char *s;
+
+        assert(label);
+
+        if (memchr(label, 0, n))
+                return false;
+
+        s = strndupa(label, n);
+        return dns_service_name_is_valid(s);
+}
+
+int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
+        _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
+        const char *p = joined, *q = NULL, *d = NULL;
+        char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
+        int an, bn, cn, r;
+        unsigned x = 0;
+
+        assert(joined);
+
+        /* Get first label from the full name */
+        an = dns_label_unescape(&p, a, sizeof(a));
+        if (an < 0)
+                return an;
+
+        if (an > 0) {
+                x++;
+
+                /* If there was a first label, try to get the second one */
+                bn = dns_label_unescape(&p, b, sizeof(b));
+                if (bn < 0)
+                        return bn;
+
+                if (bn > 0) {
+                        x++;
+
+                        /* If there was a second label, try to get the third one */
+                        q = p;
+                        cn = dns_label_unescape(&p, c, sizeof(c));
+                        if (cn < 0)
+                                return cn;
+
+                        if (cn > 0)
+                                x++;
+                } else
+                        cn = 0;
+        } else
+                an = 0;
+
+        if (x >= 2 && srv_type_label_is_valid(b, bn)) {
+
+                if (x >= 3 && srv_type_label_is_valid(c, cn)) {
+
+                        if (dns_service_name_label_is_valid(a, an)) {
+
+                                /* OK, got <name> . <type> . <type2> . <domain> */
+
+                                name = strndup(a, an);
+                                if (!name)
+                                        return -ENOMEM;
+
+                                type = new(char, bn+1+cn+1);
+                                if (!type)
+                                        return -ENOMEM;
+                                strcpy(stpcpy(stpcpy(type, b), "."), c);
+
+                                d = p;
+                                goto finish;
+                        }
+
+                } else if (srv_type_label_is_valid(a, an)) {
+
+                        /* OK, got <type> . <type2> . <domain> */
+
+                        name = NULL;
+
+                        type = new(char, an+1+bn+1);
+                        if (!type)
+                                return -ENOMEM;
+                        strcpy(stpcpy(stpcpy(type, a), "."), b);
+
+                        d = q;
+                        goto finish;
+                }
+        }
+
+        name = NULL;
+        type = NULL;
+        d = joined;
+
+finish:
+        r = dns_name_normalize(d, &domain);
+        if (r < 0)
+                return r;
+
+        if (_domain) {
+                *_domain = domain;
+                domain = NULL;
+        }
+
+        if (_type) {
+                *_type = type;
+                type = NULL;
+        }
+
+        if (_name) {
+                *_name = name;
+                name = NULL;
+        }
+
+        return 0;
+}
+
+int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
+        const char* labels[DNS_N_LABELS_MAX+1];
+        unsigned n = 0;
+        const char *p;
+        int r;
+
+        assert(name);
+        assert(ret);
+
+        p = name;
+        for (;;) {
+                if (n > DNS_N_LABELS_MAX)
+                        return -EINVAL;
+
+                labels[n] = p;
+
+                r = dns_name_parent(&p);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                n++;
+        }
+
+        if (n < n_labels)
+                return -EINVAL;
+
+        *ret = labels[n - n_labels];
+        return (int) (n - n_labels);
+}
+
+int dns_name_count_labels(const char *name) {
+        unsigned n = 0;
+        const char *p;
+        int r;
+
+        assert(name);
+
+        p = name;
+        for (;;) {
+                r = dns_name_parent(&p);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (n >= DNS_N_LABELS_MAX)
+                        return -EINVAL;
+
+                n++;
+        }
+
+        return (int) n;
+}
+
+int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        while (n_labels > 0) {
+
+                r = dns_name_parent(&a);
+                if (r <= 0)
+                        return r;
+
+                n_labels --;
+        }
+
+        return dns_name_equal(a, b);
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h
new file mode 100644
index 00000000..54860fb8
--- /dev/null
+++ b/src/systemd/src/shared/dns-domain.h
@@ -0,0 +1,107 @@
+/*-*- 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/>.
+ ***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "hashmap.h"
+#include "in-addr-util.h"
+
+/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */
+#define DNS_LABEL_MAX 63
+
+/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */
+#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1)
+
+/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */
+#define DNS_HOSTNAME_MAX 253
+
+/* Maximum length of a full hostname, on the wire, including the final NUL byte */
+#define DNS_WIRE_FOMAT_HOSTNAME_MAX 255
+
+/* Maximum number of labels per valid hostname */
+#define DNS_N_LABELS_MAX 127
+
+int dns_label_unescape(const char **name, char *dest, size_t sz);
+int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
+int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
+int dns_label_escape_new(const char *p, size_t l, char **ret);
+
+static inline int dns_name_parent(const char **name) {
+        return dns_label_unescape(name, NULL, DNS_LABEL_MAX);
+}
+
+int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
+int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
+
+int dns_name_concat(const char *a, const char *b, char **ret);
+
+static inline int dns_name_normalize(const char *s, char **ret) {
+        /* dns_name_concat() normalizes as a side-effect */
+        return dns_name_concat(s, NULL, ret);
+}
+
+static inline int dns_name_is_valid(const char *s) {
+        int r;
+
+        /* dns_name_normalize() verifies as a side effect */
+        r = dns_name_normalize(s, NULL);
+        if (r == -EINVAL)
+                return 0;
+        if (r < 0)
+                return r;
+        return 1;
+}
+
+void dns_name_hash_func(const void *s, struct siphash *state);
+int dns_name_compare_func(const void *a, const void *b);
+extern const struct hash_ops dns_name_hash_ops;
+
+int dns_name_between(const char *a, const char *b, const char *c);
+int dns_name_equal(const char *x, const char *y);
+int dns_name_endswith(const char *name, const char *suffix);
+
+int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);
+
+int dns_name_reverse(int family, const union in_addr_union *a, char **ret);
+int dns_name_address(const char *p, int *family, union in_addr_union *a);
+
+bool dns_name_is_root(const char *name);
+bool dns_name_is_single_label(const char *name);
+
+int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical);
+
+bool dns_srv_type_is_valid(const char *name);
+bool dns_service_name_is_valid(const char *name);
+
+int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
+int dns_service_split(const char *joined, char **name, char **type, char **domain);
+
+int dns_name_suffix(const char *name, unsigned n_labels, const char **ret);
+int dns_name_count_labels(const char *name);
+
+int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b);
diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h
new file mode 100644
index 00000000..5960a1c3
--- /dev/null
+++ b/src/systemd/src/systemd/_sd-common.h
@@ -0,0 +1,87 @@
+/*-*- 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/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+/* 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 _sd_useless_struct_to_allow_trailing_semicolon_
+#  else
+#    define _SD_BEGIN_DECLARATIONS                              \
+        struct _sd_useless_struct_to_allow_trailing_semicolon_
+#  endif
+#endif
+
+#ifndef _SD_END_DECLARATIONS
+#  ifdef __cplusplus
+#    define _SD_END_DECLARATIONS                                \
+        }                                                       \
+        struct _sd_useless_cpp_struct_to_allow_trailing_semicolon_
+#  else
+#    define _SD_END_DECLARATIONS                                \
+        struct _sd_useless_struct_to_allow_trailing_semicolon_
+#  endif
+#endif
+
+#define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func)             \
+        static inline void func##p(type **p) {                  \
+                if (*p)                                         \
+                        func(*p);                               \
+        }                                                       \
+        struct _sd_useless_struct_to_allow_trailing_semicolon_
+
+#endif
diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h
new file mode 100644
index 00000000..0fad8f36
--- /dev/null
+++ b/src/systemd/src/systemd/sd-dhcp-client.h
@@ -0,0 +1,86 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#include "sd-dhcp-lease.h"
+#include "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+        SD_DHCP_CLIENT_EVENT_STOP               = 0,
+        SD_DHCP_CLIENT_EVENT_IP_ACQUIRE         = 1,
+        SD_DHCP_CLIENT_EVENT_IP_CHANGE          = 2,
+        SD_DHCP_CLIENT_EVENT_EXPIRED            = 3,
+        SD_DHCP_CLIENT_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);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_client, sd_dhcp_client_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h
new file mode 100644
index 00000000..eed38eeb
--- /dev/null
+++ b/src/systemd/src/systemd/sd-dhcp-lease.h
@@ -0,0 +1,67 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+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_t1(sd_dhcp_lease *lease, uint32_t *t1);
+int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2);
+int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr);
+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 **routes);
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
+int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
new file mode 100644
index 00000000..c18d91bf
--- /dev/null
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -0,0 +1,81 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <sys/types.h>
+
+#include "sd-dhcp6-lease.h"
+#include "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+        SD_DHCP6_CLIENT_EVENT_STOP                      = 0,
+        SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE             = 10,
+        SD_DHCP6_CLIENT_EVENT_RETRANS_MAX               = 11,
+        SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE                = 12,
+        SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST       = 13,
+};
+
+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_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address);
+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_information_request(sd_dhcp6_client *client, int enabled);
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled);
+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_is_running(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);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_client, sd_dhcp6_client_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h
new file mode 100644
index 00000000..149cb239
--- /dev/null
+++ b/src/systemd/src/systemd/sd-dhcp6-lease.h
@@ -0,0 +1,56 @@
+/*-*- 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-2015 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 <inttypes.h>
+#include <netinet/in.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp6_lease sd_dhcp6_lease;
+
+void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
+                               struct in6_addr *addr,
+                               uint32_t *lifetime_preferred,
+                               uint32_t *lifetime_valid);
+
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs);
+int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
+int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
+                                 struct in6_addr **addrs);
+int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
+
+sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
+sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_lease, sd_dhcp6_lease_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h
new file mode 100644
index 00000000..17b95244
--- /dev/null
+++ b/src/systemd/src/systemd/sd-event.h
@@ -0,0 +1,142 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#include <inttypes.h>
+#include <signal.h>
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+#include <sys/types.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_INITIAL,
+        SD_EVENT_ARMED,
+        SD_EVENT_PENDING,
+        SD_EVENT_RUNNING,
+        SD_EVENT_EXITING,
+        SD_EVENT_FINISHED,
+        SD_EVENT_PREPARING,
+};
+
+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 usec);
+int sd_event_dispatch(sd_event *e);
+int sd_event_run(sd_event *e, uint64_t usec);
+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);
+
+/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h
new file mode 100644
index 00000000..da1bb2fa
--- /dev/null
+++ b/src/systemd/src/systemd/sd-id128.h
@@ -0,0 +1,119 @@
+/*-*- 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 "nm-sd-adapt.h"
+
+#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 parameter 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;
+}
+
+_sd_pure_ static inline int sd_id128_is_null(sd_id128_t a) {
+        return a.qwords[0] == 0 && a.qwords[1] == 0;
+}
+
+#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-ipv4acd.h b/src/systemd/src/systemd/sd-ipv4acd.h
new file mode 100644
index 00000000..14d0e5a9
--- /dev/null
+++ b/src/systemd/src/systemd/sd-ipv4acd.h
@@ -0,0 +1,64 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdipv4acdfoo
+#define foosdipv4acdfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Axis Communications AB. All rights reserved.
+  Copyright (C) 2015 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 <net/ethernet.h>
+#include <netinet/in.h>
+
+#include "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+        SD_IPV4ACD_EVENT_STOP           = 0,
+        SD_IPV4ACD_EVENT_BIND           = 1,
+        SD_IPV4ACD_EVENT_CONFLICT       = 2,
+};
+
+typedef struct sd_ipv4acd sd_ipv4acd;
+typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata);
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority);
+int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata);
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
+int sd_ipv4acd_is_running(sd_ipv4acd *ll);
+int sd_ipv4acd_start(sd_ipv4acd *ll);
+int sd_ipv4acd_stop(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll);
+int sd_ipv4acd_new(sd_ipv4acd **ret);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4acd, sd_ipv4acd_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h
new file mode 100644
index 00000000..307aa6f7
--- /dev/null
+++ b/src/systemd/src/systemd/sd-ipv4ll.h
@@ -0,0 +1,64 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdipv4llfoo
+#define foosdipv4llfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Axis Communications AB. 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 "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+        SD_IPV4LL_EVENT_STOP            = 0,
+        SD_IPV4LL_EVENT_BIND            = 1,
+        SD_IPV4LL_EVENT_CONFLICT        = 2,
+};
+
+typedef struct sd_ipv4ll sd_ipv4ll;
+typedef void (*sd_ipv4ll_cb_t)(sd_ipv4ll *ll, int event, void *userdata);
+
+int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
+int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority);
+int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
+int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
+int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
+int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
+int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed);
+int sd_ipv4ll_is_running(sd_ipv4ll *ll);
+int sd_ipv4ll_start(sd_ipv4ll *ll);
+int sd_ipv4ll_stop(sd_ipv4ll *ll);
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
+int sd_ipv4ll_new (sd_ipv4ll **ret);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4ll, sd_ipv4ll_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h
new file mode 100644
index 00000000..d4e7a4fa
--- /dev/null
+++ b/src/systemd/src/systemd/sd-lldp.h
@@ -0,0 +1,91 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdlldphfoo
+#define foosdlldphfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
+
+  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 <inttypes.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+        SD_LLDP_EVENT_UPDATE_INFO       = 0,
+};
+
+enum {
+        SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE,
+        SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE,
+        SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE,
+};
+
+typedef struct sd_lldp sd_lldp;
+typedef struct sd_lldp_packet sd_lldp_packet;
+
+typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata);
+
+int sd_lldp_new(int ifindex, const char *ifname, const struct ether_addr *mac, sd_lldp **ret);
+sd_lldp* sd_lldp_unref(sd_lldp *lldp);
+
+int sd_lldp_start(sd_lldp *lldp);
+int sd_lldp_stop(sd_lldp *lldp);
+
+int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority);
+int sd_lldp_detach_event(sd_lldp *lldp);
+
+int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata);
+int sd_lldp_save(sd_lldp *lldp, const char *file);
+
+int sd_lldp_packet_read_chassis_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
+int sd_lldp_packet_read_port_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
+int sd_lldp_packet_read_ttl(sd_lldp_packet *tlv, uint16_t *ttl);
+int sd_lldp_packet_read_system_name(sd_lldp_packet *tlv, char **data, uint16_t *length);
+int sd_lldp_packet_read_system_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
+int sd_lldp_packet_read_system_capability(sd_lldp_packet *tlv, uint16_t *data);
+int sd_lldp_packet_read_port_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
+
+/* IEEE 802.1 organizationally specific TLVs */
+int sd_lldp_packet_read_port_vlan_id(sd_lldp_packet *tlv, uint16_t *id);
+int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id);
+int sd_lldp_packet_read_vlan_name(sd_lldp_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length);
+int sd_lldp_packet_read_management_vid(sd_lldp_packet *tlv, uint16_t *id);
+int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id);
+
+sd_lldp_packet *sd_lldp_packet_ref(sd_lldp_packet *tlv);
+sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv);
+
+int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest);
+
+int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_packet, sd_lldp_packet_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h
new file mode 100644
index 00000000..cc674336
--- /dev/null
+++ b/src/systemd/src/systemd/sd-ndisc.h
@@ -0,0 +1,88 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdndiscfoo
+#define foosdndiscfoo
+
+/***
+  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 <inttypes.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+        SD_NDISC_EVENT_STOP     = 0,
+        SD_NDISC_EVENT_TIMEOUT  = 1,
+};
+
+typedef struct sd_ndisc sd_ndisc;
+
+typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata);
+typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
+                                                 unsigned lifetime, void *userdata);
+typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
+                                                     unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata);
+typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata);
+
+int sd_ndisc_set_callback(sd_ndisc *nd,
+                          sd_ndisc_router_callback_t rcb,
+                          sd_ndisc_prefix_onlink_callback_t plcb,
+                          sd_ndisc_prefix_autonomous_callback_t pacb,
+                          sd_ndisc_callback_t cb,
+                          void *userdata);
+int sd_ndisc_set_index(sd_ndisc *nd, int interface_index);
+int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
+
+int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority);
+int sd_ndisc_detach_event(sd_ndisc *nd);
+sd_event *sd_ndisc_get_event(sd_ndisc *nd);
+
+sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);
+sd_ndisc *sd_ndisc_unref(sd_ndisc *nd);
+int sd_ndisc_new(sd_ndisc **ret);
+
+int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu);
+
+int sd_ndisc_stop(sd_ndisc *nd);
+int sd_ndisc_router_discovery_start(sd_ndisc *nd);
+
+#define SD_NDISC_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+
+#define SD_NDISC_ADDRESS_FORMAT_VAL(address) \
+        be16toh((address).s6_addr16[0]),        \
+        be16toh((address).s6_addr16[1]),        \
+        be16toh((address).s6_addr16[2]),        \
+        be16toh((address).s6_addr16[3]),        \
+        be16toh((address).s6_addr16[4]),        \
+        be16toh((address).s6_addr16[5]),        \
+        be16toh((address).s6_addr16[6]),        \
+        be16toh((address).s6_addr16[7])
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif