[systemd-commits] 2 commits - Makefile.am src/libsystemd-dhcp src/libsystemd-network src/network src/systemd

Tom Gundersen tomegun at kemper.freedesktop.org
Thu Feb 27 16:21:00 PST 2014


 Makefile.am                                  |   56 -
 src/libsystemd-dhcp/Makefile                 |    1 
 src/libsystemd-dhcp/dhcp-internal.h          |   55 -
 src/libsystemd-dhcp/dhcp-lease.c             |  244 ------
 src/libsystemd-dhcp/dhcp-lease.h             |   57 -
 src/libsystemd-dhcp/dhcp-network.c           |  109 --
 src/libsystemd-dhcp/dhcp-option.c            |  184 ----
 src/libsystemd-dhcp/dhcp-packet.c            |  190 ----
 src/libsystemd-dhcp/dhcp-protocol.h          |  121 ---
 src/libsystemd-dhcp/sd-dhcp-client.c         | 1051 ---------------------------
 src/libsystemd-dhcp/test-dhcp-client.c       |  496 ------------
 src/libsystemd-dhcp/test-dhcp-option.c       |  378 ---------
 src/libsystemd-network/Makefile              |    1 
 src/libsystemd-network/dhcp-internal.h       |   55 +
 src/libsystemd-network/dhcp-lease-internal.h |   59 +
 src/libsystemd-network/dhcp-network.c        |  109 ++
 src/libsystemd-network/dhcp-option.c         |  184 ++++
 src/libsystemd-network/dhcp-packet.c         |  191 ++++
 src/libsystemd-network/dhcp-protocol.h       |  121 +++
 src/libsystemd-network/sd-dhcp-client.c      | 1051 +++++++++++++++++++++++++++
 src/libsystemd-network/sd-dhcp-lease.c       |  401 ++++++++++
 src/libsystemd-network/test-dhcp-client.c    |  496 ++++++++++++
 src/libsystemd-network/test-dhcp-option.c    |  378 +++++++++
 src/network/networkd-link.c                  |   72 +
 src/network/networkd-wait-online.c           |  142 +++
 src/network/networkd.c                       |    5 
 src/network/networkd.h                       |    6 
 src/network/sd-network.c                     |  237 ++++++
 src/systemd/sd-dhcp-client.h                 |   12 
 src/systemd/sd-dhcp-lease.h                  |   41 +
 src/systemd/sd-network.h                     |   87 ++
 31 files changed, 3672 insertions(+), 2918 deletions(-)

New commits:
commit 020d59000f86b3d98be763eaee6a2671f0427e46
Author: Tom Gundersen <teg at jklm.no>
Date:   Fri Feb 28 01:02:01 2014 +0100

    networkd: add networkd-wait-online
    
    This is mostly a proof of concept to try sd-network, so we don't
    hook it up with a .service file quite yet. We probably want it to
    be more clever about deciding when we are 'online'.
    
    The binary  will wait for at least one network managed by networkd,
    and until all networks managed by networkd are configured.

diff --git a/Makefile.am b/Makefile.am
index b7c38b6..6be6407 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4047,6 +4047,16 @@ nodist_systemunit_DATA += \
 GENERAL_ALIASES += \
 	$(systemunitdir)/systemd-networkd.service $(pkgsysconfdir)/system/multi-user.target.wants/systemd-networkd.service
 
+rootlibexec_PROGRAMS += \
+	systemd-networkd-wait-online
+
+systemd_networkd_wait_online_SOURCES = \
+	src/network/networkd-wait-online.c
+
+systemd_networkd_wait_online_LDADD = \
+	libsystemd-internal.la \
+	libsystemd-network.la
+
 test_network_SOURCES = \
 	src/network/test-network.c
 
diff --git a/src/network/networkd-wait-online.c b/src/network/networkd-wait-online.c
new file mode 100644
index 0000000..900dc05
--- /dev/null
+++ b/src/network/networkd-wait-online.c
@@ -0,0 +1,142 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Tom Gundersen <teg at 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 "sd-event.h"
+#include "sd-daemon.h"
+#include "sd-network.h"
+
+#include "util.h"
+
+static bool all_configured(void) {
+        _cleanup_free_ unsigned *indices = NULL;
+        bool one_ready = false;
+        int r, n, i;
+
+        n = sd_network_get_ifindices(&indices);
+        if (n <= 0)
+                return false;
+
+        for (i = 0; i < n; i++) {
+                _cleanup_free_ char *state = NULL;
+
+                r = sd_network_get_link_state(indices[i], &state);
+                if (r < 0)
+                        return false;
+
+                if (streq(state, "configured"))
+                        one_ready = true;
+
+                if (!streq(state, "configured") && !streq(state, "unmanaged"))
+                        return false;
+        }
+
+        if (one_ready)
+                return true;
+
+        return false;
+}
+
+static int event_handler(sd_event_source *s, int fd, uint32_t revents,
+                         void *userdata) {
+        sd_event *event = userdata;
+
+        assert(event);
+
+        if (all_configured())
+                sd_event_exit(event, 0);
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        sd_event *event;
+        sd_event_source *event_source;
+        sd_network_monitor *monitor;
+        int r, fd, events;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if (argc != 1) {
+                log_error("This program takes no arguments.");
+                r = -EINVAL;
+                goto out;
+        }
+
+        r = sd_network_monitor_new(NULL, &monitor);
+        if (r < 0) {
+                log_error("Could not create monitor: %s", strerror(-r));
+                goto out;
+        }
+
+        r = sd_event_new(&event);
+        if (r < 0) {
+                log_error("Could not create event: %s", strerror(-r));
+                goto out;
+        }
+
+        fd = sd_network_monitor_get_fd(monitor);
+        if (fd < 0) {
+                log_error("Could not get monitor fd: %s", strerror(-r));
+                goto out;
+        }
+
+        events = sd_network_monitor_get_events(monitor);
+        if (events < 0) {
+                log_error("Could not get monitor events: %s", strerror(-r));
+                goto out;
+        }
+
+        r = sd_event_add_io(event, &event_source, fd, events, &event_handler,
+                            event);
+        if (r < 0) {
+                log_error("Could not add io event source: %s", strerror(-r));
+                goto out;
+        }
+
+        if (all_configured()) {
+                r = 0;
+                goto out;
+        }
+
+        sd_notify(false,
+                  "READY=1\n"
+                  "STATUS=Waiting for network connecitons...");
+
+        r = sd_event_loop(event);
+        if (r < 0) {
+                log_error("Event loop failed: %s", strerror(-r));
+                goto out;
+        }
+
+out:
+        sd_notify(false,
+                  "STATUS=All interfaces configured...");
+
+        sd_event_source_unref(event_source);
+        sd_event_unref(event);
+        sd_network_monitor_unref(monitor);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}

commit fe8db0c5ee3365a2fc80ee7ebffa238f9a0a2ae2
Author: Tom Gundersen <teg at jklm.no>
Date:   Thu Feb 27 01:24:05 2014 +0100

    sd-network: add new library
    
    This is similar to sd-login, but exposes the state of networkd rather than logind.
    
    Include it in libsystemd-dhcp and rename it to libsystemd-network.

diff --git a/Makefile.am b/Makefile.am
index 25b48e5..b7c38b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -196,6 +196,7 @@ AM_CPPFLAGS = \
 	-I $(top_srcdir)/src/libsystemd/sd-bus \
 	-I $(top_srcdir)/src/libsystemd/sd-event \
 	-I $(top_srcdir)/src/libsystemd/sd-rtnl \
+	-I $(top_srcdir)/src/libsystemd-network \
 	$(OUR_CPPFLAGS)
 
 AM_CFLAGS = $(OUR_CFLAGS)
@@ -2398,41 +2399,44 @@ busctl_CFLAGS = \
 
 # ------------------------------------------------------------------------------
 noinst_LTLIBRARIES += \
-	libsystemd-dhcp.la
+	libsystemd-network.la
 
-libsystemd_dhcp_la_SOURCES = \
+libsystemd_network_la_SOURCES = \
+	src/systemd/sd-network.h \
 	src/systemd/sd-dhcp-client.h \
-	src/libsystemd-dhcp/sd-dhcp-client.c \
-	src/libsystemd-dhcp/dhcp-lease.h \
-	src/libsystemd-dhcp/dhcp-lease.c \
-	src/libsystemd-dhcp/dhcp-network.c \
-	src/libsystemd-dhcp/dhcp-option.c \
-	src/libsystemd-dhcp/dhcp-packet.c \
-	src/libsystemd-dhcp/dhcp-internal.h \
-	src/libsystemd-dhcp/dhcp-protocol.h
-
-libsystemd_dhcp_la_LIBADD = \
+	src/systemd/sd-dhcp-lease.h \
+	src/network/sd-network.c \
+	src/libsystemd-network/sd-dhcp-client.c \
+	src/libsystemd-network/dhcp-network.c \
+	src/libsystemd-network/dhcp-option.c \
+	src/libsystemd-network/dhcp-packet.c \
+	src/libsystemd-network/dhcp-internal.h \
+	src/libsystemd-network/dhcp-protocol.h \
+	src/libsystemd-network/dhcp-lease-internal.h \
+	src/libsystemd-network/sd-dhcp-lease.c
+
+libsystemd_network_la_LIBADD = \
 	libsystemd-internal.la \
 	libsystemd-shared.la
 
 test_dhcp_option_SOURCES = \
-	src/libsystemd-dhcp/dhcp-protocol.h \
-	src/libsystemd-dhcp/dhcp-internal.h \
-	src/libsystemd-dhcp/test-dhcp-option.c
+	src/libsystemd-network/dhcp-protocol.h \
+	src/libsystemd-network/dhcp-internal.h \
+	src/libsystemd-network/test-dhcp-option.c
 
 test_dhcp_option_LDADD = \
-	libsystemd-dhcp.la \
+	libsystemd-network.la \
 	libsystemd-internal.la \
 	libsystemd-shared.la
 
 test_dhcp_client_SOURCES = \
 	src/systemd/sd-dhcp-client.h \
-	src/libsystemd-dhcp/dhcp-protocol.h \
-	src/libsystemd-dhcp/dhcp-internal.h \
-	src/libsystemd-dhcp/test-dhcp-client.c
+	src/libsystemd-network/dhcp-protocol.h \
+	src/libsystemd-network/dhcp-internal.h \
+	src/libsystemd-network/test-dhcp-client.c
 
 test_dhcp_client_LDADD = \
-	libsystemd-dhcp.la \
+	libsystemd-network.la \
 	libsystemd-internal.la \
 	libsystemd-shared.la
 
@@ -4033,7 +4037,7 @@ nodist_libsystemd_networkd_core_la_SOURCES = \
 libsystemd_networkd_core_la_LIBADD = \
 	libudev-internal.la \
 	libsystemd-internal.la \
-	libsystemd-dhcp.la \
+	libsystemd-network.la \
 	libsystemd-label.la \
 	libsystemd-shared.la
 
diff --git a/src/libsystemd-dhcp/Makefile b/src/libsystemd-dhcp/Makefile
deleted file mode 120000
index d0b0e8e..0000000
--- a/src/libsystemd-dhcp/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile
\ No newline at end of file
diff --git a/src/libsystemd-dhcp/dhcp-internal.h b/src/libsystemd-dhcp/dhcp-internal.h
deleted file mode 100644
index ce83b81..0000000
--- a/src/libsystemd-dhcp/dhcp-internal.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2013 Intel Corporation. All rights reserved.
-  Copyright (C) 2014 Tom Gundersen
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdint.h>
-#include <linux/if_packet.h>
-
-#include "socket-util.h"
-
-#include "dhcp-protocol.h"
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link);
-int dhcp_network_bind_udp_socket(int index, 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(uint8_t **buf, size_t *buflen, uint8_t code,
-                       size_t optlen, const void *optval);
-
-typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
-                                const uint8_t *option, void *user_data);
-
-int dhcp_option_parse(DHCPMessage *message, size_t len,
-                      dhcp_option_cb_t cb, void *user_data);
-
-int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type,
-                      uint8_t **opt, size_t *optlen);
-
-void dhcp_packet_append_ip_headers(DHCPPacket *packet, uint16_t len);
-
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
-
-#define log_dhcp_client(client, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT: " fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-dhcp/dhcp-lease.c b/src/libsystemd-dhcp/dhcp-lease.c
deleted file mode 100644
index c1f76aa..0000000
--- a/src/libsystemd-dhcp/dhcp-lease.c
+++ /dev/null
@@ -1,244 +0,0 @@
-/***
-  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 <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <net/ethernet.h>
-#include <sys/param.h>
-
-#include "util.h"
-#include "list.h"
-
-#include "dhcp-protocol.h"
-#include "dhcp-internal.h"
-#include "dhcp-lease.h"
-#include "sd-dhcp-client.h"
-
-int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        addr->s_addr = lease->address;
-
-        return 0;
-}
-
-int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
-        assert_return(lease, -EINVAL);
-        assert_return(mtu, -EINVAL);
-
-        if (lease->mtu)
-                *mtu = lease->mtu;
-        else
-                return -ENOENT;
-
-        return 0;
-}
-
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(addr_size, -EINVAL);
-
-        if (lease->dns_size) {
-                *addr_size = lease->dns_size;
-                *addr = lease->dns;
-        } else
-                return -ENOENT;
-
-        return 0;
-}
-
-int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
-        assert_return(lease, -EINVAL);
-        assert_return(domainname, -EINVAL);
-
-        if (lease->domainname)
-                *domainname = lease->domainname;
-        else
-                return -ENOENT;
-
-        return 0;
-}
-
-int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
-        assert_return(lease, -EINVAL);
-        assert_return(hostname, -EINVAL);
-
-        if (lease->hostname)
-                *hostname = lease->hostname;
-        else
-                return -ENOENT;
-
-        return 0;
-}
-
-int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        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);
-
-        addr->s_addr = lease->subnet_mask;
-
-        return 0;
-}
-
-sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
-        if (lease)
-                assert_se(REFCNT_INC(lease->n_ref) >= 2);
-
-        return lease;
-}
-
-sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
-        if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
-                free(lease->hostname);
-                free(lease->domainname);
-                free(lease->dns);
-                free(lease);
-        }
-
-        return NULL;
-}
-
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
-                              void *user_data) {
-        sd_dhcp_lease *lease = user_data;
-        be32_t val;
-
-        switch(code) {
-
-        case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
-                if (len == 4) {
-                        memcpy(&val, option, 4);
-                        lease->lifetime = be32toh(val);
-                }
-
-                break;
-
-        case DHCP_OPTION_SERVER_IDENTIFIER:
-                if (len >= 4)
-                        memcpy(&lease->server_address, option, 4);
-
-                break;
-
-        case DHCP_OPTION_SUBNET_MASK:
-                if (len >= 4)
-                        memcpy(&lease->subnet_mask, option, 4);
-
-                break;
-
-        case DHCP_OPTION_ROUTER:
-                if (len >= 4)
-                        memcpy(&lease->router, option, 4);
-
-                break;
-
-        case DHCP_OPTION_DOMAIN_NAME_SERVER:
-                if (len && !(len % 4)) {
-                        unsigned i;
-
-                        lease->dns_size = len / 4;
-
-                        free(lease->dns);
-                        lease->dns = new0(struct in_addr, lease->dns_size);
-                        if (!lease->dns)
-                                return -ENOMEM;
-
-                        for (i = 0; i < lease->dns_size; i++) {
-                                memcpy(&lease->dns[i].s_addr, option + 4 * i, 4);
-                        }
-                }
-
-                break;
-
-        case DHCP_OPTION_INTERFACE_MTU:
-                if (len >= 2) {
-                        be16_t mtu;
-
-                        memcpy(&mtu, option, 2);
-                        lease->mtu = be16toh(mtu);
-
-                        if (lease->mtu < 68)
-                                lease->mtu = 0;
-                }
-
-                break;
-
-        case DHCP_OPTION_DOMAIN_NAME:
-                if (len >= 1) {
-                        free(lease->domainname);
-                        lease->domainname = strndup((const char *)option, len);
-                }
-
-                break;
-
-        case DHCP_OPTION_HOST_NAME:
-                if (len >= 1) {
-                        free(lease->hostname);
-                        lease->hostname = strndup((const char *)option, len);
-                }
-
-                break;
-
-        case DHCP_OPTION_RENEWAL_T1_TIME:
-                if (len == 4) {
-                        memcpy(&val, option, 4);
-                        lease->t1 = be32toh(val);
-                }
-
-                break;
-
-        case DHCP_OPTION_REBINDING_T2_TIME:
-                if (len == 4) {
-                        memcpy(&val, option, 4);
-                        lease->t2 = be32toh(val);
-                }
-
-                break;
-        }
-
-        return 0;
-}
-
-int dhcp_lease_new(sd_dhcp_lease **ret) {
-        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
-
-        lease = new0(sd_dhcp_lease, 1);
-        if (!lease)
-                return -ENOMEM;
-
-        lease->n_ref = REFCNT_INIT;
-
-        *ret = lease;
-        lease = NULL;
-
-        return 0;
-}
diff --git a/src/libsystemd-dhcp/dhcp-lease.h b/src/libsystemd-dhcp/dhcp-lease.h
deleted file mode 100644
index 87323dc..0000000
--- a/src/libsystemd-dhcp/dhcp-lease.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2013 Intel Corporation. All rights reserved.
-  Copyright (C) 2014 Tom Gundersen
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdint.h>
-#include <linux/if_packet.h>
-
-#include "socket-util.h"
-#include "refcnt.h"
-
-#include "dhcp-protocol.h"
-
-#include "sd-dhcp-client.h"
-
-struct sd_dhcp_lease {
-        RefCount n_ref;
-
-        uint32_t t1;
-        uint32_t t2;
-        uint32_t lifetime;
-        be32_t address;
-        be32_t server_address;
-        be32_t subnet_mask;
-        be32_t router;
-        struct in_addr *dns;
-        size_t dns_size;
-        uint16_t mtu;
-        char *domainname;
-        char *hostname;
-};
-
-int dhcp_lease_new(sd_dhcp_lease **ret);
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
-                              void *user_data);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref);
-#define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp)
diff --git a/src/libsystemd-dhcp/dhcp-network.c b/src/libsystemd-dhcp/dhcp-network.c
deleted file mode 100644
index 934e8bf..0000000
--- a/src/libsystemd-dhcp/dhcp-network.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/***
-  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 <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <linux/if_packet.h>
-#include <net/ethernet.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include "socket-util.h"
-
-#include "dhcp-internal.h"
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
-{
-        int s, one = 1;
-
-        assert(index > 0);
-        assert(link);
-
-        s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                   htons(ETH_P_IP));
-        if (s < 0)
-                return -errno;
-
-        link->ll.sll_family = AF_PACKET;
-        link->ll.sll_protocol = htons(ETH_P_IP);
-        link->ll.sll_ifindex =  index;
-        link->ll.sll_halen = ETH_ALEN;
-        memset(link->ll.sll_addr, 0xff, ETH_ALEN);
-
-        if (setsockopt (s, SOL_PACKET, PACKET_AUXDATA, &one, sizeof(one)) < 0)
-                return -errno;
-
-        if (bind(s, &link->sa, sizeof(link->ll)) < 0) {
-                close_nointr_nofail(s);
-                return -errno;
-        }
-
-        return s;
-}
-
-int dhcp_network_bind_udp_socket(int index, be32_t address, uint16_t port)
-{
-        int s;
-        union sockaddr_union src = {
-                .in.sin_family = AF_INET,
-                .in.sin_port = htobe16(port),
-                .in.sin_addr.s_addr = address,
-        };
-
-        s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
-        if (s < 0)
-                return -errno;
-
-        if (bind(s, &src.sa, sizeof(src.in)) < 0) {
-                close_nointr_nofail(s);
-                return -errno;
-        }
-
-        return s;
-}
-
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
-                                 const void *packet, size_t len)
-{
-        assert(link);
-        assert(packet);
-        assert(len);
-
-        if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 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,
-        };
-
-        if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
-                return -errno;
-
-        return 0;
-}
diff --git a/src/libsystemd-dhcp/dhcp-option.c b/src/libsystemd-dhcp/dhcp-option.c
deleted file mode 100644
index 4d45b3b..0000000
--- a/src/libsystemd-dhcp/dhcp-option.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/*-*- 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 <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include "dhcp-internal.h"
-
-int dhcp_option_append(uint8_t **buf, size_t *buflen, uint8_t code,
-                       size_t optlen, const void *optval)
-{
-        if (!buf || !buflen)
-                return -EINVAL;
-
-        switch (code) {
-
-        case DHCP_OPTION_PAD:
-        case DHCP_OPTION_END:
-                if (*buflen < 1)
-                        return -ENOBUFS;
-
-                (*buf)[0] = code;
-                *buf += 1;
-                *buflen -= 1;
-                break;
-
-        default:
-                if (*buflen < optlen + 2)
-                        return -ENOBUFS;
-
-                if (!optval)
-                        return -EINVAL;
-
-                (*buf)[0] = code;
-                (*buf)[1] = optlen;
-                memcpy(&(*buf)[2], optval, optlen);
-
-                *buf += optlen + 2;
-                *buflen -= (optlen + 2);
-
-                break;
-        }
-
-        return 0;
-}
-
-static int parse_options(const uint8_t *buf, size_t buflen, uint8_t *overload,
-                         uint8_t *message_type, dhcp_option_cb_t cb,
-                         void *user_data)
-{
-        const uint8_t *code = buf;
-        const uint8_t *len;
-
-        while (buflen > 0) {
-                switch (*code) {
-                case DHCP_OPTION_PAD:
-                        buflen -= 1;
-                        code++;
-                        break;
-
-                case DHCP_OPTION_END:
-                        return 0;
-
-                case DHCP_OPTION_MESSAGE_TYPE:
-                        if (buflen < 3)
-                                return -ENOBUFS;
-                        buflen -= 3;
-
-                        len = code + 1;
-                        if (*len != 1)
-                                return -EINVAL;
-
-                        if (message_type)
-                                *message_type = *(len + 1);
-
-                        code += 3;
-
-                        break;
-
-                case DHCP_OPTION_OVERLOAD:
-                        if (buflen < 3)
-                                return -ENOBUFS;
-                        buflen -= 3;
-
-                        len = code + 1;
-                        if (*len != 1)
-                                return -EINVAL;
-
-                        if (overload)
-                                *overload = *(len + 1);
-
-                        code += 3;
-
-                        break;
-
-                default:
-                        if (buflen < 3)
-                                return -ENOBUFS;
-
-                        len = code + 1;
-
-                        if (buflen < (size_t)*len + 2)
-                                return -EINVAL;
-                        buflen -= *len + 2;
-
-                        if (cb)
-                                cb(*code, *len, len + 1, user_data);
-
-                        code += *len + 2;
-
-                        break;
-                }
-        }
-
-        if (buflen)
-                return -EINVAL;
-
-        return 0;
-}
-
-int dhcp_option_parse(DHCPMessage *message, size_t len,
-                      dhcp_option_cb_t cb, void *user_data)
-{
-        uint8_t overload = 0;
-        uint8_t message_type = 0;
-        uint8_t *opt = (uint8_t *)(message + 1);
-        int res;
-
-        if (!message)
-                return -EINVAL;
-
-        if (len < sizeof(DHCPMessage) + 4)
-                return -EINVAL;
-
-        len -= sizeof(DHCPMessage) + 4;
-
-        if (opt[0] != 0x63 && opt[1] != 0x82 && opt[2] != 0x53 &&
-                        opt[3] != 0x63)
-                return -EINVAL;
-
-        res = parse_options(&opt[4], len, &overload, &message_type,
-                        cb, user_data);
-        if (res < 0)
-                return res;
-
-        if (overload & DHCP_OVERLOAD_FILE) {
-                res = parse_options(message->file, sizeof(message->file),
-                                NULL, &message_type, cb, user_data);
-                if (res < 0)
-                        return res;
-        }
-
-        if (overload & DHCP_OVERLOAD_SNAME) {
-                res = parse_options(message->sname, sizeof(message->sname),
-                                NULL, &message_type, cb, user_data);
-                if (res < 0)
-                        return res;
-        }
-
-        if (message_type)
-                return message_type;
-
-        return -ENOMSG;
-}
diff --git a/src/libsystemd-dhcp/dhcp-packet.c b/src/libsystemd-dhcp/dhcp-packet.c
deleted file mode 100644
index 8388e56..0000000
--- a/src/libsystemd-dhcp/dhcp-packet.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/***
-  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 <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-#include <sys/param.h>
-
-#include "util.h"
-#include "list.h"
-
-#include "dhcp-protocol.h"
-#include "dhcp-lease.h"
-#include "dhcp-internal.h"
-#include "sd-dhcp-client.h"
-
-#define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
-
-int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
-                      uint8_t type, uint8_t **opt, size_t *optlen) {
-        int err;
-
-        assert(op == BOOTREQUEST || op == BOOTREPLY);
-
-        *opt = (uint8_t *)(message + 1);
-
-        if (*optlen < 4)
-                return -ENOBUFS;
-        *optlen -= 4;
-
-        message->op = op;
-        message->htype = ARPHRD_ETHER;
-        message->hlen = ETHER_ADDR_LEN;
-        message->xid = htobe32(xid);
-
-        (*opt)[0] = 0x63;
-        (*opt)[1] = 0x82;
-        (*opt)[2] = 0x53;
-        (*opt)[3] = 0x63;
-
-        *opt += 4;
-
-        err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
-                                 &type);
-        if (err < 0)
-                return err;
-
-        return 0;
-}
-
-static uint16_t dhcp_checksum(void *buf, int len) {
-        uint32_t sum;
-        uint16_t *check;
-        int i;
-        uint8_t *odd;
-
-        sum = 0;
-        check = buf;
-
-        for (i = 0; i < len / 2 ; i++)
-                sum += check[i];
-
-        if (len & 0x01) {
-                odd = buf;
-                sum += odd[len - 1];
-        }
-
-        while (sum >> 16)
-                sum = (sum & 0xffff) + (sum >> 16);
-
-        return ~sum;
-}
-
-void dhcp_packet_append_ip_headers(DHCPPacket *packet, uint16_t len) {
-        packet->ip.version = IPVERSION;
-        packet->ip.ihl = DHCP_IP_SIZE / 4;
-        packet->ip.tot_len = htobe16(len);
-
-        packet->ip.protocol = IPPROTO_UDP;
-        packet->ip.saddr = INADDR_ANY;
-        packet->ip.daddr = INADDR_BROADCAST;
-
-        packet->udp.source = htobe16(DHCP_PORT_CLIENT);
-        packet->udp.dest = htobe16(DHCP_PORT_SERVER);
-
-        packet->udp.len = htobe16(len - DHCP_IP_SIZE);
-
-        packet->ip.check = packet->udp.len;
-        packet->udp.check = dhcp_checksum(&packet->ip.ttl, len - 8);
-
-        packet->ip.ttl = IPDEFTTL;
-        packet->ip.check = 0;
-        packet->ip.check = dhcp_checksum(&packet->ip, DHCP_IP_SIZE);
-}
-
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
-        size_t hdrlen;
-
-        assert(packet);
-
-        /* IP */
-
-        if (len < DHCP_IP_SIZE) {
-                log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
-                                " smaller than IP header (%u bytes)", len,
-                                DHCP_IP_SIZE);
-                return -EINVAL;
-        }
-
-        if (packet->ip.ihl < 5) {
-                log_dhcp_client(client, "ignoring packet: IPv4 IHL (%u words) invalid",
-                                packet->ip.ihl);
-                return -EINVAL;
-        }
-
-        hdrlen = packet->ip.ihl * 4;
-        if (hdrlen < 20) {
-                log_dhcp_client(client, "ignoring packet: IPv4 IHL (%zu bytes) "
-                                "smaller than minimum (20 bytes)", hdrlen);
-                return -EINVAL;
-        }
-
-        if (len < hdrlen) {
-                log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
-                                "smaller than expected (%zu) by IP header", len,
-                                hdrlen);
-                return -EINVAL;
-        }
-
-        if (dhcp_checksum(&packet->ip, hdrlen)) {
-                log_dhcp_client(client, "ignoring packet: invalid IP checksum");
-                return -EINVAL;
-        }
-
-        /* UDP */
-
-        if (len < DHCP_IP_UDP_SIZE) {
-                log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
-                                " smaller than IP+UDP header (%u bytes)", len,
-                                DHCP_IP_UDP_SIZE);
-                return -EINVAL;
-        }
-
-        if (len < hdrlen + be16toh(packet->udp.len)) {
-                log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
-                                "smaller than expected (%zu) by UDP header", len,
-                                hdrlen + be16toh(packet->udp.len));
-                return -EINVAL;
-        }
-
-        if (checksum && packet->udp.check) {
-                packet->ip.check = packet->udp.len;
-                packet->ip.ttl = 0;
-
-                if (dhcp_checksum(&packet->ip.ttl,
-                                  be16toh(packet->udp.len) + 12)) {
-                        log_dhcp_client(client, "ignoring packet: invalid UDP checksum");
-                        return -EINVAL;
-                }
-        }
-
-        if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
-                log_dhcp_client(client, "ignoring packet: to port %u, which "
-                                "is not the DHCP client port (%u)",
-                                be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
-                return -EINVAL;
-        }
-
-        return 0;
-}
diff --git a/src/libsystemd-dhcp/dhcp-protocol.h b/src/libsystemd-dhcp/dhcp-protocol.h
deleted file mode 100644
index 81d36ce..0000000
--- a/src/libsystemd-dhcp/dhcp-protocol.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2013 Intel Corporation. All rights reserved.
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <netinet/udp.h>
-#include <netinet/ip.h>
-#include <stdint.h>
-
-#include "macro.h"
-#include "sparse-endian.h"
-
-struct DHCPMessage {
-        uint8_t op;
-        uint8_t htype;
-        uint8_t hlen;
-        uint8_t hops;
-        be32_t xid;
-        be16_t secs;
-        be16_t flags;
-        be32_t ciaddr;
-        be32_t yiaddr;
-        be32_t siaddr;
-        be32_t giaddr;
-        uint8_t chaddr[16];
-        uint8_t sname[64];
-        uint8_t file[128];
-} _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_MIN_OPTIONS_SIZE   312
-
-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,
-};
-
-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,
-};
-
-enum {
-        DHCP_OVERLOAD_FILE                      = 1,
-        DHCP_OVERLOAD_SNAME                     = 2,
-};
-
-enum {
-        DHCP_OPTION_PAD                         = 0,
-        DHCP_OPTION_SUBNET_MASK                 = 1,
-        DHCP_OPTION_ROUTER                      = 3,
-        DHCP_OPTION_DOMAIN_NAME_SERVER          = 6,
-        DHCP_OPTION_HOST_NAME                   = 12,
-        DHCP_OPTION_DOMAIN_NAME                 = 15,
-        DHCP_OPTION_INTERFACE_MTU               = 26,
-        DHCP_OPTION_NTP_SERVER                  = 42,
-        DHCP_OPTION_REQUESTED_IP_ADDRESS        = 50,
-        DHCP_OPTION_IP_ADDRESS_LEASE_TIME       = 51,
-        DHCP_OPTION_OVERLOAD                    = 52,
-        DHCP_OPTION_MESSAGE_TYPE                = 53,
-        DHCP_OPTION_SERVER_IDENTIFIER           = 54,
-        DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
-        DHCP_OPTION_MAXIMUM_MESSAGE_SIZE        = 57,
-        DHCP_OPTION_RENEWAL_T1_TIME             = 58,
-        DHCP_OPTION_REBINDING_T2_TIME           = 59,
-        DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
-        DHCP_OPTION_END                         = 255,
-};
diff --git a/src/libsystemd-dhcp/sd-dhcp-client.c b/src/libsystemd-dhcp/sd-dhcp-client.c
deleted file mode 100644
index 0c82260..0000000
--- a/src/libsystemd-dhcp/sd-dhcp-client.c
+++ /dev/null
@@ -1,1051 +0,0 @@
-/***
-  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 <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <net/ethernet.h>
-#include <sys/param.h>
-#include <sys/ioctl.h>
-
-#include "util.h"
-#include "list.h"
-
-#include "dhcp-protocol.h"
-#include "dhcp-lease.h"
-#include "dhcp-internal.h"
-#include "sd-dhcp-client.h"
-
-struct sd_dhcp_client {
-        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;
-        uint8_t *req_opts;
-        size_t req_opts_allocated;
-        size_t req_opts_size;
-        be32_t last_addr;
-        struct ether_addr mac_addr;
-        uint32_t xid;
-        usec_t start_time;
-        uint16_t secs;
-        unsigned int attempt;
-        usec_t request_sent;
-        sd_event_source *timeout_t1;
-        sd_event_source *timeout_t2;
-        sd_event_source *timeout_expire;
-        sd_dhcp_client_cb_t cb;
-        void *userdata;
-        sd_dhcp_lease *lease;
-};
-
-static const uint8_t default_req_opts[] = {
-        DHCP_OPTION_SUBNET_MASK,
-        DHCP_OPTION_ROUTER,
-        DHCP_OPTION_HOST_NAME,
-        DHCP_OPTION_DOMAIN_NAME,
-        DHCP_OPTION_DOMAIN_NAME_SERVER,
-        DHCP_OPTION_NTP_SERVER,
-};
-
-static int client_receive_message_raw(sd_event_source *s, int fd,
-                                      uint32_t revents, void *userdata);
-static int client_receive_message_udp(sd_event_source *s, int fd,
-                                      uint32_t revents, void *userdata);
-
-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_option(sd_dhcp_client *client, uint8_t option) {
-        size_t i;
-
-        assert_return(client, -EINVAL);
-        assert_return (client->state == DHCP_STATE_INIT, -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(client->state == DHCP_STATE_INIT, -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(client->state == DHCP_STATE_INIT, -EBUSY);
-        assert_return(interface_index >= -1, -EINVAL);
-
-        client->index = interface_index;
-
-        return 0;
-}
-
-int sd_dhcp_client_set_mac(sd_dhcp_client *client,
-                           const struct ether_addr *addr) {
-        assert_return(client, -EINVAL);
-        assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
-
-        memcpy(&client->mac_addr, addr, ETH_ALEN);
-
-        return 0;
-}
-
-int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
-        assert_return(client, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (client->state != DHCP_STATE_BOUND &&
-            client->state != DHCP_STATE_RENEWING &&
-            client->state != DHCP_STATE_REBINDING)
-                return -EADDRNOTAVAIL;
-
-        *ret = sd_dhcp_lease_ref(client->lease);
-
-        return 0;
-}
-
-static int client_notify(sd_dhcp_client *client, int event) {
-        if (client->cb)
-                client->cb(client, event, client->userdata);
-
-        return 0;
-}
-
-static int client_stop(sd_dhcp_client *client, int error) {
-        assert_return(client, -EINVAL);
-
-        client->receive_message =
-                sd_event_source_unref(client->receive_message);
-
-        if (client->fd >= 0)
-                close(client->fd);
-        client->fd = -1;
-
-        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_notify(client, error);
-
-        client->start_time = 0;
-        client->secs = 0;
-        client->state = DHCP_STATE_INIT;
-
-        if (client->lease)
-                client->lease = sd_dhcp_lease_unref(client->lease);
-
-        log_dhcp_client(client, "STOPPED");
-
-        return 0;
-}
-
-static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
-                               uint8_t type, uint16_t secs, uint8_t **opt,
-                               size_t *optlen) {
-        int r;
-
-        assert(secs);
-
-        r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
-                              optlen);
-        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 */
-        message->secs = htobe16(secs);
-
-        memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
-
-        if (client->state == DHCP_STATE_RENEWING ||
-            client->state == DHCP_STATE_REBINDING)
-                message->ciaddr = client->lease->address;
-
-        /* Some DHCP servers will refuse to issue an DHCP lease if the Client
-           Identifier option is not set */
-        r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
-                               ETH_ALEN, &client->mac_addr);
-        if (r < 0)
-                return r;
-
-        if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
-                be16_t max_size;
-
-                r = dhcp_option_append(opt, optlen,
-                                       DHCP_OPTION_PARAMETER_REQUEST_LIST,
-                                       client->req_opts_size,
-                                       client->req_opts);
-                if (r < 0)
-                        return r;
-
-                /* Some DHCP servers will send bigger DHCP packets than the
-                   defined default size unless the Maximum Messge Size option
-                   is explicitely set */
-                max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
-                                   DHCP_MIN_OPTIONS_SIZE);
-                r = dhcp_option_append(opt, optlen,
-                                       DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
-                                       2, &max_size);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
-        int err = 0;
-        _cleanup_free_ DHCPPacket *discover;
-        size_t optlen, len;
-        uint8_t *opt;
-
-        optlen = DHCP_MIN_OPTIONS_SIZE;
-        len = sizeof(DHCPPacket) + optlen;
-
-        discover = malloc0(len);
-
-        if (!discover)
-                return -ENOMEM;
-
-        err = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
-                                  secs, &opt, &optlen);
-        if (err < 0)
-                return err;
-
-        if (client->last_addr != INADDR_ANY) {
-                err = dhcp_option_append(&opt, &optlen,
-                                         DHCP_OPTION_REQUESTED_IP_ADDRESS,
-                                         4, &client->last_addr);
-                if (err < 0)
-                        return err;
-        }
-
-        err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
-        if (err < 0)
-                return err;
-
-        dhcp_packet_append_ip_headers(discover, len);
-
-        err = dhcp_network_send_raw_socket(client->fd, &client->link,
-                                           discover, len);
-
-        log_dhcp_client(client, "DISCOVER");
-
-        return err;
-}
-
-static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
-        _cleanup_free_ DHCPPacket *request;
-        size_t optlen, len;
-        int err;
-        uint8_t *opt;
-
-        optlen = DHCP_MIN_OPTIONS_SIZE;
-        len = sizeof(DHCPPacket) + optlen;
-
-        request = malloc0(len);
-        if (!request)
-                return -ENOMEM;
-
-        err = client_message_init(client, &request->dhcp, DHCP_REQUEST, secs,
-                                  &opt, &optlen);
-        if (err < 0)
-                return err;
-
-        if (client->state == DHCP_STATE_REQUESTING) {
-                err = dhcp_option_append(&opt, &optlen,
-                                         DHCP_OPTION_REQUESTED_IP_ADDRESS,
-                                         4, &client->lease->address);
-                if (err < 0)
-                        return err;
-
-                err = dhcp_option_append(&opt, &optlen,
-                                         DHCP_OPTION_SERVER_IDENTIFIER,
-                                         4, &client->lease->server_address);
-                if (err < 0)
-                        return err;
-        }
-
-        err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
-        if (err < 0)
-                return err;
-
-        if (client->state == DHCP_STATE_RENEWING) {
-                err = dhcp_network_send_udp_socket(client->fd,
-                                                   client->lease->server_address,
-                                                   DHCP_PORT_SERVER,
-                                                   &request->dhcp,
-                                                   len - DHCP_IP_UDP_SIZE);
-        } else {
-                dhcp_packet_append_ip_headers(request, len);
-
-                err = dhcp_network_send_raw_socket(client->fd, &client->link,
-                                                   request, len);
-        }
-
-        log_dhcp_client(client, "REQUEST");
-
-        return err;
-}
-
-static uint16_t client_update_secs(sd_dhcp_client *client, usec_t time_now)
-{
-        client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
-
-        return client->secs;
-}
-
-static int client_timeout_resend(sd_event_source *s, uint64_t usec,
-                                 void *userdata) {
-        sd_dhcp_client *client = userdata;
-        usec_t next_timeout = 0;
-        uint32_t time_left;
-        int r = 0;
-
-        assert(s);
-        assert(client);
-        assert(client->event);
-
-        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 = usec + 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 = usec + time_left * USEC_PER_SEC;
-                break;
-
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_SELECTING:
-        case DHCP_STATE_REQUESTING:
-        case DHCP_STATE_BOUND:
-
-                if (client->attempt < 64)
-                        client->attempt *= 2;
-
-                next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC;
-
-                break;
-        }
-
-        next_timeout += (random_u32() & 0x1fffff);
-
-        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
-        r = sd_event_add_monotonic(client->event,
-                                     &client->timeout_resend,
-                                     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;
-
-        switch (client->state) {
-        case DHCP_STATE_INIT:
-
-                client_update_secs(client, usec);
-
-                r = client_send_discover(client, client->secs);
-                if (r >= 0) {
-                        client->state = DHCP_STATE_SELECTING;
-                        client->attempt = 1;
-                } else {
-                        if (client->attempt >= 64)
-                                goto error;
-                }
-
-                break;
-
-        case DHCP_STATE_SELECTING:
-                client_update_secs(client, usec);
-
-                r = client_send_discover(client, client->secs);
-                if (r < 0 && client->attempt >= 64)
-                        goto error;
-
-                break;
-
-        case DHCP_STATE_REQUESTING:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-                r = client_send_request(client, client->secs);
-                if (r < 0 && client->attempt >= 64)
-                         goto error;
-
-                client->request_sent = usec;
-
-                break;
-
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_BOUND:
-
-                break;
-        }
-
-        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_events(sd_dhcp_client *client,
-                                    sd_event_io_handler_t io_callback,
-                                    usec_t usec) {
-        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;
-
-        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
-        r = sd_event_add_monotonic(client->event,
-                                   &client->timeout_resend,
-                                   usec, 0,
-                                   client_timeout_resend, client);
-        if (r < 0)
-                goto error;
-
-        r = sd_event_source_set_priority(client->timeout_resend,
-                                         client->event_priority);
-
-error:
-        if (r < 0)
-                client_stop(client, r);
-
-        return 0;
-
-}
-
-static int client_timeout_expire(sd_event_source *s, uint64_t usec,
-                                 void *userdata) {
-        sd_dhcp_client *client = userdata;
-
-        log_dhcp_client(client, "EXPIRED");
-
-        client_stop(client, DHCP_EVENT_EXPIRED);
-
-        return 0;
-}
-
-static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_dhcp_client *client = userdata;
-        int r;
-
-        if (client->fd >= 0) {
-                client->receive_message =
-                        sd_event_source_unref(client->receive_message);
-                close(client->fd);
-                client->fd = -1;
-        }
-
-        client->state = DHCP_STATE_REBINDING;
-        client->attempt = 1;
-
-        r = dhcp_network_bind_raw_socket(client->index, &client->link);
-        if (r < 0) {
-                client_stop(client, r);
-                return 0;
-        }
-
-        client->fd = r;
-
-        log_dhcp_client(client, "TIMEOUT T2");
-
-        return client_initialize_events(client, client_receive_message_raw,
-                                        usec);
-}
-
-static int client_timeout_t1(sd_event_source *s, uint64_t usec,
-                             void *userdata) {
-        sd_dhcp_client *client = userdata;
-        int r;
-
-        client->state = DHCP_STATE_RENEWING;
-        client->attempt = 1;
-
-        r = dhcp_network_bind_udp_socket(client->index,
-                                         client->lease->address,
-                                         DHCP_PORT_CLIENT);
-        if (r < 0) {
-                client_stop(client, r);
-                return 0;
-        }
-
-        client->fd = r;
-
-        log_dhcp_client(client, "TIMEOUT T1");
-
-        return client_initialize_events(client, client_receive_message_udp, usec);
-}
-
-static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
-                               size_t len) {
-        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
-        int r;
-
-        r = dhcp_lease_new(&lease);
-        if (r < 0)
-                return r;
-
-        r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
-        if (r != DHCP_OFFER)
-                return -ENOMSG;
-
-        lease->address = offer->yiaddr;
-
-        if (lease->address == INADDR_ANY ||
-            lease->server_address == INADDR_ANY ||
-            lease->subnet_mask == INADDR_ANY ||
-            lease->lifetime == 0)
-                return -ENOMSG;
-
-        client->lease = lease;
-        lease = NULL;
-
-        log_dhcp_client(client, "OFFER");
-
-        return 0;
-}
-
-static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
-                             size_t len) {
-        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
-        int r;
-
-        r = dhcp_lease_new(&lease);
-        if (r < 0)
-                return r;
-
-        r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
-        if (r == DHCP_NAK) {
-                log_dhcp_client(client, "NAK");
-                return DHCP_EVENT_NO_LEASE;
-        }
-
-        if (r != DHCP_ACK)
-                return -ENOMSG;
-
-        lease->address = ack->yiaddr;
-
-        if (lease->address == INADDR_ANY ||
-            lease->server_address == INADDR_ANY ||
-            lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
-                return -ENOMSG;
-
-        r = DHCP_EVENT_IP_ACQUIRE;
-        if (client->lease) {
-                if (client->lease->address != lease->address ||
-                    client->lease->subnet_mask != lease->subnet_mask ||
-                    client->lease->router != lease->router) {
-                        r = DHCP_EVENT_IP_CHANGE;
-                }
-
-                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(uint64_t request_sent,
-                                       uint32_t lifetime) {
-        return request_sent + (lifetime - 3) * USEC_PER_SEC +
-                + (random_u32() & 0x1fffff);
-}
-
-static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
-        uint64_t next_timeout;
-        int r;
-
-        assert(client);
-        assert(client->event);
-
-        if (client->lease->lifetime < 10)
-                return -EINVAL;
-
-        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);
-
-        if (!client->lease->t1)
-                client->lease->t1 = client->lease->lifetime / 2;
-
-        next_timeout = client_compute_timeout(client->request_sent,
-                                              client->lease->t1);
-        if (next_timeout < usec)
-                return -EINVAL;
-
-        r = sd_event_add_monotonic(client->event,
-                                     &client->timeout_t1,
-                                     next_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;
-
-        if (!client->lease->t2)
-                client->lease->t2 = client->lease->lifetime * 7 / 8;
-
-        if (client->lease->t2 < client->lease->t1)
-                return -EINVAL;
-
-        if (client->lease->lifetime < client->lease->t2)
-                return -EINVAL;
-
-        next_timeout = client_compute_timeout(client->request_sent,
-                                              client->lease->t2);
-        if (next_timeout < usec)
-                return -EINVAL;
-
-        r = sd_event_add_monotonic(client->event,
-                                     &client->timeout_t2,
-                                     next_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;
-
-        next_timeout = client_compute_timeout(client->request_sent,
-                                              client->lease->lifetime);
-        if (next_timeout < usec)
-                return -EINVAL;
-
-        r = sd_event_add_monotonic(client->event,
-                                     &client->timeout_expire, next_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;
-
-        return 0;
-}
-
-static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
-                                 int len, usec_t time_now) {
-        int r = 0, notify_event = 0;
-
-        assert(client);
-        assert(client->event);
-        assert(message);
-
-        if (len < DHCP_MESSAGE_SIZE) {
-                log_dhcp_client(client, "message too small (%d bytes): "
-                                "ignoring", len);
-                return 0;
-        }
-
-        if (message->op != BOOTREPLY) {
-                log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
-                return 0;
-        }
-
-        if (be32toh(message->xid) != client->xid) {
-                log_dhcp_client(client, "received xid (%u) does not match "
-                                "expected (%u): ignoring",
-                                be32toh(message->xid), client->xid);
-                return 0;
-        }
-
-        if (memcmp(&message->chaddr[0], &client->mac_addr.ether_addr_octet,
-                   ETHER_ADDR_LEN)) {
-                log_dhcp_client(client, "received chaddr does not match "
-                                "expected: ignoring");
-                return 0;
-        }
-
-        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_monotonic(client->event,
-                                                   &client->timeout_resend,
-                                                   time_now, 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;
-                }
-
-                break;
-
-        case DHCP_STATE_REQUESTING:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-
-                r = client_handle_ack(client, message, len);
-
-                if (r == DHCP_EVENT_NO_LEASE)
-                        goto error;
-
-                if (r >= 0) {
-                        client->timeout_resend =
-                                sd_event_source_unref(client->timeout_resend);
-
-                        if (client->state == DHCP_STATE_REQUESTING)
-                                notify_event = DHCP_EVENT_IP_ACQUIRE;
-                        else if (r != DHCP_EVENT_IP_ACQUIRE)
-                                notify_event = r;
-
-                        client->state = DHCP_STATE_BOUND;
-                        client->attempt = 1;
-
-                        client->last_addr = client->lease->address;
-
-                        r = client_set_lease_timeouts(client, time_now);
-                        if (r < 0)
-                                goto error;
-
-                        if (notify_event)
-                                client_notify(client, notify_event);
-
-                        client->receive_message =
-                                sd_event_source_unref(client->receive_message);
-                        close(client->fd);
-                        client->fd = -1;
-                }
-
-                r = 0;
-
-                break;
-
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_BOUND:
-
-                break;
-        }
-
-error:
-        if (r < 0 || r == DHCP_EVENT_NO_LEASE)
-                return client_stop(client, r);
-
-        return 0;
-}
-
-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;
-        usec_t time_now;
-
-        assert(s);
-        assert(client);
-        assert(client->event);
-
-        r = ioctl(fd, FIONREAD, &buflen);
-        if (r < 0 || buflen <= 0)
-                buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE;
-
-        message = malloc0(buflen);
-        if (!message)
-                return -ENOMEM;
-
-        len = read(fd, message, buflen);
-        if (len < 0)
-                return 0;
-
-        r = sd_event_get_now_monotonic(client->event, &time_now);
-        if (r < 0)
-                return client_stop(client, r);
-
-        return client_handle_message(client, message, len,
-                                     time_now);
-}
-
-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;
-        usec_t time_now;
-        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);
-        assert(client->event);
-
-        r = ioctl(fd, FIONREAD, &buflen);
-        if (r < 0 || buflen <= 0)
-                buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE;
-
-        packet = malloc0(buflen);
-        if (!packet)
-                return -ENOMEM;
-
-        iov.iov_base = packet;
-        iov.iov_len = buflen;
-
-        len = recvmsg(fd, &msg, 0);
-        if (len < 0) {
-                log_dhcp_client(client, "could not receive message from raw "
-                                "socket: %s", strerror(errno));
-                return 0;
-        }
-
-        for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-                if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
-                        struct tpacket_auxdata *aux = (void *)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;
-
-        r = sd_event_get_now_monotonic(client->event, &time_now);
-        if (r < 0)
-                return client_stop(client, r);
-
-        return client_handle_message(client, &packet->dhcp, len, time_now);
-}
-
-int sd_dhcp_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->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);
-
-        if (r < 0) {
-                client_stop(client, r);
-                return r;
-        }
-
-        client->fd = r;
-        client->start_time = now(CLOCK_MONOTONIC);
-        client->secs = 0;
-
-        log_dhcp_client(client, "STARTED");
-
-        return client_initialize_events(client, client_receive_message_raw,
-                                        client->start_time);
-}
-
-int sd_dhcp_client_stop(sd_dhcp_client *client) {
-        return client_stop(client, DHCP_EVENT_STOP);
-}
-
-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;
-}
-
-void sd_dhcp_client_free(sd_dhcp_client *client) {
-        if (!client)
-                return;
-
-        sd_dhcp_client_stop(client);
-        sd_dhcp_client_detach_event(client);
-
-        free(client->req_opts);
-        free(client);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
-#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
-
-int sd_dhcp_client_new(sd_dhcp_client **ret) {
-        _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
-
-        assert_return(ret, -EINVAL);
-
-        client = new0(sd_dhcp_client, 1);
-        if (!client)
-                return -ENOMEM;
-
-        client->state = DHCP_STATE_INIT;
-        client->index = -1;
-        client->fd = -1;
-        client->attempt = 1;
-
-        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/libsystemd-dhcp/test-dhcp-client.c b/src/libsystemd-dhcp/test-dhcp-client.c
deleted file mode 100644
index 8061e5f..0000000
--- a/src/libsystemd-dhcp/test-dhcp-client.c
+++ /dev/null
@@ -1,496 +0,0 @@
-/*-*- 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 <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include "util.h"
-#include "socket-util.h"
-
-#include "dhcp-protocol.h"
-#include "dhcp-internal.h"
-#include "sd-dhcp-client.h"
-
-static struct ether_addr mac_addr = {
-        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
-};
-
-typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp);
-
-static bool verbose = false;
-static int test_fd[2];
-static test_callback_recv_t callback_recv;
-static be32_t xid;
-
-static void test_request_basic(sd_event *e)
-{
-        int r;
-
-        sd_dhcp_client *client;
-
-        if (verbose)
-                printf("* %s\n", __FUNCTION__);
-
-        r = sd_dhcp_client_new(&client);
-
-        assert_se(r >= 0);
-        assert_se(client);
-
-        r = sd_dhcp_client_attach_event(client, e, 0);
-        assert_se(r >= 0);
-
-        assert_se(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
-        assert_se(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
-        assert_se(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
-
-        assert_se(sd_dhcp_client_set_index(client, 15) == 0);
-        assert_se(sd_dhcp_client_set_index(client, -42) == -EINVAL);
-        assert_se(sd_dhcp_client_set_index(client, -1) == 0);
-
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_SUBNET_MASK) == -EEXIST);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_ROUTER) == -EEXIST);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_HOST_NAME) == -EEXIST);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_DOMAIN_NAME_SERVER)
-                        == -EEXIST);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_NTP_SERVER) == -EEXIST);
-
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_PAD) == -EINVAL);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_END) == -EINVAL);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_OVERLOAD) == -EINVAL);
-        assert_se(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_PARAMETER_REQUEST_LIST)
-                        == -EINVAL);
-
-        assert_se(sd_dhcp_client_set_request_option(client, 33) == 0);
-        assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
-        assert_se(sd_dhcp_client_set_request_option(client, 44) == 0);
-        assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
-}
-
-static uint16_t client_checksum(void *buf, int len)
-{
-        uint32_t sum;
-        uint16_t *check;
-        int i;
-        uint8_t *odd;
-
-        sum = 0;
-        check = buf;
-
-        for (i = 0; i < len / 2 ; i++)
-                sum += check[i];
-
-        if (len & 0x01) {
-                odd = buf;
-                sum += odd[len - 1];
-        }
-
-        while (sum >> 16)
-                sum = (sum & 0xffff) + (sum >> 16);
-
-        return ~sum;
-}
-
-static void test_checksum(void)
-{
-        uint8_t buf[20] = {
-                0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
-                0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                0xff, 0xff, 0xff, 0xff
-        };
-
-        if (verbose)
-                printf("* %s\n", __FUNCTION__);
-
-        assert_se(client_checksum(&buf, 20) == be16toh(0x78ae));
-}
-
-static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
-                void *user_data)
-{
-        return 0;
-}
-
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
-                                 const void *packet, size_t len)
-{
-        size_t size;
-        _cleanup_free_ DHCPPacket *discover;
-        uint16_t ip_check, udp_check;
-
-        assert_se(s >= 0);
-        assert_se(packet);
-
-        size = sizeof(DHCPPacket) + 4;
-        assert_se(len > size);
-
-        discover = memdup(packet, len);
-
-        assert_se(discover->ip.ttl == IPDEFTTL);
-        assert_se(discover->ip.protocol == IPPROTO_UDP);
-        assert_se(discover->ip.saddr == INADDR_ANY);
-        assert_se(discover->ip.daddr == INADDR_BROADCAST);
-        assert_se(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
-        assert_se(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
-
-        ip_check = discover->ip.check;
-
-        discover->ip.ttl = 0;
-        discover->ip.check = discover->udp.len;
-
-        udp_check = ~client_checksum(&discover->ip.ttl, len - 8);
-        assert_se(udp_check == 0xffff);
-
-        discover->ip.ttl = IPDEFTTL;
-        discover->ip.check = ip_check;
-
-        ip_check = ~client_checksum(&discover->ip, sizeof(discover->ip));
-        assert_se(ip_check == 0xffff);
-
-        assert_se(discover->dhcp.xid);
-        assert_se(memcmp(discover->dhcp.chaddr,
-                      &mac_addr.ether_addr_octet, 6) == 0);
-
-        size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
-
-        assert_se(callback_recv);
-        callback_recv(size, &discover->dhcp);
-
-        return 575;
-}
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
-{
-        if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
-                return -errno;
-
-        return test_fd[0];
-}
-
-int dhcp_network_bind_udp_socket(int index, be32_t address, uint16_t port)
-{
-        return 0;
-}
-
-int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
-                                 const void *packet, size_t len)
-{
-        return 0;
-}
-
-static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
-{
-        int res;
-
-        res = dhcp_option_parse(dhcp, size, check_options, NULL);
-        assert_se(res == DHCP_DISCOVER);
-
-        if (verbose)
-                printf("  recv DHCP Discover 0x%08x\n", be32toh(dhcp->xid));
-
-        return 0;
-}
-
-static void test_discover_message(sd_event *e)
-{
-        sd_dhcp_client *client;
-        int res, r;
-
-        if (verbose)
-                printf("* %s\n", __FUNCTION__);
-
-        r = sd_dhcp_client_new(&client);
-        assert_se(r >= 0);
-        assert_se(client);
-
-        r = sd_dhcp_client_attach_event(client, e, 0);
-        assert_se(r >= 0);
-
-        assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
-        assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
-
-        assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0);
-
-        callback_recv = test_discover_message_verify;
-
-        res = sd_dhcp_client_start(client);
-
-        assert_se(res == 0 || res == -EINPROGRESS);
-
-        sd_event_run(e, (uint64_t) -1);
-
-        sd_dhcp_client_stop(client);
-        sd_dhcp_client_free(client);
-
-        close(test_fd[0]);
-        close(test_fd[1]);
-
-        callback_recv = NULL;
-}
-
-static uint8_t test_addr_acq_offer[] = {
-        0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
-        0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
-        0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
-        0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
-        0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
-        0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36,
-        0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
-        0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
-        0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
-        0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
-        0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
-        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static uint8_t test_addr_acq_ack[] = {
-        0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
-        0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
-        0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
-        0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
-        0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
-        0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
-        0x00, 0x02, 0x58, 0x01, 0x04,   0xff, 0xff, 0xff,
-        0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
-        0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
-        0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
-        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static void test_addr_acq_acquired(sd_dhcp_client *client, int event,
-                                   void *userdata)
-{
-        sd_event *e = userdata;
-        sd_dhcp_lease *lease;
-        struct in_addr addr;
-
-        assert_se(client);
-        assert_se(event == DHCP_EVENT_IP_ACQUIRE);
-
-        assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0);
-        assert_se(lease);
-
-        assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0);
-        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44],
-                      sizeof(addr.s_addr)) == 0);
-
-        assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0);
-        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285],
-                      sizeof(addr.s_addr)) == 0);
-
-        assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0);
-        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308],
-                      sizeof(addr.s_addr)) == 0);
-
-        if (verbose)
-                printf("  DHCP address acquired\n");
-
-        sd_event_exit(e, 0);
-}
-
-static int test_addr_acq_recv_request(size_t size, DHCPMessage *request)
-{
-        uint16_t udp_check = 0;
-        int res;
-
-        res = dhcp_option_parse(request, size, check_options, NULL);
-        assert_se(res == DHCP_REQUEST);
-        assert_se(xid == request->xid);
-
-        if (verbose)
-                printf("  recv DHCP Request  0x%08x\n", be32toh(xid));
-
-        memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check));
-        memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid));
-        memcpy(&test_addr_acq_ack[56], &mac_addr.ether_addr_octet,
-               ETHER_ADDR_LEN);
-
-        callback_recv = NULL;
-
-        res = write(test_fd[1], test_addr_acq_ack,
-                    sizeof(test_addr_acq_ack));
-        assert_se(res == sizeof(test_addr_acq_ack));
-
-        if (verbose)
-                printf("  send DHCP Ack\n");
-
-        return 0;
-};
-
-static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover)
-{
-        uint16_t udp_check = 0;
-        int res;
-
-        res = dhcp_option_parse(discover, size, check_options, NULL);
-        assert_se(res == DHCP_DISCOVER);
-
-        xid = discover->xid;
-
-        if (verbose)
-                printf("  recv DHCP Discover 0x%08x\n", be32toh(xid));
-
-        memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check));
-        memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid));
-        memcpy(&test_addr_acq_offer[56], &mac_addr.ether_addr_octet,
-               ETHER_ADDR_LEN);
-
-        callback_recv = test_addr_acq_recv_request;
-
-        res = write(test_fd[1], test_addr_acq_offer,
-                    sizeof(test_addr_acq_offer));
-        assert_se(res == sizeof(test_addr_acq_offer));
-
-        if (verbose)
-                printf("  send DHCP Offer\n");
-
-        return 0;
-}
-
-static void test_addr_acq(sd_event *e)
-{
-        sd_dhcp_client *client;
-        int res, r;
-
-        if (verbose)
-                printf("* %s\n", __FUNCTION__);
-
-        r = sd_dhcp_client_new(&client);
-        assert_se(r >= 0);
-        assert_se(client);
-
-        r = sd_dhcp_client_attach_event(client, e, 0);
-        assert_se(r >= 0);
-
-        assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
-        assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
-
-        assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e)
-                >= 0);
-
-        callback_recv = test_addr_acq_recv_discover;
-
-        res = sd_dhcp_client_start(client);
-        assert_se(res == 0 || res == -EINPROGRESS);
-
-        sd_event_loop(e);
-
-        sd_dhcp_client_set_callback(client, NULL, NULL);
-        sd_dhcp_client_stop(client);
-        sd_dhcp_client_free(client);
-
-        close(test_fd[0]);
-        close(test_fd[1]);
-
-        callback_recv = NULL;
-        xid = 0;
-}
-
-int main(int argc, char *argv[])
-{
-        sd_event *e;
-
-        assert_se(sd_event_new(&e) >= 0);
-
-        test_request_basic(e);
-        test_checksum();
-
-        test_discover_message(e);
-        test_addr_acq(e);
-
-        return 0;
-}
diff --git a/src/libsystemd-dhcp/test-dhcp-option.c b/src/libsystemd-dhcp/test-dhcp-option.c
deleted file mode 100644
index 8659fd5..0000000
--- a/src/libsystemd-dhcp/test-dhcp-option.c
+++ /dev/null
@@ -1,378 +0,0 @@
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <assert.h>
-
-#include "util.h"
-#include "macro.h"
-
-#include "dhcp-protocol.h"
-#include "dhcp-internal.h"
-
-struct option_desc {
-        uint8_t sname[64];
-        int snamelen;
-        uint8_t file[128];
-        int filelen;
-        uint8_t options[128];
-        int len;
-        bool success;
-        int filepos;
-        int snamepos;
-        int pos;
-};
-
-static bool verbose = false;
-
-static struct option_desc option_tests[] = {
-        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
-        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
-                          DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
-        { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
-        { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
-                          0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
-                          0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
-                          0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
-                          0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
-          40, true, },
-        { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
-                          42, 3, 0, 0, 0 }, 8, true, },
-        { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
-
-        { {}, 0,
-          { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
-          { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
-
-        { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
-          { 222, 3, 1, 2, 3 }, 5,
-          { DHCP_OPTION_OVERLOAD, 1,
-            DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
-};
-
-static const char *dhcp_type(int type)
-{
-        switch(type) {
-        case DHCP_DISCOVER:
-                return "DHCPDISCOVER";
-        case DHCP_OFFER:
-                return "DHCPOFFER";
-        case DHCP_REQUEST:
-                return "DHCPREQUEST";
-        case DHCP_DECLINE:
-                return "DHCPDECLINE";
-        case DHCP_ACK:
-                return "DHCPACK";
-        case DHCP_NAK:
-                return "DHCPNAK";
-        case DHCP_RELEASE:
-                return "DHCPRELEASE";
-        default:
-                return "unknown";
-        }
-}
-
-static void test_invalid_buffer_length(void)
-{
-        DHCPMessage message;
-
-        assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
-        assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage), NULL, NULL)
-               == -EINVAL);
-}
-
-static void test_cookie(void)
-{
-        _cleanup_free_ DHCPMessage *message;
-        size_t len = sizeof(DHCPMessage) + 4;
-        uint8_t *opt;
-
-        message = malloc0(len);
-
-        opt = (uint8_t *)(message + 1);
-        opt[0] = 0xff;
-
-        assert_se(dhcp_option_parse(message, len, NULL, NULL) == -EINVAL);
-
-        opt[0] = 99;
-        opt[1] = 130;
-        opt[2] = 83;
-        opt[3] = 99;
-
-        assert_se(dhcp_option_parse(message, len, NULL, NULL) == -ENOMSG);
-}
-
-static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
-                uint8_t *file, uint8_t filelen,
-                uint8_t *sname, uint8_t snamelen)
-{
-        DHCPMessage *message;
-        size_t len = sizeof(DHCPMessage) + 4 + optlen;
-        uint8_t *opt;
-
-        message = malloc0(len);
-        opt = (uint8_t *)(message + 1);
-
-        opt[0] = 99;
-        opt[1] = 130;
-        opt[2] = 83;
-        opt[3] = 99;
-
-        if (options && optlen)
-                memcpy(&opt[4], options, optlen);
-
-        if (file && filelen <= 128)
-                memcpy(&message->file, file, filelen);
-
-        if (sname && snamelen <= 64)
-                memcpy(&message->sname, sname, snamelen);
-
-        return message;
-}
-
-static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen)
-{
-        while (*descpos < *desclen) {
-                switch(descoption[*descpos]) {
-                case DHCP_OPTION_PAD:
-                        *descpos += 1;
-                        break;
-
-                case DHCP_OPTION_MESSAGE_TYPE:
-                case DHCP_OPTION_OVERLOAD:
-                        *descpos += 3;
-                        break;
-
-                default:
-                        return;
-                }
-        }
-}
-
-static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
-                           void *user_data)
-{
-        struct option_desc *desc = user_data;
-        uint8_t *descoption = NULL;
-        int *desclen = NULL, *descpos = NULL;
-        uint8_t optcode = 0;
-        uint8_t optlen = 0;
-        uint8_t i;
-
-        assert_se((!desc && !code && !len) || desc);
-
-        if (!desc)
-                return -EINVAL;
-
-        assert_se(code != DHCP_OPTION_PAD);
-        assert_se(code != DHCP_OPTION_END);
-        assert_se(code != DHCP_OPTION_MESSAGE_TYPE);
-        assert_se(code != DHCP_OPTION_OVERLOAD);
-
-        while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
-
-                if (desc->pos >= 0) {
-                        descoption = &desc->options[0];
-                        desclen = &desc->len;
-                        descpos = &desc->pos;
-                } else if (desc->filepos >= 0) {
-                        descoption = &desc->file[0];
-                        desclen = &desc->filelen;
-                        descpos = &desc->filepos;
-                } else if (desc->snamepos >= 0) {
-                        descoption = &desc->sname[0];
-                        desclen = &desc->snamelen;
-                        descpos = &desc->snamepos;
-                }
-
-                assert_se(descoption && desclen && descpos);
-
-                if (*desclen)
-                        test_ignore_opts(descoption, descpos, desclen);
-
-                if (*descpos < *desclen)
-                        break;
-
-                if (*descpos == *desclen)
-                        *descpos = -1;
-        }
-
-        assert_se(descpos);
-        assert_se(*descpos != -1);
-
-        optcode = descoption[*descpos];
-        optlen = descoption[*descpos + 1];
-
-        if (verbose)
-                printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
-                                len, optlen);
-
-        assert_se(code == optcode);
-        assert_se(len == optlen);
-
-        for (i = 0; i < len; i++) {
-
-                if (verbose)
-                        printf("0x%02x(0x%02x) ", option[i],
-                                        descoption[*descpos + 2 + i]);
-
-                assert_se(option[i] == descoption[*descpos + 2 + i]);
-        }
-
-        if (verbose)
-                printf("\n");
-
-        *descpos += optlen + 2;
-
-        test_ignore_opts(descoption, descpos, desclen);
-
-        if (desc->pos != -1 && desc->pos == desc->len)
-                desc->pos = -1;
-
-        if (desc->filepos != -1 && desc->filepos == desc->filelen)
-                desc->filepos = -1;
-
-        if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
-                desc->snamepos = -1;
-
-        return 0;
-}
-
-static void test_options(struct option_desc *desc)
-{
-        uint8_t *options = NULL;
-        uint8_t *file = NULL;
-        uint8_t *sname = NULL;
-        int optlen = 0;
-        int filelen = 0;
-        int snamelen = 0;
-        int buflen = 0;
-        _cleanup_free_ DHCPMessage *message;
-        int res;
-
-        if (desc) {
-                file = &desc->file[0];
-                filelen = desc->filelen;
-                if (!filelen)
-                        desc->filepos = -1;
-
-                sname = &desc->sname[0];
-                snamelen = desc->snamelen;
-                if (!snamelen)
-                        desc->snamepos = -1;
-
-                options = &desc->options[0];
-                optlen = desc->len;
-                desc->pos = 0;
-        }
-        message = create_message(options, optlen, file, filelen,
-                        sname, snamelen);
-
-        buflen = sizeof(DHCPMessage) + 4 + optlen;
-
-        if (!desc) {
-                assert_se((res = dhcp_option_parse(message, buflen,
-                                                test_options_cb,
-                                                NULL)) == -ENOMSG);
-        } else if (desc->success) {
-                assert_se((res = dhcp_option_parse(message, buflen,
-                                                test_options_cb,
-                                                desc)) >= 0);
-                assert_se(desc->pos == -1 && desc->filepos == -1 &&
-                                desc->snamepos == -1);
-        } else
-                assert_se((res = dhcp_option_parse(message, buflen,
-                                                test_options_cb,
-                                                desc)) < 0);
-
-        if (verbose)
-                printf("DHCP type %s\n", dhcp_type(res));
-}
-
-static uint8_t result[64] = {
-        'A', 'B', 'C', 'D',
-};
-
-static uint8_t options[64] = {
-        'A', 'B', 'C', 'D',
-        160, 2, 0x11, 0x12,
-        0,
-        31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
-        0,
-        55, 3, 0x51, 0x52, 0x53,
-        17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
-        255
-};
-
-static void test_option_set(void)
-{
-        size_t len, oldlen;
-        int pos, i;
-        uint8_t *opt;
-
-        assert_se(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
-
-        len = 0;
-        opt = &result[0];
-        assert_se(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
-        assert_se(opt == &result[0] && len == 0);
-
-        assert_se(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
-                                  0, NULL) == -ENOBUFS);
-        assert_se(opt == &result[0] && len == 0);
-
-        opt = &result[4];
-        len = 1;
-        assert_se(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
-                                    0, NULL) >= 0);
-        assert_se(opt == &result[5] && len == 0);
-
-        pos = 4;
-        len = 60;
-        while (pos < 64 && options[pos] != DHCP_OPTION_END) {
-                opt = &result[pos];
-                oldlen = len;
-
-                assert_se(dhcp_option_append(&opt, &len, options[pos],
-                                          options[pos + 1],
-                                          &options[pos + 2]) >= 0);
-
-                if (options[pos] == DHCP_OPTION_PAD) {
-                        assert_se(opt == &result[pos + 1]);
-                        assert_se(len == oldlen - 1);
-                        pos++;
-                } else {
-                        assert_se(opt == &result[pos + 2 + options[pos + 1]]);
-                        assert_se(len == oldlen - 2 - options[pos + 1]);
-                        pos += 2 + options[pos + 1];
-                }
-        }
-
-        for (i = 0; i < pos; i++) {
-                if (verbose)
-                        printf("%2d: 0x%02x(0x%02x)\n", i, result[i],
-                               options[i]);
-                assert_se(result[i] == options[i]);
-        }
-
-        if (verbose)
-                printf ("\n");
-}
-
-int main(int argc, char *argv[])
-{
-        unsigned int i;
-
-        test_invalid_buffer_length();
-        test_cookie();
-
-        test_options(NULL);
-
-        for (i = 0; i < ELEMENTSOF(option_tests); i++)
-                test_options(&option_tests[i]);
-
-        test_option_set();
-
-        return 0;
-}
diff --git a/src/libsystemd-network/Makefile b/src/libsystemd-network/Makefile
new file mode 120000
index 0000000..d0b0e8e
--- /dev/null
+++ b/src/libsystemd-network/Makefile
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
new file mode 100644
index 0000000..ce83b81
--- /dev/null
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+  Copyright (C) 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+#include <linux/if_packet.h>
+
+#include "socket-util.h"
+
+#include "dhcp-protocol.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link);
+int dhcp_network_bind_udp_socket(int index, 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(uint8_t **buf, size_t *buflen, uint8_t code,
+                       size_t optlen, const void *optval);
+
+typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
+                                const uint8_t *option, void *user_data);
+
+int dhcp_option_parse(DHCPMessage *message, size_t len,
+                      dhcp_option_cb_t cb, void *user_data);
+
+int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type,
+                      uint8_t **opt, size_t *optlen);
+
+void dhcp_packet_append_ip_headers(DHCPPacket *packet, uint16_t len);
+
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
+
+#define log_dhcp_client(client, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT: " fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
new file mode 100644
index 0000000..d12bcac
--- /dev/null
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -0,0 +1,59 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+  Copyright (C) 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+#include <linux/if_packet.h>
+
+#include "refcnt.h"
+
+#include "dhcp-protocol.h"
+
+#include "sd-dhcp-client.h"
+
+struct sd_dhcp_lease {
+        RefCount n_ref;
+
+        uint32_t t1;
+        uint32_t t2;
+        uint32_t lifetime;
+        be32_t address;
+        be32_t server_address;
+        be32_t subnet_mask;
+        be32_t router;
+        struct in_addr *dns;
+        size_t dns_size;
+        uint16_t mtu;
+        char *domainname;
+        char *hostname;
+};
+
+int dhcp_lease_new(sd_dhcp_lease **ret);
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
+                              void *user_data);
+
+int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
+int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref);
+#define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp)
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
new file mode 100644
index 0000000..934e8bf
--- /dev/null
+++ b/src/libsystemd-network/dhcp-network.c
@@ -0,0 +1,109 @@
+/***
+  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 <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "socket-util.h"
+
+#include "dhcp-internal.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
+{
+        int s, one = 1;
+
+        assert(index > 0);
+        assert(link);
+
+        s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                   htons(ETH_P_IP));
+        if (s < 0)
+                return -errno;
+
+        link->ll.sll_family = AF_PACKET;
+        link->ll.sll_protocol = htons(ETH_P_IP);
+        link->ll.sll_ifindex =  index;
+        link->ll.sll_halen = ETH_ALEN;
+        memset(link->ll.sll_addr, 0xff, ETH_ALEN);
+
+        if (setsockopt (s, SOL_PACKET, PACKET_AUXDATA, &one, sizeof(one)) < 0)
+                return -errno;
+
+        if (bind(s, &link->sa, sizeof(link->ll)) < 0) {
+                close_nointr_nofail(s);
+                return -errno;
+        }
+
+        return s;
+}
+
+int dhcp_network_bind_udp_socket(int index, be32_t address, uint16_t port)
+{
+        int s;
+        union sockaddr_union src = {
+                .in.sin_family = AF_INET,
+                .in.sin_port = htobe16(port),
+                .in.sin_addr.s_addr = address,
+        };
+
+        s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+        if (s < 0)
+                return -errno;
+
+        if (bind(s, &src.sa, sizeof(src.in)) < 0) {
+                close_nointr_nofail(s);
+                return -errno;
+        }
+
+        return s;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+                                 const void *packet, size_t len)
+{
+        assert(link);
+        assert(packet);
+        assert(len);
+
+        if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 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,
+        };
+
+        if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
+                return -errno;
+
+        return 0;
+}
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
new file mode 100644
index 0000000..4d45b3b
--- /dev/null
+++ b/src/libsystemd-network/dhcp-option.c
@@ -0,0 +1,184 @@
+/*-*- 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 <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "dhcp-internal.h"
+
+int dhcp_option_append(uint8_t **buf, size_t *buflen, uint8_t code,
+                       size_t optlen, const void *optval)
+{
+        if (!buf || !buflen)
+                return -EINVAL;
+
+        switch (code) {
+
+        case DHCP_OPTION_PAD:
+        case DHCP_OPTION_END:
+                if (*buflen < 1)
+                        return -ENOBUFS;
+
+                (*buf)[0] = code;
+                *buf += 1;
+                *buflen -= 1;
+                break;
+
+        default:
+                if (*buflen < optlen + 2)
+                        return -ENOBUFS;
+
+                if (!optval)
+                        return -EINVAL;
+
+                (*buf)[0] = code;
+                (*buf)[1] = optlen;
+                memcpy(&(*buf)[2], optval, optlen);
+
+                *buf += optlen + 2;
+                *buflen -= (optlen + 2);
+
+                break;
+        }
+
+        return 0;
+}
+
+static int parse_options(const uint8_t *buf, size_t buflen, uint8_t *overload,
+                         uint8_t *message_type, dhcp_option_cb_t cb,
+                         void *user_data)
+{
+        const uint8_t *code = buf;
+        const uint8_t *len;
+
+        while (buflen > 0) {
+                switch (*code) {
+                case DHCP_OPTION_PAD:
+                        buflen -= 1;
+                        code++;
+                        break;
+
+                case DHCP_OPTION_END:
+                        return 0;
+
+                case DHCP_OPTION_MESSAGE_TYPE:
+                        if (buflen < 3)
+                                return -ENOBUFS;
+                        buflen -= 3;
+
+                        len = code + 1;
+                        if (*len != 1)
+                                return -EINVAL;
+
+                        if (message_type)
+                                *message_type = *(len + 1);
+
+                        code += 3;
+
+                        break;
+
+                case DHCP_OPTION_OVERLOAD:
+                        if (buflen < 3)
+                                return -ENOBUFS;
+                        buflen -= 3;
+
+                        len = code + 1;
+                        if (*len != 1)
+                                return -EINVAL;
+
+                        if (overload)
+                                *overload = *(len + 1);
+
+                        code += 3;
+
+                        break;
+
+                default:
+                        if (buflen < 3)
+                                return -ENOBUFS;
+
+                        len = code + 1;
+
+                        if (buflen < (size_t)*len + 2)
+                                return -EINVAL;
+                        buflen -= *len + 2;
+
+                        if (cb)
+                                cb(*code, *len, len + 1, user_data);
+
+                        code += *len + 2;
+
+                        break;
+                }
+        }
+
+        if (buflen)
+                return -EINVAL;
+
+        return 0;
+}
+
+int dhcp_option_parse(DHCPMessage *message, size_t len,
+                      dhcp_option_cb_t cb, void *user_data)
+{
+        uint8_t overload = 0;
+        uint8_t message_type = 0;
+        uint8_t *opt = (uint8_t *)(message + 1);
+        int res;
+
+        if (!message)
+                return -EINVAL;
+
+        if (len < sizeof(DHCPMessage) + 4)
+                return -EINVAL;
+
+        len -= sizeof(DHCPMessage) + 4;
+
+        if (opt[0] != 0x63 && opt[1] != 0x82 && opt[2] != 0x53 &&
+                        opt[3] != 0x63)
+                return -EINVAL;
+
+        res = parse_options(&opt[4], len, &overload, &message_type,
+                        cb, user_data);
+        if (res < 0)
+                return res;
+
+        if (overload & DHCP_OVERLOAD_FILE) {
+                res = parse_options(message->file, sizeof(message->file),
+                                NULL, &message_type, cb, user_data);
+                if (res < 0)
+                        return res;
+        }
+
+        if (overload & DHCP_OVERLOAD_SNAME) {
+                res = parse_options(message->sname, sizeof(message->sname),
+                                NULL, &message_type, cb, user_data);
+                if (res < 0)
+                        return res;
+        }
+
+        if (message_type)
+                return message_type;
+
+        return -ENOMSG;
+}
diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
new file mode 100644
index 0000000..95c4277
--- /dev/null
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -0,0 +1,191 @@
+/***
+  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 <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <sys/param.h>
+
+#include "util.h"
+#include "list.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-lease-internal.h"
+#include "dhcp-internal.h"
+#include "sd-dhcp-lease.h"
+#include "sd-dhcp-client.h"
+
+#define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
+
+int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
+                      uint8_t type, uint8_t **opt, size_t *optlen) {
+        int err;
+
+        assert(op == BOOTREQUEST || op == BOOTREPLY);
+
+        *opt = (uint8_t *)(message + 1);
+
+        if (*optlen < 4)
+                return -ENOBUFS;
+        *optlen -= 4;
+
+        message->op = op;
+        message->htype = ARPHRD_ETHER;
+        message->hlen = ETHER_ADDR_LEN;
+        message->xid = htobe32(xid);
+
+        (*opt)[0] = 0x63;
+        (*opt)[1] = 0x82;
+        (*opt)[2] = 0x53;
+        (*opt)[3] = 0x63;
+
+        *opt += 4;
+
+        err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
+                                 &type);
+        if (err < 0)
+                return err;
+
+        return 0;
+}
+
+static uint16_t dhcp_checksum(void *buf, int len) {
+        uint32_t sum;
+        uint16_t *check;
+        int i;
+        uint8_t *odd;
+
+        sum = 0;
+        check = buf;
+
+        for (i = 0; i < len / 2 ; i++)
+                sum += check[i];
+
+        if (len & 0x01) {
+                odd = buf;
+                sum += odd[len - 1];
+        }
+
+        while (sum >> 16)
+                sum = (sum & 0xffff) + (sum >> 16);
+
+        return ~sum;
+}
+
+void dhcp_packet_append_ip_headers(DHCPPacket *packet, uint16_t len) {
+        packet->ip.version = IPVERSION;
+        packet->ip.ihl = DHCP_IP_SIZE / 4;
+        packet->ip.tot_len = htobe16(len);
+
+        packet->ip.protocol = IPPROTO_UDP;
+        packet->ip.saddr = INADDR_ANY;
+        packet->ip.daddr = INADDR_BROADCAST;
+
+        packet->udp.source = htobe16(DHCP_PORT_CLIENT);
+        packet->udp.dest = htobe16(DHCP_PORT_SERVER);
+
+        packet->udp.len = htobe16(len - DHCP_IP_SIZE);
+
+        packet->ip.check = packet->udp.len;
+        packet->udp.check = dhcp_checksum(&packet->ip.ttl, len - 8);
+
+        packet->ip.ttl = IPDEFTTL;
+        packet->ip.check = 0;
+        packet->ip.check = dhcp_checksum(&packet->ip, DHCP_IP_SIZE);
+}
+
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
+        size_t hdrlen;
+
+        assert(packet);
+
+        /* IP */
+
+        if (len < DHCP_IP_SIZE) {
+                log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
+                                " smaller than IP header (%u bytes)", len,
+                                DHCP_IP_SIZE);
+                return -EINVAL;
+        }
+
+        if (packet->ip.ihl < 5) {
+                log_dhcp_client(client, "ignoring packet: IPv4 IHL (%u words) invalid",
+                                packet->ip.ihl);
+                return -EINVAL;
+        }
+
+        hdrlen = packet->ip.ihl * 4;
+        if (hdrlen < 20) {
+                log_dhcp_client(client, "ignoring packet: IPv4 IHL (%zu bytes) "
+                                "smaller than minimum (20 bytes)", hdrlen);
+                return -EINVAL;
+        }
+
+        if (len < hdrlen) {
+                log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
+                                "smaller than expected (%zu) by IP header", len,
+                                hdrlen);
+                return -EINVAL;
+        }
+
+        if (dhcp_checksum(&packet->ip, hdrlen)) {
+                log_dhcp_client(client, "ignoring packet: invalid IP checksum");
+                return -EINVAL;
+        }
+
+        /* UDP */
+
+        if (len < DHCP_IP_UDP_SIZE) {
+                log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
+                                " smaller than IP+UDP header (%u bytes)", len,
+                                DHCP_IP_UDP_SIZE);
+                return -EINVAL;
+        }
+
+        if (len < hdrlen + be16toh(packet->udp.len)) {
+                log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
+                                "smaller than expected (%zu) by UDP header", len,
+                                hdrlen + be16toh(packet->udp.len));
+                return -EINVAL;
+        }
+
+        if (checksum && packet->udp.check) {
+                packet->ip.check = packet->udp.len;
+                packet->ip.ttl = 0;
+
+                if (dhcp_checksum(&packet->ip.ttl,
+                                  be16toh(packet->udp.len) + 12)) {
+                        log_dhcp_client(client, "ignoring packet: invalid UDP checksum");
+                        return -EINVAL;
+                }
+        }
+
+        if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
+                log_dhcp_client(client, "ignoring packet: to port %u, which "
+                                "is not the DHCP client port (%u)",
+                                be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
+                return -EINVAL;
+        }
+
+        return 0;
+}
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
new file mode 100644
index 0000000..81d36ce
--- /dev/null
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -0,0 +1,121 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <stdint.h>
+
+#include "macro.h"
+#include "sparse-endian.h"
+
+struct DHCPMessage {
+        uint8_t op;
+        uint8_t htype;
+        uint8_t hlen;
+        uint8_t hops;
+        be32_t xid;
+        be16_t secs;
+        be16_t flags;
+        be32_t ciaddr;
+        be32_t yiaddr;
+        be32_t siaddr;
+        be32_t giaddr;
+        uint8_t chaddr[16];
+        uint8_t sname[64];
+        uint8_t file[128];
+} _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_MIN_OPTIONS_SIZE   312
+
+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,
+};
+
+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,
+};
+
+enum {
+        DHCP_OVERLOAD_FILE                      = 1,
+        DHCP_OVERLOAD_SNAME                     = 2,
+};
+
+enum {
+        DHCP_OPTION_PAD                         = 0,
+        DHCP_OPTION_SUBNET_MASK                 = 1,
+        DHCP_OPTION_ROUTER                      = 3,
+        DHCP_OPTION_DOMAIN_NAME_SERVER          = 6,
+        DHCP_OPTION_HOST_NAME                   = 12,
+        DHCP_OPTION_DOMAIN_NAME                 = 15,
+        DHCP_OPTION_INTERFACE_MTU               = 26,
+        DHCP_OPTION_NTP_SERVER                  = 42,
+        DHCP_OPTION_REQUESTED_IP_ADDRESS        = 50,
+        DHCP_OPTION_IP_ADDRESS_LEASE_TIME       = 51,
+        DHCP_OPTION_OVERLOAD                    = 52,
+        DHCP_OPTION_MESSAGE_TYPE                = 53,
+        DHCP_OPTION_SERVER_IDENTIFIER           = 54,
+        DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
+        DHCP_OPTION_MAXIMUM_MESSAGE_SIZE        = 57,
+        DHCP_OPTION_RENEWAL_T1_TIME             = 58,
+        DHCP_OPTION_REBINDING_T2_TIME           = 59,
+        DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
+        DHCP_OPTION_END                         = 255,
+};
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
new file mode 100644
index 0000000..1f676cc
--- /dev/null
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -0,0 +1,1051 @@
+/***
+  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 <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <net/ethernet.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include "util.h"
+#include "list.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+#include "dhcp-lease-internal.h"
+#include "sd-dhcp-client.h"
+
+struct sd_dhcp_client {
+        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;
+        uint8_t *req_opts;
+        size_t req_opts_allocated;
+        size_t req_opts_size;
+        be32_t last_addr;
+        struct ether_addr mac_addr;
+        uint32_t xid;
+        usec_t start_time;
+        uint16_t secs;
+        unsigned int attempt;
+        usec_t request_sent;
+        sd_event_source *timeout_t1;
+        sd_event_source *timeout_t2;
+        sd_event_source *timeout_expire;
+        sd_dhcp_client_cb_t cb;
+        void *userdata;
+        sd_dhcp_lease *lease;
+};
+
+static const uint8_t default_req_opts[] = {
+        DHCP_OPTION_SUBNET_MASK,
+        DHCP_OPTION_ROUTER,
+        DHCP_OPTION_HOST_NAME,
+        DHCP_OPTION_DOMAIN_NAME,
+        DHCP_OPTION_DOMAIN_NAME_SERVER,
+        DHCP_OPTION_NTP_SERVER,
+};
+
+static int client_receive_message_raw(sd_event_source *s, int fd,
+                                      uint32_t revents, void *userdata);
+static int client_receive_message_udp(sd_event_source *s, int fd,
+                                      uint32_t revents, void *userdata);
+
+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_option(sd_dhcp_client *client, uint8_t option) {
+        size_t i;
+
+        assert_return(client, -EINVAL);
+        assert_return (client->state == DHCP_STATE_INIT, -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(client->state == DHCP_STATE_INIT, -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(client->state == DHCP_STATE_INIT, -EBUSY);
+        assert_return(interface_index >= -1, -EINVAL);
+
+        client->index = interface_index;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_mac(sd_dhcp_client *client,
+                           const struct ether_addr *addr) {
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
+
+        memcpy(&client->mac_addr, addr, ETH_ALEN);
+
+        return 0;
+}
+
+int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
+        assert_return(client, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (client->state != DHCP_STATE_BOUND &&
+            client->state != DHCP_STATE_RENEWING &&
+            client->state != DHCP_STATE_REBINDING)
+                return -EADDRNOTAVAIL;
+
+        *ret = sd_dhcp_lease_ref(client->lease);
+
+        return 0;
+}
+
+static int client_notify(sd_dhcp_client *client, int event) {
+        if (client->cb)
+                client->cb(client, event, client->userdata);
+
+        return 0;
+}
+
+static int client_stop(sd_dhcp_client *client, int error) {
+        assert_return(client, -EINVAL);
+
+        client->receive_message =
+                sd_event_source_unref(client->receive_message);
+
+        if (client->fd >= 0)
+                close(client->fd);
+        client->fd = -1;
+
+        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_notify(client, error);
+
+        client->start_time = 0;
+        client->secs = 0;
+        client->state = DHCP_STATE_INIT;
+
+        if (client->lease)
+                client->lease = sd_dhcp_lease_unref(client->lease);
+
+        log_dhcp_client(client, "STOPPED");
+
+        return 0;
+}
+
+static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
+                               uint8_t type, uint16_t secs, uint8_t **opt,
+                               size_t *optlen) {
+        int r;
+
+        assert(secs);
+
+        r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
+                              optlen);
+        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 */
+        message->secs = htobe16(secs);
+
+        memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
+
+        if (client->state == DHCP_STATE_RENEWING ||
+            client->state == DHCP_STATE_REBINDING)
+                message->ciaddr = client->lease->address;
+
+        /* Some DHCP servers will refuse to issue an DHCP lease if the Client
+           Identifier option is not set */
+        r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
+                               ETH_ALEN, &client->mac_addr);
+        if (r < 0)
+                return r;
+
+        if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
+                be16_t max_size;
+
+                r = dhcp_option_append(opt, optlen,
+                                       DHCP_OPTION_PARAMETER_REQUEST_LIST,
+                                       client->req_opts_size,
+                                       client->req_opts);
+                if (r < 0)
+                        return r;
+
+                /* Some DHCP servers will send bigger DHCP packets than the
+                   defined default size unless the Maximum Messge Size option
+                   is explicitely set */
+                max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
+                                   DHCP_MIN_OPTIONS_SIZE);
+                r = dhcp_option_append(opt, optlen,
+                                       DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
+                                       2, &max_size);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
+        int err = 0;
+        _cleanup_free_ DHCPPacket *discover;
+        size_t optlen, len;
+        uint8_t *opt;
+
+        optlen = DHCP_MIN_OPTIONS_SIZE;
+        len = sizeof(DHCPPacket) + optlen;
+
+        discover = malloc0(len);
+
+        if (!discover)
+                return -ENOMEM;
+
+        err = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
+                                  secs, &opt, &optlen);
+        if (err < 0)
+                return err;
+
+        if (client->last_addr != INADDR_ANY) {
+                err = dhcp_option_append(&opt, &optlen,
+                                         DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                         4, &client->last_addr);
+                if (err < 0)
+                        return err;
+        }
+
+        err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
+        if (err < 0)
+                return err;
+
+        dhcp_packet_append_ip_headers(discover, len);
+
+        err = dhcp_network_send_raw_socket(client->fd, &client->link,
+                                           discover, len);
+
+        log_dhcp_client(client, "DISCOVER");
+
+        return err;
+}
+
+static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
+        _cleanup_free_ DHCPPacket *request;
+        size_t optlen, len;
+        int err;
+        uint8_t *opt;
+
+        optlen = DHCP_MIN_OPTIONS_SIZE;
+        len = sizeof(DHCPPacket) + optlen;
+
+        request = malloc0(len);
+        if (!request)
+                return -ENOMEM;
+
+        err = client_message_init(client, &request->dhcp, DHCP_REQUEST, secs,
+                                  &opt, &optlen);
+        if (err < 0)
+                return err;
+
+        if (client->state == DHCP_STATE_REQUESTING) {
+                err = dhcp_option_append(&opt, &optlen,
+                                         DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                         4, &client->lease->address);
+                if (err < 0)
+                        return err;
+
+                err = dhcp_option_append(&opt, &optlen,
+                                         DHCP_OPTION_SERVER_IDENTIFIER,
+                                         4, &client->lease->server_address);
+                if (err < 0)
+                        return err;
+        }
+
+        err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
+        if (err < 0)
+                return err;
+
+        if (client->state == DHCP_STATE_RENEWING) {
+                err = dhcp_network_send_udp_socket(client->fd,
+                                                   client->lease->server_address,
+                                                   DHCP_PORT_SERVER,
+                                                   &request->dhcp,
+                                                   len - DHCP_IP_UDP_SIZE);
+        } else {
+                dhcp_packet_append_ip_headers(request, len);
+
+                err = dhcp_network_send_raw_socket(client->fd, &client->link,
+                                                   request, len);
+        }
+
+        log_dhcp_client(client, "REQUEST");
+
+        return err;
+}
+
+static uint16_t client_update_secs(sd_dhcp_client *client, usec_t time_now)
+{
+        client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
+
+        return client->secs;
+}
+
+static int client_timeout_resend(sd_event_source *s, uint64_t usec,
+                                 void *userdata) {
+        sd_dhcp_client *client = userdata;
+        usec_t next_timeout = 0;
+        uint32_t time_left;
+        int r = 0;
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        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 = usec + 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 = usec + time_left * USEC_PER_SEC;
+                break;
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_BOUND:
+
+                if (client->attempt < 64)
+                        client->attempt *= 2;
+
+                next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC;
+
+                break;
+        }
+
+        next_timeout += (random_u32() & 0x1fffff);
+
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
+        r = sd_event_add_monotonic(client->event,
+                                     &client->timeout_resend,
+                                     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;
+
+        switch (client->state) {
+        case DHCP_STATE_INIT:
+
+                client_update_secs(client, usec);
+
+                r = client_send_discover(client, client->secs);
+                if (r >= 0) {
+                        client->state = DHCP_STATE_SELECTING;
+                        client->attempt = 1;
+                } else {
+                        if (client->attempt >= 64)
+                                goto error;
+                }
+
+                break;
+
+        case DHCP_STATE_SELECTING:
+                client_update_secs(client, usec);
+
+                r = client_send_discover(client, client->secs);
+                if (r < 0 && client->attempt >= 64)
+                        goto error;
+
+                break;
+
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+                r = client_send_request(client, client->secs);
+                if (r < 0 && client->attempt >= 64)
+                         goto error;
+
+                client->request_sent = usec;
+
+                break;
+
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_BOUND:
+
+                break;
+        }
+
+        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_events(sd_dhcp_client *client,
+                                    sd_event_io_handler_t io_callback,
+                                    usec_t usec) {
+        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;
+
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
+        r = sd_event_add_monotonic(client->event,
+                                   &client->timeout_resend,
+                                   usec, 0,
+                                   client_timeout_resend, client);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(client->timeout_resend,
+                                         client->event_priority);
+
+error:
+        if (r < 0)
+                client_stop(client, r);
+
+        return 0;
+
+}
+
+static int client_timeout_expire(sd_event_source *s, uint64_t usec,
+                                 void *userdata) {
+        sd_dhcp_client *client = userdata;
+
+        log_dhcp_client(client, "EXPIRED");
+
+        client_stop(client, DHCP_EVENT_EXPIRED);
+
+        return 0;
+}
+
+static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
+        sd_dhcp_client *client = userdata;
+        int r;
+
+        if (client->fd >= 0) {
+                client->receive_message =
+                        sd_event_source_unref(client->receive_message);
+                close(client->fd);
+                client->fd = -1;
+        }
+
+        client->state = DHCP_STATE_REBINDING;
+        client->attempt = 1;
+
+        r = dhcp_network_bind_raw_socket(client->index, &client->link);
+        if (r < 0) {
+                client_stop(client, r);
+                return 0;
+        }
+
+        client->fd = r;
+
+        log_dhcp_client(client, "TIMEOUT T2");
+
+        return client_initialize_events(client, client_receive_message_raw,
+                                        usec);
+}
+
+static int client_timeout_t1(sd_event_source *s, uint64_t usec,
+                             void *userdata) {
+        sd_dhcp_client *client = userdata;
+        int r;
+
+        client->state = DHCP_STATE_RENEWING;
+        client->attempt = 1;
+
+        r = dhcp_network_bind_udp_socket(client->index,
+                                         client->lease->address,
+                                         DHCP_PORT_CLIENT);
+        if (r < 0) {
+                client_stop(client, r);
+                return 0;
+        }
+
+        client->fd = r;
+
+        log_dhcp_client(client, "TIMEOUT T1");
+
+        return client_initialize_events(client, client_receive_message_udp, usec);
+}
+
+static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
+                               size_t len) {
+        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+        int r;
+
+        r = dhcp_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
+        if (r != DHCP_OFFER)
+                return -ENOMSG;
+
+        lease->address = offer->yiaddr;
+
+        if (lease->address == INADDR_ANY ||
+            lease->server_address == INADDR_ANY ||
+            lease->subnet_mask == INADDR_ANY ||
+            lease->lifetime == 0)
+                return -ENOMSG;
+
+        client->lease = lease;
+        lease = NULL;
+
+        log_dhcp_client(client, "OFFER");
+
+        return 0;
+}
+
+static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
+                             size_t len) {
+        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+        int r;
+
+        r = dhcp_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
+        if (r == DHCP_NAK) {
+                log_dhcp_client(client, "NAK");
+                return DHCP_EVENT_NO_LEASE;
+        }
+
+        if (r != DHCP_ACK)
+                return -ENOMSG;
+
+        lease->address = ack->yiaddr;
+
+        if (lease->address == INADDR_ANY ||
+            lease->server_address == INADDR_ANY ||
+            lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
+                return -ENOMSG;
+
+        r = DHCP_EVENT_IP_ACQUIRE;
+        if (client->lease) {
+                if (client->lease->address != lease->address ||
+                    client->lease->subnet_mask != lease->subnet_mask ||
+                    client->lease->router != lease->router) {
+                        r = DHCP_EVENT_IP_CHANGE;
+                }
+
+                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(uint64_t request_sent,
+                                       uint32_t lifetime) {
+        return request_sent + (lifetime - 3) * USEC_PER_SEC +
+                + (random_u32() & 0x1fffff);
+}
+
+static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
+        uint64_t next_timeout;
+        int r;
+
+        assert(client);
+        assert(client->event);
+
+        if (client->lease->lifetime < 10)
+                return -EINVAL;
+
+        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);
+
+        if (!client->lease->t1)
+                client->lease->t1 = client->lease->lifetime / 2;
+
+        next_timeout = client_compute_timeout(client->request_sent,
+                                              client->lease->t1);
+        if (next_timeout < usec)
+                return -EINVAL;
+
+        r = sd_event_add_monotonic(client->event,
+                                     &client->timeout_t1,
+                                     next_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;
+
+        if (!client->lease->t2)
+                client->lease->t2 = client->lease->lifetime * 7 / 8;
+
+        if (client->lease->t2 < client->lease->t1)
+                return -EINVAL;
+
+        if (client->lease->lifetime < client->lease->t2)
+                return -EINVAL;
+
+        next_timeout = client_compute_timeout(client->request_sent,
+                                              client->lease->t2);
+        if (next_timeout < usec)
+                return -EINVAL;
+
+        r = sd_event_add_monotonic(client->event,
+                                     &client->timeout_t2,
+                                     next_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;
+
+        next_timeout = client_compute_timeout(client->request_sent,
+                                              client->lease->lifetime);
+        if (next_timeout < usec)
+                return -EINVAL;
+
+        r = sd_event_add_monotonic(client->event,
+                                     &client->timeout_expire, next_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;
+
+        return 0;
+}
+
+static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
+                                 int len, usec_t time_now) {
+        int r = 0, notify_event = 0;
+
+        assert(client);
+        assert(client->event);
+        assert(message);
+
+        if (len < DHCP_MESSAGE_SIZE) {
+                log_dhcp_client(client, "message too small (%d bytes): "
+                                "ignoring", len);
+                return 0;
+        }
+
+        if (message->op != BOOTREPLY) {
+                log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
+                return 0;
+        }
+
+        if (be32toh(message->xid) != client->xid) {
+                log_dhcp_client(client, "received xid (%u) does not match "
+                                "expected (%u): ignoring",
+                                be32toh(message->xid), client->xid);
+                return 0;
+        }
+
+        if (memcmp(&message->chaddr[0], &client->mac_addr.ether_addr_octet,
+                   ETHER_ADDR_LEN)) {
+                log_dhcp_client(client, "received chaddr does not match "
+                                "expected: ignoring");
+                return 0;
+        }
+
+        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_monotonic(client->event,
+                                                   &client->timeout_resend,
+                                                   time_now, 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;
+                }
+
+                break;
+
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+
+                r = client_handle_ack(client, message, len);
+
+                if (r == DHCP_EVENT_NO_LEASE)
+                        goto error;
+
+                if (r >= 0) {
+                        client->timeout_resend =
+                                sd_event_source_unref(client->timeout_resend);
+
+                        if (client->state == DHCP_STATE_REQUESTING)
+                                notify_event = DHCP_EVENT_IP_ACQUIRE;
+                        else if (r != DHCP_EVENT_IP_ACQUIRE)
+                                notify_event = r;
+
+                        client->state = DHCP_STATE_BOUND;
+                        client->attempt = 1;
+
+                        client->last_addr = client->lease->address;
+
+                        r = client_set_lease_timeouts(client, time_now);
+                        if (r < 0)
+                                goto error;
+
+                        if (notify_event)
+                                client_notify(client, notify_event);
+
+                        client->receive_message =
+                                sd_event_source_unref(client->receive_message);
+                        close(client->fd);
+                        client->fd = -1;
+                }
+
+                r = 0;
+
+                break;
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_BOUND:
+
+                break;
+        }
+
+error:
+        if (r < 0 || r == DHCP_EVENT_NO_LEASE)
+                return client_stop(client, r);
+
+        return 0;
+}
+
+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;
+        usec_t time_now;
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        r = ioctl(fd, FIONREAD, &buflen);
+        if (r < 0 || buflen <= 0)
+                buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE;
+
+        message = malloc0(buflen);
+        if (!message)
+                return -ENOMEM;
+
+        len = read(fd, message, buflen);
+        if (len < 0)
+                return 0;
+
+        r = sd_event_get_now_monotonic(client->event, &time_now);
+        if (r < 0)
+                return client_stop(client, r);
+
+        return client_handle_message(client, message, len,
+                                     time_now);
+}
+
+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;
+        usec_t time_now;
+        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);
+        assert(client->event);
+
+        r = ioctl(fd, FIONREAD, &buflen);
+        if (r < 0 || buflen <= 0)
+                buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE;
+
+        packet = malloc0(buflen);
+        if (!packet)
+                return -ENOMEM;
+
+        iov.iov_base = packet;
+        iov.iov_len = buflen;
+
+        len = recvmsg(fd, &msg, 0);
+        if (len < 0) {
+                log_dhcp_client(client, "could not receive message from raw "
+                                "socket: %s", strerror(errno));
+                return 0;
+        }
+
+        for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
+                        struct tpacket_auxdata *aux = (void *)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;
+
+        r = sd_event_get_now_monotonic(client->event, &time_now);
+        if (r < 0)
+                return client_stop(client, r);
+
+        return client_handle_message(client, &packet->dhcp, len, time_now);
+}
+
+int sd_dhcp_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->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);
+
+        if (r < 0) {
+                client_stop(client, r);
+                return r;
+        }
+
+        client->fd = r;
+        client->start_time = now(CLOCK_MONOTONIC);
+        client->secs = 0;
+
+        log_dhcp_client(client, "STARTED");
+
+        return client_initialize_events(client, client_receive_message_raw,
+                                        client->start_time);
+}
+
+int sd_dhcp_client_stop(sd_dhcp_client *client) {
+        return client_stop(client, DHCP_EVENT_STOP);
+}
+
+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;
+}
+
+void sd_dhcp_client_free(sd_dhcp_client *client) {
+        if (!client)
+                return;
+
+        sd_dhcp_client_stop(client);
+        sd_dhcp_client_detach_event(client);
+
+        free(client->req_opts);
+        free(client);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
+#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
+
+int sd_dhcp_client_new(sd_dhcp_client **ret) {
+        _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        client = new0(sd_dhcp_client, 1);
+        if (!client)
+                return -ENOMEM;
+
+        client->state = DHCP_STATE_INIT;
+        client->index = -1;
+        client->fd = -1;
+        client->attempt = 1;
+
+        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/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
new file mode 100644
index 0000000..0529b6d
--- /dev/null
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -0,0 +1,401 @@
+/***
+  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 <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+
+#include "util.h"
+#include "list.h"
+#include "mkdir.h"
+#include "fileio.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+#include "dhcp-lease-internal.h"
+#include "sd-dhcp-lease.h"
+#include "sd-dhcp-client.h"
+
+int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        addr->s_addr = lease->address;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
+        assert_return(lease, -EINVAL);
+        assert_return(mtu, -EINVAL);
+
+        if (lease->mtu)
+                *mtu = lease->mtu;
+        else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(addr_size, -EINVAL);
+
+        if (lease->dns_size) {
+                *addr_size = lease->dns_size;
+                *addr = lease->dns;
+        } else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
+        assert_return(lease, -EINVAL);
+        assert_return(domainname, -EINVAL);
+
+        if (lease->domainname)
+                *domainname = lease->domainname;
+        else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
+        assert_return(lease, -EINVAL);
+        assert_return(hostname, -EINVAL);
+
+        if (lease->hostname)
+                *hostname = lease->hostname;
+        else
+                return -ENOENT;
+
+        return 0;
+}
+
+int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        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);
+
+        addr->s_addr = lease->subnet_mask;
+
+        return 0;
+}
+
+sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
+        if (lease)
+                assert_se(REFCNT_INC(lease->n_ref) >= 2);
+
+        return lease;
+}
+
+sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
+        if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
+                free(lease->hostname);
+                free(lease->domainname);
+                free(lease->dns);
+                free(lease);
+        }
+
+        return NULL;
+}
+
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
+                              void *user_data) {
+        sd_dhcp_lease *lease = user_data;
+        be32_t val;
+
+        switch(code) {
+
+        case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+                if (len == 4) {
+                        memcpy(&val, option, 4);
+                        lease->lifetime = be32toh(val);
+                }
+
+                break;
+
+        case DHCP_OPTION_SERVER_IDENTIFIER:
+                if (len >= 4)
+                        memcpy(&lease->server_address, option, 4);
+
+                break;
+
+        case DHCP_OPTION_SUBNET_MASK:
+                if (len >= 4)
+                        memcpy(&lease->subnet_mask, option, 4);
+
+                break;
+
+        case DHCP_OPTION_ROUTER:
+                if (len >= 4)
+                        memcpy(&lease->router, option, 4);
+
+                break;
+
+        case DHCP_OPTION_DOMAIN_NAME_SERVER:
+                if (len && !(len % 4)) {
+                        unsigned i;
+
+                        lease->dns_size = len / 4;
+
+                        free(lease->dns);
+                        lease->dns = new0(struct in_addr, lease->dns_size);
+                        if (!lease->dns)
+                                return -ENOMEM;
+
+                        for (i = 0; i < lease->dns_size; i++) {
+                                memcpy(&lease->dns[i].s_addr, option + 4 * i, 4);
+                        }
+                }
+
+                break;
+
+        case DHCP_OPTION_INTERFACE_MTU:
+                if (len >= 2) {
+                        be16_t mtu;
+
+                        memcpy(&mtu, option, 2);
+                        lease->mtu = be16toh(mtu);
+
+                        if (lease->mtu < 68)
+                                lease->mtu = 0;
+                }
+
+                break;
+
+        case DHCP_OPTION_DOMAIN_NAME:
+                if (len >= 1) {
+                        free(lease->domainname);
+                        lease->domainname = strndup((const char *)option, len);
+                }
+
+                break;
+
+        case DHCP_OPTION_HOST_NAME:
+                if (len >= 1) {
+                        free(lease->hostname);
+                        lease->hostname = strndup((const char *)option, len);
+                }
+
+                break;
+
+        case DHCP_OPTION_RENEWAL_T1_TIME:
+                if (len == 4) {
+                        memcpy(&val, option, 4);
+                        lease->t1 = be32toh(val);
+                }
+
+                break;
+
+        case DHCP_OPTION_REBINDING_T2_TIME:
+                if (len == 4) {
+                        memcpy(&val, option, 4);
+                        lease->t2 = be32toh(val);
+                }
+
+                break;
+        }
+
+        return 0;
+}
+
+int dhcp_lease_new(sd_dhcp_lease **ret) {
+        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+
+        lease = new0(sd_dhcp_lease, 1);
+        if (!lease)
+                return -ENOMEM;
+
+        lease->n_ref = REFCNT_INIT;
+
+        *ret = lease;
+        lease = NULL;
+
+        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;
+        char buf[INET_ADDRSTRLEN];
+        struct in_addr address;
+        const char *string;
+        uint16_t mtu;
+        int r;
+
+        assert(lease);
+        assert(lease_file);
+
+        r = mkdir_safe_label("/run/systemd/network/leases", 0755, 0, 0);
+        if (r < 0)
+                goto finish;
+
+        r = fopen_temporary(lease_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
+
+        fchmod(fileno(f), 0644);
+
+        r = sd_dhcp_lease_get_address(lease, &address);
+        if (r < 0)
+                goto finish;
+
+        string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
+        if (!string) {
+                r = -errno;
+                goto finish;
+        }
+
+        fprintf(f,
+                "# This is private data. Do not parse.\n"
+                "ADDRESS=%s\n", string);
+
+        r = sd_dhcp_lease_get_router(lease, &address);
+        if (r < 0)
+                goto finish;
+
+        string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
+        if (!string) {
+                r = -errno;
+                goto finish;
+        }
+
+        fprintf(f,
+                "ROUTER=%s\n", string);
+
+        r = sd_dhcp_lease_get_netmask(lease, &address);
+        if (r < 0)
+                goto finish;
+
+        string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
+        if (!string) {
+                r = -errno;
+                goto finish;
+        }
+
+        fprintf(f,
+                "NETMASK=%s\n", string);
+
+        r = sd_dhcp_lease_get_mtu(lease, &mtu);
+        if (r >= 0)
+                fprintf(f, "MTU=%" PRIu16 "\n", mtu);
+
+/* TODO: DNS. See resolv.conf writing in network-manager.c */
+
+        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 = 0;
+
+        fflush(f);
+
+        if (ferror(f) || rename(temp_path, lease_file) < 0) {
+                r = -errno;
+                unlink(lease_file);
+                unlink(temp_path);
+        }
+
+finish:
+        if (r < 0)
+                log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
+
+        return r;
+}
+
+int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
+        _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
+        _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
+                            *mtu = NULL;
+        struct in_addr addr;
+        int r;
+
+        assert(lease_file);
+        assert(ret);
+
+        r = dhcp_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        r = parse_env_file(lease_file, NEWLINE,
+                           "ADDRESS", &address,
+                           "ROUTER", &router,
+                           "NETMASK", &netmask,
+                           "MTU", &mtu,
+                           "DOMAINNAME", &lease->domainname,
+                           "HOSTNAME", &lease->hostname,
+                           NULL);
+        if (r < 0) {
+                if (r == -ENOENT)
+                        return 0;
+
+                log_error("Failed to read %s: %s", lease_file, strerror(-r));
+                return r;
+        }
+
+        r = inet_pton(AF_INET, address, &addr);
+        if (r < 0)
+                return r;
+
+        lease->address = addr.s_addr;
+
+        r = inet_pton(AF_INET, router, &addr);
+        if (r < 0)
+                return r;
+
+        lease->router = addr.s_addr;
+
+        r = inet_pton(AF_INET, netmask, &addr);
+        if (r < 0)
+                return r;
+
+        lease->subnet_mask = addr.s_addr;
+
+        if (mtu) {
+                uint16_t u;
+                if (sscanf(mtu, "%" SCNu16, &u) > 0)
+                        lease->mtu = u;
+        }
+
+        *ret = lease;
+        lease = NULL;
+
+        return 0;
+}
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
new file mode 100644
index 0000000..8061e5f
--- /dev/null
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -0,0 +1,496 @@
+/*-*- 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 <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "socket-util.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+#include "sd-dhcp-client.h"
+
+static struct ether_addr mac_addr = {
+        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+};
+
+typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp);
+
+static bool verbose = false;
+static int test_fd[2];
+static test_callback_recv_t callback_recv;
+static be32_t xid;
+
+static void test_request_basic(sd_event *e)
+{
+        int r;
+
+        sd_dhcp_client *client;
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        r = sd_dhcp_client_new(&client);
+
+        assert_se(r >= 0);
+        assert_se(client);
+
+        r = sd_dhcp_client_attach_event(client, e, 0);
+        assert_se(r >= 0);
+
+        assert_se(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
+        assert_se(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
+        assert_se(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
+
+        assert_se(sd_dhcp_client_set_index(client, 15) == 0);
+        assert_se(sd_dhcp_client_set_index(client, -42) == -EINVAL);
+        assert_se(sd_dhcp_client_set_index(client, -1) == 0);
+
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_SUBNET_MASK) == -EEXIST);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_ROUTER) == -EEXIST);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_HOST_NAME) == -EEXIST);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_DOMAIN_NAME_SERVER)
+                        == -EEXIST);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_NTP_SERVER) == -EEXIST);
+
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_PAD) == -EINVAL);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_END) == -EINVAL);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_OVERLOAD) == -EINVAL);
+        assert_se(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_PARAMETER_REQUEST_LIST)
+                        == -EINVAL);
+
+        assert_se(sd_dhcp_client_set_request_option(client, 33) == 0);
+        assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
+        assert_se(sd_dhcp_client_set_request_option(client, 44) == 0);
+        assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
+}
+
+static uint16_t client_checksum(void *buf, int len)
+{
+        uint32_t sum;
+        uint16_t *check;
+        int i;
+        uint8_t *odd;
+
+        sum = 0;
+        check = buf;
+
+        for (i = 0; i < len / 2 ; i++)
+                sum += check[i];
+
+        if (len & 0x01) {
+                odd = buf;
+                sum += odd[len - 1];
+        }
+
+        while (sum >> 16)
+                sum = (sum & 0xffff) + (sum >> 16);
+
+        return ~sum;
+}
+
+static void test_checksum(void)
+{
+        uint8_t buf[20] = {
+                0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
+                0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0xff, 0xff, 0xff, 0xff
+        };
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        assert_se(client_checksum(&buf, 20) == be16toh(0x78ae));
+}
+
+static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
+                void *user_data)
+{
+        return 0;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+                                 const void *packet, size_t len)
+{
+        size_t size;
+        _cleanup_free_ DHCPPacket *discover;
+        uint16_t ip_check, udp_check;
+
+        assert_se(s >= 0);
+        assert_se(packet);
+
+        size = sizeof(DHCPPacket) + 4;
+        assert_se(len > size);
+
+        discover = memdup(packet, len);
+
+        assert_se(discover->ip.ttl == IPDEFTTL);
+        assert_se(discover->ip.protocol == IPPROTO_UDP);
+        assert_se(discover->ip.saddr == INADDR_ANY);
+        assert_se(discover->ip.daddr == INADDR_BROADCAST);
+        assert_se(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
+        assert_se(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
+
+        ip_check = discover->ip.check;
+
+        discover->ip.ttl = 0;
+        discover->ip.check = discover->udp.len;
+
+        udp_check = ~client_checksum(&discover->ip.ttl, len - 8);
+        assert_se(udp_check == 0xffff);
+
+        discover->ip.ttl = IPDEFTTL;
+        discover->ip.check = ip_check;
+
+        ip_check = ~client_checksum(&discover->ip, sizeof(discover->ip));
+        assert_se(ip_check == 0xffff);
+
+        assert_se(discover->dhcp.xid);
+        assert_se(memcmp(discover->dhcp.chaddr,
+                      &mac_addr.ether_addr_octet, 6) == 0);
+
+        size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
+
+        assert_se(callback_recv);
+        callback_recv(size, &discover->dhcp);
+
+        return 575;
+}
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
+{
+        if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
+                return -errno;
+
+        return test_fd[0];
+}
+
+int dhcp_network_bind_udp_socket(int index, be32_t address, uint16_t port)
+{
+        return 0;
+}
+
+int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
+                                 const void *packet, size_t len)
+{
+        return 0;
+}
+
+static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
+{
+        int res;
+
+        res = dhcp_option_parse(dhcp, size, check_options, NULL);
+        assert_se(res == DHCP_DISCOVER);
+
+        if (verbose)
+                printf("  recv DHCP Discover 0x%08x\n", be32toh(dhcp->xid));
+
+        return 0;
+}
+
+static void test_discover_message(sd_event *e)
+{
+        sd_dhcp_client *client;
+        int res, r;
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        r = sd_dhcp_client_new(&client);
+        assert_se(r >= 0);
+        assert_se(client);
+
+        r = sd_dhcp_client_attach_event(client, e, 0);
+        assert_se(r >= 0);
+
+        assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
+        assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
+
+        assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0);
+
+        callback_recv = test_discover_message_verify;
+
+        res = sd_dhcp_client_start(client);
+
+        assert_se(res == 0 || res == -EINPROGRESS);
+
+        sd_event_run(e, (uint64_t) -1);
+
+        sd_dhcp_client_stop(client);
+        sd_dhcp_client_free(client);
+
+        close(test_fd[0]);
+        close(test_fd[1]);
+
+        callback_recv = NULL;
+}
+
+static uint8_t test_addr_acq_offer[] = {
+        0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
+        0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
+        0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
+        0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
+        0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
+        0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36,
+        0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
+        0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff,
+        0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
+        0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
+        0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
+        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static uint8_t test_addr_acq_ack[] = {
+        0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00,
+        0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01,
+        0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44,
+        0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf,
+        0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
+        0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00,
+        0x00, 0x02, 0x58, 0x01, 0x04,   0xff, 0xff, 0xff,
+        0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f,
+        0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74,
+        0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01,
+        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static void test_addr_acq_acquired(sd_dhcp_client *client, int event,
+                                   void *userdata)
+{
+        sd_event *e = userdata;
+        sd_dhcp_lease *lease;
+        struct in_addr addr;
+
+        assert_se(client);
+        assert_se(event == DHCP_EVENT_IP_ACQUIRE);
+
+        assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0);
+        assert_se(lease);
+
+        assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0);
+        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44],
+                      sizeof(addr.s_addr)) == 0);
+
+        assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0);
+        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285],
+                      sizeof(addr.s_addr)) == 0);
+
+        assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0);
+        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308],
+                      sizeof(addr.s_addr)) == 0);
+
+        if (verbose)
+                printf("  DHCP address acquired\n");
+
+        sd_event_exit(e, 0);
+}
+
+static int test_addr_acq_recv_request(size_t size, DHCPMessage *request)
+{
+        uint16_t udp_check = 0;
+        int res;
+
+        res = dhcp_option_parse(request, size, check_options, NULL);
+        assert_se(res == DHCP_REQUEST);
+        assert_se(xid == request->xid);
+
+        if (verbose)
+                printf("  recv DHCP Request  0x%08x\n", be32toh(xid));
+
+        memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check));
+        memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid));
+        memcpy(&test_addr_acq_ack[56], &mac_addr.ether_addr_octet,
+               ETHER_ADDR_LEN);
+
+        callback_recv = NULL;
+
+        res = write(test_fd[1], test_addr_acq_ack,
+                    sizeof(test_addr_acq_ack));
+        assert_se(res == sizeof(test_addr_acq_ack));
+
+        if (verbose)
+                printf("  send DHCP Ack\n");
+
+        return 0;
+};
+
+static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover)
+{
+        uint16_t udp_check = 0;
+        int res;
+
+        res = dhcp_option_parse(discover, size, check_options, NULL);
+        assert_se(res == DHCP_DISCOVER);
+
+        xid = discover->xid;
+
+        if (verbose)
+                printf("  recv DHCP Discover 0x%08x\n", be32toh(xid));
+
+        memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check));
+        memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid));
+        memcpy(&test_addr_acq_offer[56], &mac_addr.ether_addr_octet,
+               ETHER_ADDR_LEN);
+
+        callback_recv = test_addr_acq_recv_request;
+
+        res = write(test_fd[1], test_addr_acq_offer,
+                    sizeof(test_addr_acq_offer));
+        assert_se(res == sizeof(test_addr_acq_offer));
+
+        if (verbose)
+                printf("  send DHCP Offer\n");
+
+        return 0;
+}
+
+static void test_addr_acq(sd_event *e)
+{
+        sd_dhcp_client *client;
+        int res, r;
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        r = sd_dhcp_client_new(&client);
+        assert_se(r >= 0);
+        assert_se(client);
+
+        r = sd_dhcp_client_attach_event(client, e, 0);
+        assert_se(r >= 0);
+
+        assert_se(sd_dhcp_client_set_index(client, 42) >= 0);
+        assert_se(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
+
+        assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e)
+                >= 0);
+
+        callback_recv = test_addr_acq_recv_discover;
+
+        res = sd_dhcp_client_start(client);
+        assert_se(res == 0 || res == -EINPROGRESS);
+
+        sd_event_loop(e);
+
+        sd_dhcp_client_set_callback(client, NULL, NULL);
+        sd_dhcp_client_stop(client);
+        sd_dhcp_client_free(client);
+
+        close(test_fd[0]);
+        close(test_fd[1]);
+
+        callback_recv = NULL;
+        xid = 0;
+}
+
+int main(int argc, char *argv[])
+{
+        sd_event *e;
+
+        assert_se(sd_event_new(&e) >= 0);
+
+        test_request_basic(e);
+        test_checksum();
+
+        test_discover_message(e);
+        test_addr_acq(e);
+
+        return 0;
+}
diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c
new file mode 100644
index 0000000..8659fd5
--- /dev/null
+++ b/src/libsystemd-network/test-dhcp-option.c
@@ -0,0 +1,378 @@
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include "util.h"
+#include "macro.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+
+struct option_desc {
+        uint8_t sname[64];
+        int snamelen;
+        uint8_t file[128];
+        int filelen;
+        uint8_t options[128];
+        int len;
+        bool success;
+        int filepos;
+        int snamepos;
+        int pos;
+};
+
+static bool verbose = false;
+
+static struct option_desc option_tests[] = {
+        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
+        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
+                          DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
+        { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
+        { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
+                          0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
+                          0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
+                          0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
+                          0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
+          40, true, },
+        { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
+                          42, 3, 0, 0, 0 }, 8, true, },
+        { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
+
+        { {}, 0,
+          { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
+          { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
+
+        { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
+          { 222, 3, 1, 2, 3 }, 5,
+          { DHCP_OPTION_OVERLOAD, 1,
+            DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
+};
+
+static const char *dhcp_type(int type)
+{
+        switch(type) {
+        case DHCP_DISCOVER:
+                return "DHCPDISCOVER";
+        case DHCP_OFFER:
+                return "DHCPOFFER";
+        case DHCP_REQUEST:
+                return "DHCPREQUEST";
+        case DHCP_DECLINE:
+                return "DHCPDECLINE";
+        case DHCP_ACK:
+                return "DHCPACK";
+        case DHCP_NAK:
+                return "DHCPNAK";
+        case DHCP_RELEASE:
+                return "DHCPRELEASE";
+        default:
+                return "unknown";
+        }
+}
+
+static void test_invalid_buffer_length(void)
+{
+        DHCPMessage message;
+
+        assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
+        assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage), NULL, NULL)
+               == -EINVAL);
+}
+
+static void test_cookie(void)
+{
+        _cleanup_free_ DHCPMessage *message;
+        size_t len = sizeof(DHCPMessage) + 4;
+        uint8_t *opt;
+
+        message = malloc0(len);
+
+        opt = (uint8_t *)(message + 1);
+        opt[0] = 0xff;
+
+        assert_se(dhcp_option_parse(message, len, NULL, NULL) == -EINVAL);
+
+        opt[0] = 99;
+        opt[1] = 130;
+        opt[2] = 83;
+        opt[3] = 99;
+
+        assert_se(dhcp_option_parse(message, len, NULL, NULL) == -ENOMSG);
+}
+
+static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
+                uint8_t *file, uint8_t filelen,
+                uint8_t *sname, uint8_t snamelen)
+{
+        DHCPMessage *message;
+        size_t len = sizeof(DHCPMessage) + 4 + optlen;
+        uint8_t *opt;
+
+        message = malloc0(len);
+        opt = (uint8_t *)(message + 1);
+
+        opt[0] = 99;
+        opt[1] = 130;
+        opt[2] = 83;
+        opt[3] = 99;
+
+        if (options && optlen)
+                memcpy(&opt[4], options, optlen);
+
+        if (file && filelen <= 128)
+                memcpy(&message->file, file, filelen);
+
+        if (sname && snamelen <= 64)
+                memcpy(&message->sname, sname, snamelen);
+
+        return message;
+}
+
+static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen)
+{
+        while (*descpos < *desclen) {
+                switch(descoption[*descpos]) {
+                case DHCP_OPTION_PAD:
+                        *descpos += 1;
+                        break;
+
+                case DHCP_OPTION_MESSAGE_TYPE:
+                case DHCP_OPTION_OVERLOAD:
+                        *descpos += 3;
+                        break;
+
+                default:
+                        return;
+                }
+        }
+}
+
+static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
+                           void *user_data)
+{
+        struct option_desc *desc = user_data;
+        uint8_t *descoption = NULL;
+        int *desclen = NULL, *descpos = NULL;
+        uint8_t optcode = 0;
+        uint8_t optlen = 0;
+        uint8_t i;
+
+        assert_se((!desc && !code && !len) || desc);
+
+        if (!desc)
+                return -EINVAL;
+
+        assert_se(code != DHCP_OPTION_PAD);
+        assert_se(code != DHCP_OPTION_END);
+        assert_se(code != DHCP_OPTION_MESSAGE_TYPE);
+        assert_se(code != DHCP_OPTION_OVERLOAD);
+
+        while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
+
+                if (desc->pos >= 0) {
+                        descoption = &desc->options[0];
+                        desclen = &desc->len;
+                        descpos = &desc->pos;
+                } else if (desc->filepos >= 0) {
+                        descoption = &desc->file[0];
+                        desclen = &desc->filelen;
+                        descpos = &desc->filepos;
+                } else if (desc->snamepos >= 0) {
+                        descoption = &desc->sname[0];
+                        desclen = &desc->snamelen;
+                        descpos = &desc->snamepos;
+                }
+
+                assert_se(descoption && desclen && descpos);
+
+                if (*desclen)
+                        test_ignore_opts(descoption, descpos, desclen);
+
+                if (*descpos < *desclen)
+                        break;
+
+                if (*descpos == *desclen)
+                        *descpos = -1;
+        }
+
+        assert_se(descpos);
+        assert_se(*descpos != -1);
+
+        optcode = descoption[*descpos];
+        optlen = descoption[*descpos + 1];
+
+        if (verbose)
+                printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
+                                len, optlen);
+
+        assert_se(code == optcode);
+        assert_se(len == optlen);
+
+        for (i = 0; i < len; i++) {
+
+                if (verbose)
+                        printf("0x%02x(0x%02x) ", option[i],
+                                        descoption[*descpos + 2 + i]);
+
+                assert_se(option[i] == descoption[*descpos + 2 + i]);
+        }
+
+        if (verbose)
+                printf("\n");
+
+        *descpos += optlen + 2;
+
+        test_ignore_opts(descoption, descpos, desclen);
+
+        if (desc->pos != -1 && desc->pos == desc->len)
+                desc->pos = -1;
+
+        if (desc->filepos != -1 && desc->filepos == desc->filelen)
+                desc->filepos = -1;
+
+        if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
+                desc->snamepos = -1;
+
+        return 0;
+}
+
+static void test_options(struct option_desc *desc)
+{
+        uint8_t *options = NULL;
+        uint8_t *file = NULL;
+        uint8_t *sname = NULL;
+        int optlen = 0;
+        int filelen = 0;
+        int snamelen = 0;
+        int buflen = 0;
+        _cleanup_free_ DHCPMessage *message;
+        int res;
+
+        if (desc) {
+                file = &desc->file[0];
+                filelen = desc->filelen;
+                if (!filelen)
+                        desc->filepos = -1;
+
+                sname = &desc->sname[0];
+                snamelen = desc->snamelen;
+                if (!snamelen)
+                        desc->snamepos = -1;
+
+                options = &desc->options[0];
+                optlen = desc->len;
+                desc->pos = 0;
+        }
+        message = create_message(options, optlen, file, filelen,
+                        sname, snamelen);
+
+        buflen = sizeof(DHCPMessage) + 4 + optlen;
+
+        if (!desc) {
+                assert_se((res = dhcp_option_parse(message, buflen,
+                                                test_options_cb,
+                                                NULL)) == -ENOMSG);
+        } else if (desc->success) {
+                assert_se((res = dhcp_option_parse(message, buflen,
+                                                test_options_cb,
+                                                desc)) >= 0);
+                assert_se(desc->pos == -1 && desc->filepos == -1 &&
+                                desc->snamepos == -1);
+        } else
+                assert_se((res = dhcp_option_parse(message, buflen,
+                                                test_options_cb,
+                                                desc)) < 0);
+
+        if (verbose)
+                printf("DHCP type %s\n", dhcp_type(res));
+}
+
+static uint8_t result[64] = {
+        'A', 'B', 'C', 'D',
+};
+
+static uint8_t options[64] = {
+        'A', 'B', 'C', 'D',
+        160, 2, 0x11, 0x12,
+        0,
+        31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+        0,
+        55, 3, 0x51, 0x52, 0x53,
+        17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+        255
+};
+
+static void test_option_set(void)
+{
+        size_t len, oldlen;
+        int pos, i;
+        uint8_t *opt;
+
+        assert_se(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
+
+        len = 0;
+        opt = &result[0];
+        assert_se(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
+        assert_se(opt == &result[0] && len == 0);
+
+        assert_se(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
+                                  0, NULL) == -ENOBUFS);
+        assert_se(opt == &result[0] && len == 0);
+
+        opt = &result[4];
+        len = 1;
+        assert_se(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
+                                    0, NULL) >= 0);
+        assert_se(opt == &result[5] && len == 0);
+
+        pos = 4;
+        len = 60;
+        while (pos < 64 && options[pos] != DHCP_OPTION_END) {
+                opt = &result[pos];
+                oldlen = len;
+
+                assert_se(dhcp_option_append(&opt, &len, options[pos],
+                                          options[pos + 1],
+                                          &options[pos + 2]) >= 0);
+
+                if (options[pos] == DHCP_OPTION_PAD) {
+                        assert_se(opt == &result[pos + 1]);
+                        assert_se(len == oldlen - 1);
+                        pos++;
+                } else {
+                        assert_se(opt == &result[pos + 2 + options[pos + 1]]);
+                        assert_se(len == oldlen - 2 - options[pos + 1]);
+                        pos += 2 + options[pos + 1];
+                }
+        }
+
+        for (i = 0; i < pos; i++) {
+                if (verbose)
+                        printf("%2d: 0x%02x(0x%02x)\n", i, result[i],
+                               options[i]);
+                assert_se(result[i] == options[i]);
+        }
+
+        if (verbose)
+                printf ("\n");
+}
+
+int main(int argc, char *argv[])
+{
+        unsigned int i;
+
+        test_invalid_buffer_length();
+        test_cookie();
+
+        test_options(NULL);
+
+        for (i = 0; i < ELEMENTSOF(option_tests); i++)
+                test_options(&option_tests[i]);
+
+        test_option_set();
+
+        return 0;
+}
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index b217123..f09fb75 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -28,6 +28,8 @@
 #include "bus-util.h"
 #include "net-util.h"
 
+#include "dhcp-lease-internal.h"
+
 int link_new(Manager *manager, struct udev_device *device, Link **ret) {
         _cleanup_link_free_ Link *link = NULL;
         const char *mac;
@@ -51,6 +53,11 @@ int link_new(Manager *manager, struct udev_device *device, Link **ret) {
         if (link->ifindex <= 0)
                 return -EINVAL;
 
+        r = asprintf(&link->state_file, "/run/systemd/network/links/%u",
+                     (unsigned) link->ifindex);
+        if (r < 0)
+                return r;
+
         mac = udev_device_get_sysattr_value(device, "address");
         if (mac) {
                 mac_addr = ether_aton(mac);
@@ -83,6 +90,7 @@ void link_free(Link *link) {
         hashmap_remove(link->manager->links, &link->ifindex);
 
         free(link->ifname);
+        free(link->state_file);
 
         free(link);
 }
@@ -139,6 +147,8 @@ static int link_enter_configured(Link *link) {
 
         link->state = LINK_STATE_CONFIGURED;
 
+        link_save(link);
+
         return 0;
 }
 
@@ -148,6 +158,8 @@ static void link_enter_failed(Link *link) {
         log_warning_link(link, "failed");
 
         link->state = LINK_STATE_FAILED;
+
+        link_save(link);
 }
 
 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
@@ -900,6 +912,8 @@ static int link_enter_enslave(Link *link) {
 
         link->state = LINK_STATE_ENSLAVING;
 
+        link_save(link);
+
         if (!link->network->bridge && !link->network->bond &&
             hashmap_isempty(link->network->vlans) &&
             hashmap_isempty(link->network->macvlans))
@@ -1066,3 +1080,61 @@ int link_update(Link *link, sd_rtnl_message *m) {
 
         return link_update_flags(link, flags);
 }
+
+int link_save(Link *link) {
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(link);
+        assert(link->state_file);
+
+        r = mkdir_safe_label("/run/systemd/network/links", 0755, 0, 0);
+        if (r < 0)
+                goto finish;
+
+        r = fopen_temporary(link->state_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
+
+        fchmod(fileno(f), 0644);
+
+        fprintf(f,
+                "# This is private data. Do not parse.\n"
+                "STATE=%s\n",
+                link_state_to_string(link->state));
+
+        if (link->dhcp_lease) {
+                const char *lease_file = "/run/systemd/network/leases/test.lease";
+
+                r = dhcp_lease_save(link->dhcp_lease, lease_file);
+                if (r < 0)
+                        goto finish;
+
+                fprintf(f, "DHCP_LEASE=%s\n", lease_file);
+        }
+
+        fflush(f);
+
+        if (ferror(f) || rename(temp_path, link->state_file) < 0) {
+                r = -errno;
+                unlink(link->state_file);
+                unlink(temp_path);
+        }
+
+finish:
+        if (r < 0)
+                log_error("Failed to save link data %s: %s", link->state_file, strerror(-r));
+
+        return r;
+}
+
+static const char* const link_state_table[_LINK_STATE_MAX] = {
+        [LINK_STATE_ENSLAVING] = "configuring",
+        [LINK_STATE_SETTING_ADDRESSES] = "configuring",
+        [LINK_STATE_SETTING_ROUTES] = "configuring",
+        [LINK_STATE_CONFIGURED] = "configured",
+        [LINK_STATE_FAILED] = "failed",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState);
diff --git a/src/network/networkd.c b/src/network/networkd.c
index 00e9a5f..a80740b 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -40,6 +40,11 @@ int main(int argc, char *argv[]) {
                 goto out;
         }
 
+        /* Always create the directories people can create inotify
+         * watches in. */
+        mkdir_label("/run/systemd/network/links", 0755);
+        mkdir_label("/run/systemd/network/leases", 0755);
+
         r = manager_new(&m);
         if (r < 0) {
                 log_error("Could not create manager: %s", strerror(-r));
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 85c3009..e9c0dd5 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -189,6 +189,7 @@ struct Link {
 
         uint64_t ifindex;
         char *ifname;
+        char *state_file;
         struct ether_addr mac;
 
         unsigned flags;
@@ -354,6 +355,11 @@ int link_configure(Link *link);
 
 int link_update(Link *link, sd_rtnl_message *message);
 
+int link_save(Link *link);
+
+const char* link_state_to_string(LinkState s) _const_;
+LinkState link_state_from_string(const char *s) _pure_;
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free);
 #define _cleanup_link_free_ _cleanup_(link_freep)
 
diff --git a/src/network/sd-network.c b/src/network/sd-network.c
new file mode 100644
index 0000000..7469873
--- /dev/null
+++ b/src/network/sd-network.c
@@ -0,0 +1,237 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+  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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
+
+#include "util.h"
+#include "macro.h"
+#include "strv.h"
+#include "fileio.h"
+#include "sd-network.h"
+#include "dhcp-lease-internal.h"
+
+_public_ int sd_network_get_link_state(unsigned index, char **state) {
+        char *p, *s = NULL;
+        int r;
+
+        assert_return(index, -EINVAL);
+        assert_return(state, -EINVAL);
+
+        if (asprintf(&p, "/run/systemd/network/links/%u", index) < 0)
+                return -ENOMEM;
+
+        r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
+        free(p);
+
+        if (r == -ENOENT) {
+                free(s);
+                s = strdup("unknown");
+                if (!s)
+                        return -ENOMEM;
+
+                *state = s;
+                return 0;
+        } else if (r < 0) {
+                free(s);
+                return r;
+        } else if (!s)
+                return -EIO;
+
+        *state = s;
+        return 0;
+}
+
+_public_ int sd_network_get_dhcp_lease(unsigned index, sd_dhcp_lease **ret) {
+        sd_dhcp_lease *lease;
+        char *p, *s = NULL;
+        int r;
+
+        assert_return(index, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (asprintf(&p, "/run/systemd/network/links/%u", index) < 0)
+                return -ENOMEM;
+
+        r = parse_env_file(p, NEWLINE, "DHCP_LEASE", &s, NULL);
+        free(p);
+
+        if (r < 0) {
+                free(s);
+                return r;
+        } else if (!s)
+                return -EIO;
+
+        r = dhcp_lease_load(s, &lease);
+        if (r < 0)
+                return r;
+
+        *ret = lease;
+
+        return 0;
+}
+
+_public_ int sd_network_get_ifindices(unsigned **indices) {
+        _cleanup_closedir_ DIR *d;
+        int r = 0;
+        unsigned n = 0;
+        _cleanup_free_ uid_t *l = NULL;
+
+        d = opendir("/run/systemd/network/links/");
+        if (!d)
+                return -errno;
+
+        for (;;) {
+                struct dirent *de;
+                int k;
+                unsigned index;
+
+                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;
+
+                k = safe_atou(de->d_name, &index);
+                if (k < 0)
+                        continue;
+
+                if (indices) {
+                        if ((unsigned) r >= n) {
+                                unsigned *t;
+
+                                n = MAX(16, 2*r);
+                                t = realloc(l, sizeof(unsigned) * n);
+                                if (!t)
+                                        return -ENOMEM;
+
+                                l = t;
+                        }
+
+                        assert((unsigned) r < n);
+                        l[r++] = index;
+                } else
+                        r++;
+        }
+
+        if (indices) {
+                *indices = l;
+                l = NULL;
+        }
+
+        return r;
+}
+
+static inline int MONITOR_TO_FD(sd_network_monitor *m) {
+        return (int) (unsigned long) m - 1;
+}
+
+static inline sd_network_monitor* FD_TO_MONITOR(int fd) {
+        return (sd_network_monitor*) (unsigned long) (fd + 1);
+}
+
+_public_ int sd_network_monitor_new(const char *category, sd_network_monitor **m) {
+        int fd, k;
+        bool good = false;
+
+        assert_return(m, -EINVAL);
+
+        fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        if (!category || streq(category, "netif")) {
+                k = inotify_add_watch(fd, "/run/systemd/network/links/", IN_MOVED_TO|IN_DELETE);
+                if (k < 0) {
+                        close_nointr_nofail(fd);
+                        return -errno;
+                }
+
+                good = true;
+        }
+
+        if (!good) {
+                close_nointr(fd);
+                return -EINVAL;
+        }
+
+        *m = FD_TO_MONITOR(fd);
+        return 0;
+}
+
+_public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
+        int fd;
+
+        assert_return(m, NULL);
+
+        fd = MONITOR_TO_FD(m);
+        close_nointr(fd);
+
+        return NULL;
+}
+
+_public_ int sd_network_monitor_flush(sd_network_monitor *m) {
+
+        assert_return(m, -EINVAL);
+
+        return flush_fd(MONITOR_TO_FD(m));
+}
+
+_public_ int sd_network_monitor_get_fd(sd_network_monitor *m) {
+
+        assert_return(m, -EINVAL);
+
+        return MONITOR_TO_FD(m);
+}
+
+_public_ int sd_network_monitor_get_events(sd_network_monitor *m) {
+
+        assert_return(m, -EINVAL);
+
+        /* For now we will only return POLLIN here, since we don't
+         * need anything else ever for inotify.  However, let's have
+         * this API to keep our options open should we later on need
+         * it. */
+        return POLLIN;
+}
+
+_public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) {
+
+        assert_return(m, -EINVAL);
+        assert_return(timeout_usec, -EINVAL);
+
+        /* For now we will only return (uint64_t) -1, since we don't
+         * need any timeout. However, let's have this API to keep our
+         * options open should we later on need it. */
+        *timeout_usec = (uint64_t) -1;
+        return 0;
+}
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 87f5adb..a60bb58 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -26,6 +26,7 @@
 #include <net/ethernet.h>
 
 #include "sd-event.h"
+#include "sd-dhcp-lease.h"
 
 enum {
         DHCP_EVENT_STOP                         = 0,
@@ -36,7 +37,6 @@ enum {
 };
 
 typedef struct sd_dhcp_client sd_dhcp_client;
-typedef struct sd_dhcp_lease sd_dhcp_lease;
 
 typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event,
                                     void *userdata);
@@ -52,16 +52,6 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
                            const struct ether_addr *addr);
 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret);
 
-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_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_dns(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size);
-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_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
 void sd_dhcp_client_free(sd_dhcp_client *client);
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
new file mode 100644
index 0000000..af687a6
--- /dev/null
+++ b/src/systemd/sd-dhcp-lease.h
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddhcpleasehfoo
+#define foosddhcpleasehfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+  Copyright (C) 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+typedef struct sd_dhcp_lease sd_dhcp_lease;
+
+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_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_dns(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size);
+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);
+
+#endif
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
new file mode 100644
index 0000000..c0b093d
--- /dev/null
+++ b/src/systemd/sd-network.h
@@ -0,0 +1,87 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdloginhfoo
+#define foosdloginhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+  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 <sys/types.h>
+#include <inttypes.h>
+
+#include "sd-dhcp-lease.h"
+
+#include "_sd-common.h"
+
+/*
+ * A few points:
+ *
+ * Instead of returning an empty string array or empty integer array, we
+ * may return NULL.
+ *
+ * Free the data the library returns with libc free(). String arrays
+ * are NULL terminated and you need to free the array itself in
+ * addition to the strings contained.
+ *
+ * We return error codes as negative errno, kernel-style. 0 or
+ * positive on success.
+ *
+ * These functions access data in /run. This is a virtual file systems,
+ * hence the accesses is relatively cheap.
+ *
+ * See sd-network(3) for more information.
+ */
+
+_SD_BEGIN_DECLARATIONS;
+
+/* Get state from ifindex. Possible states: unknown, unmanaged, failed, configuring, configured */
+int sd_network_get_link_state(unsigned index, char**state);
+
+/* Get DHCPv4 lease from ifindex. */
+int sd_network_get_dhcp_lease(unsigned index, sd_dhcp_lease **ret);
+
+/* Get all network interfaces indices, store in *indices. Returns the
+ * number of indices. If indices is NULL only returns the number of indices. */
+int sd_network_get_ifindices(unsigned **indices);
+
+/* Monitor object */
+typedef struct sd_network_monitor sd_network_monitor;
+
+/* Create a new monitor. Category must be NULL. */
+int sd_network_monitor_new(const char *category, sd_network_monitor** ret);
+
+/* Destroys the passed monitor. Returns NULL. */
+sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m);
+
+/* Flushes the monitor */
+int sd_network_monitor_flush(sd_network_monitor *m);
+
+/* Get FD from monitor */
+int sd_network_monitor_get_fd(sd_network_monitor *m);
+
+/* Get poll() mask to monitor */
+int sd_network_monitor_get_events(sd_network_monitor *m);
+
+/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */
+int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec);
+
+_SD_END_DECLARATIONS;
+
+#endif



More information about the systemd-commits mailing list