[systemd-devel] [PATCH] dhcp: Move src/dhcp to src/libsystemd-dhcp

Patrik Flykt patrik.flykt at linux.intel.com
Thu Dec 12 00:29:18 PST 2013


---
 Makefile.am                                       |   42 +-
 src/dhcp/Makefile                                 |    1 -
 src/dhcp/client.c                                 | 1002 ---------------------
 src/dhcp/internal.h                               |   41 -
 src/dhcp/network.c                                |   65 --
 src/dhcp/option.c                                 |  184 ----
 src/dhcp/protocol.h                               |  119 ---
 src/dhcp/test-dhcp-client.c                       |  224 -----
 src/dhcp/test-dhcp-option.c                       |  377 --------
 src/libsystemd-dhcp/Makefile                      |    1 +
 src/libsystemd-dhcp/client.c                      | 1002 +++++++++++++++++++++
 src/libsystemd-dhcp/internal.h                    |   41 +
 src/libsystemd-dhcp/network.c                     |   65 ++
 src/libsystemd-dhcp/option.c                      |  184 ++++
 src/libsystemd-dhcp/protocol.h                    |  119 +++
 src/libsystemd-dhcp/test-libsystemd-dhcp-client.c |  224 +++++
 src/libsystemd-dhcp/test-libsystemd-dhcp-option.c |  377 ++++++++
 17 files changed, 2034 insertions(+), 2034 deletions(-)
 delete mode 120000 src/dhcp/Makefile
 delete mode 100644 src/dhcp/client.c
 delete mode 100644 src/dhcp/internal.h
 delete mode 100644 src/dhcp/network.c
 delete mode 100644 src/dhcp/option.c
 delete mode 100644 src/dhcp/protocol.h
 delete mode 100644 src/dhcp/test-dhcp-client.c
 delete mode 100644 src/dhcp/test-dhcp-option.c
 create mode 120000 src/libsystemd-dhcp/Makefile
 create mode 100644 src/libsystemd-dhcp/client.c
 create mode 100644 src/libsystemd-dhcp/internal.h
 create mode 100644 src/libsystemd-dhcp/network.c
 create mode 100644 src/libsystemd-dhcp/option.c
 create mode 100644 src/libsystemd-dhcp/protocol.h
 create mode 100644 src/libsystemd-dhcp/test-libsystemd-dhcp-client.c
 create mode 100644 src/libsystemd-dhcp/test-libsystemd-dhcp-option.c

diff --git a/Makefile.am b/Makefile.am
index 9b3b48a..bfa3df7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3849,11 +3849,11 @@ endif
 # ------------------------------------------------------------------------------
 libsystemd_dhcp_la_SOURCES = \
 	src/systemd/sd-dhcp-client.h \
-	src/dhcp/protocol.h \
-	src/dhcp/internal.h \
-	src/dhcp/network.c \
-	src/dhcp/option.c \
-	src/dhcp/client.c
+	src/libsystemd-dhcp/protocol.h \
+	src/libsystemd-dhcp/internal.h \
+	src/libsystemd-dhcp/network.c \
+	src/libsystemd-dhcp/option.c \
+	src/libsystemd-dhcp/client.c
 
 noinst_LTLIBRARIES += \
 	libsystemd-dhcp.la
@@ -3868,31 +3868,31 @@ libsystemd_dhcp_la_LIBADD = \
 	libsystemd-shared.la \
 	libsystemd-bus.la
 
-test_dhcp_option_SOURCES = \
-	src/dhcp/protocol.h \
-	src/dhcp/internal.h \
-	src/dhcp/option.c \
-	src/dhcp/test-dhcp-option.c
+test_libsystemd_dhcp_option_SOURCES = \
+	src/libsystemd-dhcp/protocol.h \
+	src/libsystemd-dhcp/internal.h \
+	src/libsystemd-dhcp/option.c \
+	src/libsystemd-dhcp/test-libsystemd-dhcp-option.c
 
-test_dhcp_option_LDADD = \
+test_libsystemd_dhcp_option_LDADD = \
 	libsystemd-shared.la
 
-test_dhcp_client_SOURCES = \
-	src/dhcp/protocol.h \
+test_libsystemd_dhcp_client_SOURCES = \
+	src/libsystemd-dhcp/protocol.h \
 	src/systemd/sd-dhcp-client.h \
-	src/dhcp/internal.h \
-	src/dhcp/option.h \
-	src/dhcp/option.c \
-	src/dhcp/client.c \
-	src/dhcp/test-dhcp-client.c
+	src/libsystemd-dhcp/internal.h \
+	src/libsystemd-dhcp/option.h \
+	src/libsystemd-dhcp/option.c \
+	src/libsystemd-dhcp/client.c \
+	src/libsystemd-dhcp/test-libsystemd-dhcp-client.c
 
-test_dhcp_client_LDADD = \
+test_libsystemd_dhcp_client_LDADD = \
 	libsystemd-shared.la \
 	libsystemd-bus.la
 
 tests += \
-	test-dhcp-option \
-	test-dhcp-client
+	test-libsystemd-dhcp-option \
+	test-libsystemd-dhcp-client
 
 # ------------------------------------------------------------------------------
 if ENABLE_MACHINED
diff --git a/src/dhcp/Makefile b/src/dhcp/Makefile
deleted file mode 120000
index d0b0e8e..0000000
--- a/src/dhcp/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile
\ No newline at end of file
diff --git a/src/dhcp/client.c b/src/dhcp/client.c
deleted file mode 100644
index 2a479e4..0000000
--- a/src/dhcp/client.c
+++ /dev/null
@@ -1,1002 +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 "util.h"
-#include "list.h"
-
-#include "protocol.h"
-#include "internal.h"
-#include "sd-dhcp-client.h"
-
-#define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
-
-struct DHCPLease {
-        uint32_t t1;
-        uint32_t t2;
-        uint32_t lifetime;
-        uint32_t address;
-        uint32_t server_address;
-        uint32_t subnet_mask;
-        uint32_t router;
-};
-
-typedef struct DHCPLease DHCPLease;
-
-struct sd_dhcp_client {
-        DHCPState state;
-        sd_event *event;
-        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_size;
-        uint32_t last_addr;
-        struct ether_addr mac_addr;
-        uint32_t xid;
-        usec_t start_time;
-        unsigned int attempt;
-        usec_t request_sent;
-        sd_event_source *timeout_t1;
-        sd_event_source *timeout_t2;
-        sd_event_source *timeout_expire;
-        sd_dhcp_client_cb_t cb;
-        void *userdata;
-        DHCPLease *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,
-};
-
-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_size,
-                            client->req_opts_size + 1))
-                return -ENOMEM;
-
-        client->req_opts[client->req_opts_size - 1] = 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_address(sd_dhcp_client *client, struct in_addr *addr)
-{
-        assert_return(client, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        switch (client->state) {
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_SELECTING:
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_REQUESTING:
-                return -EADDRNOTAVAIL;
-
-        case DHCP_STATE_BOUND:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-                addr->s_addr = client->lease->address;
-
-                break;
-        }
-
-        return 0;
-}
-
-int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr)
-{
-        assert_return(client, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        switch (client->state) {
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_SELECTING:
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_REQUESTING:
-                return -EADDRNOTAVAIL;
-
-        case DHCP_STATE_BOUND:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-                addr->s_addr = client->lease->subnet_mask;
-
-                break;
-        }
-
-        return 0;
-}
-
-int sd_dhcp_client_prefixlen(const struct in_addr *addr)
-{
-        int len = 0;
-        uint32_t mask;
-
-        assert_return(addr, -EADDRNOTAVAIL);
-
-        mask = be32toh(addr->s_addr);
-        while (mask) {
-                len++;
-                mask = mask << 1;
-        }
-
-        return len;
-}
-
-int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr)
-{
-        assert_return(client, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        switch (client->state) {
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_SELECTING:
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_REQUESTING:
-                return -EADDRNOTAVAIL;
-
-        case DHCP_STATE_BOUND:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-                addr->s_addr = client->lease->router;
-
-                break;
-        }
-
-        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);
-        assert_return(client->state != DHCP_STATE_INIT &&
-                      client->state != DHCP_STATE_INIT_REBOOT, -EALREADY);
-
-        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);
-
-        switch (client->state) {
-
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_SELECTING:
-        case DHCP_STATE_REQUESTING:
-        case DHCP_STATE_BOUND:
-
-                client->start_time = 0;
-                client->state = DHCP_STATE_INIT;
-                break;
-
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-
-                break;
-        }
-
-        if (client->lease) {
-                free(client->lease);
-                client->lease = NULL;
-        }
-
-        return 0;
-}
-
-static int client_packet_init(sd_dhcp_client *client, uint8_t type,
-                              DHCPMessage *message, uint16_t secs,
-                              uint8_t **opt, size_t *optlen)
-{
-        int err;
-        be16_t max_size;
-
-        *opt = (uint8_t *)(message + 1);
-
-        if (*optlen < 4)
-                return -ENOBUFS;
-        *optlen -= 4;
-
-        message->op = BOOTREQUEST;
-        message->htype = 1;
-        message->hlen = ETHER_ADDR_LEN;
-        message->xid = htobe32(client->xid);
-
-        /* 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);
-        (*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;
-
-        /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
-           Identifier option is not set */
-        err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
-                                 ETH_ALEN, &client->mac_addr);
-        if (err < 0)
-                return err;
-
-        if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
-                err = dhcp_option_append(opt, optlen,
-                                         DHCP_OPTION_PARAMETER_REQUEST_LIST,
-                                         client->req_opts_size,
-                                         client->req_opts);
-                if (err < 0)
-                        return err;
-
-                /* 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_CLIENT_MIN_OPTIONS_SIZE);
-                err = dhcp_option_append(opt, optlen,
-                                         DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
-                                         2, &max_size);
-                if (err < 0)
-                        return err;
-        }
-
-        return 0;
-}
-
-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];
-        }
-
-        return ~((sum & 0xffff) + (sum >> 16));
-}
-
-static void client_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 = client_checksum(&packet->ip.ttl, len - 8);
-
-        packet->ip.ttl = IPDEFTTL;
-        packet->ip.check = 0;
-        packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE);
-}
-
-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_CLIENT_MIN_OPTIONS_SIZE;
-        len = sizeof(DHCPPacket) + optlen;
-
-        discover = malloc0(len);
-
-        if (!discover)
-                return -ENOMEM;
-
-        err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
-                                 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;
-
-        client_append_ip_headers(discover, len);
-
-        err = dhcp_network_send_raw_socket(client->fd, &client->link,
-                                           discover, len);
-
-        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_CLIENT_MIN_OPTIONS_SIZE;
-        len = DHCP_MESSAGE_SIZE + optlen;
-
-        request = malloc0(len);
-        if (!request)
-                return -ENOMEM;
-
-        err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, 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;
-
-        client_append_ip_headers(request, len);
-
-        err = dhcp_network_send_raw_socket(client->fd, &client->link,
-                                           request, len);
-
-        return err;
-}
-
-static int client_timeout_resend(sd_event_source *s, uint64_t usec,
-                                 void *userdata)
-{
-        sd_dhcp_client *client = userdata;
-        usec_t next_timeout;
-        uint16_t secs;
-        int err = 0;
-
-        secs = (usec - client->start_time) / USEC_PER_SEC;
-
-        if (client->attempt < 64)
-                client->attempt *= 2;
-
-        next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC +
-                (random_u() & 0x1fffff);
-
-        err = sd_event_add_monotonic(client->event, next_timeout,
-                                     10 * USEC_PER_MSEC,
-                                     client_timeout_resend, client,
-                                     &client->timeout_resend);
-        if (err < 0)
-                goto error;
-
-        switch (client->state) {
-        case DHCP_STATE_INIT:
-                err = client_send_discover(client, secs);
-                if (err >= 0) {
-                        client->state = DHCP_STATE_SELECTING;
-                        client->attempt = 1;
-                } else {
-                        if (client->attempt >= 64)
-                                goto error;
-                }
-
-                break;
-
-        case DHCP_STATE_SELECTING:
-                err = client_send_discover(client, secs);
-                if (err < 0 && client->attempt >= 64)
-                        goto error;
-
-                break;
-
-        case DHCP_STATE_REQUESTING:
-                err = client_send_request(client, secs);
-                if (err < 0 && client->attempt >= 64)
-                         goto error;
-
-                client->request_sent = usec;
-
-                break;
-
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_BOUND:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-
-                break;
-        }
-
-        return 0;
-
-error:
-        client_stop(client, err);
-
-        /* Errors were dealt with when stopping the client, don't spill
-           errors into the event loop handler */
-        return 0;
-}
-
-static int client_timeout_expire(sd_event_source *s, uint64_t usec,
-                                 void *userdata)
-{
-        sd_dhcp_client *client = userdata;
-
-        client_stop(client, DHCP_EVENT_EXPIRED);
-
-        return 0;
-}
-
-static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
-{
-        return 0;
-}
-
-static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
-{
-        return 0;
-}
-
-static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
-                              void *user_data)
-{
-        DHCPLease *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_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;
-}
-
-static int client_verify_headers(sd_dhcp_client *client, DHCPPacket *message,
-                                 size_t len)
-{
-        size_t hdrlen;
-
-        if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
-                return -EINVAL;
-
-        hdrlen = message->ip.ihl * 4;
-        if (hdrlen < 20 || hdrlen > len || client_checksum(&message->ip,
-                                                           hdrlen))
-                return -EINVAL;
-
-        message->ip.check = message->udp.len;
-        message->ip.ttl = 0;
-
-        if (hdrlen + be16toh(message->udp.len) > len ||
-            client_checksum(&message->ip.ttl, be16toh(message->udp.len) + 12))
-                return -EINVAL;
-
-        if (be16toh(message->udp.source) != DHCP_PORT_SERVER ||
-            be16toh(message->udp.dest) != DHCP_PORT_CLIENT)
-                return -EINVAL;
-
-        if (message->dhcp.op != BOOTREPLY)
-                return -EINVAL;
-
-        if (be32toh(message->dhcp.xid) != client->xid)
-                return -EINVAL;
-
-        if (memcmp(&message->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
-                    ETHER_ADDR_LEN))
-                return -EINVAL;
-
-        return 0;
-}
-
-static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
-                                size_t len)
-{
-        int err;
-        DHCPLease *lease;
-
-        err = client_verify_headers(client, offer, len);
-        if (err < 0)
-                return err;
-
-        lease = new0(DHCPLease, 1);
-        if (!lease)
-                return -ENOMEM;
-
-        len = len - DHCP_IP_UDP_SIZE;
-        if (dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
-                              lease) != DHCP_OFFER)
-                goto error;
-
-        lease->address = offer->dhcp.yiaddr;
-
-        if (lease->address == INADDR_ANY ||
-            lease->server_address == INADDR_ANY ||
-            lease->subnet_mask == INADDR_ANY ||
-            lease->lifetime == 0)
-                goto error;
-
-        client->lease = lease;
-
-        return 0;
-
-error:
-        free(lease);
-
-        return -ENOMSG;
-}
-
-static int client_receive_ack(sd_dhcp_client *client, DHCPPacket *offer,
-                              size_t len)
-{
-        int r;
-        DHCPLease *lease;
-
-        r = client_verify_headers(client, offer, len);
-        if (r < 0)
-                return r;
-
-        lease = new0(DHCPLease, 1);
-        if (!lease)
-                return -ENOBUFS;
-
-        len = len - DHCP_IP_UDP_SIZE;
-        r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer, lease);
-
-        if (r != DHCP_ACK)
-                goto error;
-
-        lease->address = offer->dhcp.yiaddr;
-
-        if (lease->address == INADDR_ANY ||
-            lease->server_address == INADDR_ANY ||
-            lease->subnet_mask == INADDR_ANY || lease->lifetime == 0) {
-                r = -ENOMSG;
-                goto error;
-        }
-
-        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;
-                }
-
-                free(client->lease);
-        }
-
-        client->lease = lease;
-
-        return r;
-
-error:
-        free(lease);
-
-        return r;
-}
-
-static uint64_t client_compute_timeout(uint64_t request_sent,
-                                       uint32_t lifetime)
-{
-        return request_sent + (lifetime - 3) * USEC_PER_SEC +
-                + (random_u() & 0x1fffff);
-}
-
-static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec)
-{
-        int err;
-        uint64_t next_timeout;
-
-        if (client->lease->lifetime < 10)
-                return -EINVAL;
-
-        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;
-
-        err = sd_event_add_monotonic(client->event, next_timeout,
-                                     10 * USEC_PER_MSEC,
-                                     client_timeout_t1, client,
-                                     &client->timeout_t1);
-        if (err < 0)
-                return err;
-
-        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;
-
-        err = sd_event_add_monotonic(client->event, next_timeout,
-                                     10 * USEC_PER_MSEC,
-                                     client_timeout_t2, client,
-                                     &client->timeout_t2);
-        if (err < 0)
-                return err;
-
-        next_timeout = client_compute_timeout(client->request_sent,
-                                              client->lease->lifetime);
-        if (next_timeout < usec)
-                return -EINVAL;
-
-        err = sd_event_add_monotonic(client->event, next_timeout,
-                                     10 * USEC_PER_MSEC,
-                                     client_timeout_expire, client,
-                                     &client->timeout_expire);
-        if (err < 0)
-                return err;
-
-        return 0;
-}
-
-static int client_receive_raw_message(sd_event_source *s, int fd,
-                                      uint32_t revents, void *userdata)
-{
-        sd_dhcp_client *client = userdata;
-        uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE];
-        int buflen = sizeof(buf);
-        int len, r = 0;
-        DHCPPacket *message;
-        usec_t time_now;
-
-        len = read(fd, &buf, buflen);
-        if (len < 0)
-                return 0;
-
-        r = sd_event_get_now_monotonic(client->event, &time_now);
-        if (r < 0)
-                goto error;
-
-        message = (DHCPPacket *)&buf;
-
-        switch (client->state) {
-        case DHCP_STATE_SELECTING:
-
-                if (client_receive_offer(client, message, len) >= 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, time_now, 0,
-                                                   client_timeout_resend,
-                                                   client,
-                                                   &client->timeout_resend);
-                        if (r < 0)
-                                goto error;
-                }
-
-                break;
-
-        case DHCP_STATE_REQUESTING:
-
-                r = client_receive_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);
-
-                        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;
-
-                        client_notify(client, DHCP_EVENT_IP_ACQUIRE);
-
-                        close(client->fd);
-                        client->fd = -1;
-                        client->receive_message =
-                                sd_event_source_unref(client->receive_message);
-                }
-                break;
-
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_BOUND:
-        case DHCP_STATE_RENEWING:
-        case DHCP_STATE_REBINDING:
-
-                break;
-        }
-
-error:
-        if (r < 0)
-                return client_stop(client, r);
-
-        return 0;
-}
-
-int sd_dhcp_client_start(sd_dhcp_client *client)
-{
-        int err;
-
-        assert_return(client, -EINVAL);
-        assert_return(client->index >= 0, -EINVAL);
-        assert_return(client->state == DHCP_STATE_INIT ||
-                      client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
-
-        client->xid = random_u();
-
-        client->fd = dhcp_network_bind_raw_socket(client->index,
-                                                  &client->link);
-
-        if (client->fd < 0) {
-                err = client->fd;
-                goto error;
-        }
-
-        err = sd_event_add_io(client->event, client->fd, EPOLLIN,
-                              client_receive_raw_message, client,
-                              &client->receive_message);
-        if (err < 0)
-                goto error;
-
-        client->start_time = now(CLOCK_MONOTONIC);
-        err = sd_event_add_monotonic(client->event, client->start_time, 0,
-                                     client_timeout_resend, client,
-                                     &client->timeout_resend);
-        if (err < 0)
-                goto error;
-
-        return 0;
-
-error:
-        client_stop(client, err);
-
-        return err;
-}
-
-int sd_dhcp_client_stop(sd_dhcp_client *client)
-{
-        return client_stop(client, DHCP_EVENT_STOP);
-}
-
-sd_dhcp_client *sd_dhcp_client_free(sd_dhcp_client *client)
-{
-        assert_return(client, NULL);
-
-        sd_dhcp_client_stop(client);
-
-        sd_event_unref(client->event);
-        free(client->req_opts);
-        free(client);
-
-        return NULL;
-}
-
-sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
-{
-        sd_dhcp_client *client;
-
-        assert_return(event, NULL);
-
-        client = new0(sd_dhcp_client, 1);
-        if (!client)
-                return NULL;
-
-        client->event = sd_event_ref(event);
-        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) {
-                free(client);
-                return NULL;
-        }
-
-        return client;
-}
diff --git a/src/dhcp/internal.h b/src/dhcp/internal.h
deleted file mode 100644
index 85bb908..0000000
--- a/src/dhcp/internal.h
+++ /dev/null
@@ -1,41 +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 <stdint.h>
-#include <linux/if_packet.h>
-
-#include "socket-util.h"
-
-#include "protocol.h"
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link);
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
-                                 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);
diff --git a/src/dhcp/network.c b/src/dhcp/network.c
deleted file mode 100644
index 3a16947..0000000
--- a/src/dhcp/network.c
+++ /dev/null
@@ -1,65 +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 "internal.h"
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
- {
-        int s;
-
-        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 (bind(s, &link->sa, sizeof(link->ll)) < 0) {
-                close(s);
-                return -errno;
-        }
-
-        return s;
-}
-
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
-                                 const void *packet, size_t len)
-{
-        int err = 0;
-
-        if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 0)
-                err = -errno;
-
-        return err;
-}
diff --git a/src/dhcp/option.c b/src/dhcp/option.c
deleted file mode 100644
index 235b387..0000000
--- a/src/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 "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/dhcp/protocol.h b/src/dhcp/protocol.h
deleted file mode 100644
index 1e59965..0000000
--- a/src/dhcp/protocol.h
+++ /dev/null
@@ -1,119 +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;
-        uint32_t ciaddr;
-        uint32_t yiaddr;
-        uint32_t siaddr;
-        uint32_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))
-
-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_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/dhcp/test-dhcp-client.c b/src/dhcp/test-dhcp-client.c
deleted file mode 100644
index ee0848d..0000000
--- a/src/dhcp/test-dhcp-client.c
+++ /dev/null
@@ -1,224 +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 "protocol.h"
-#include "internal.h"
-#include "sd-dhcp-client.h"
-
-static struct ether_addr mac_addr = {
-        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
-};
-
-static int test_fd[2];
-
-static void test_request_basic(sd_event *e)
-{
-        sd_dhcp_client *client;
-
-        client = sd_dhcp_client_new(e);
-
-        assert(client);
-
-        assert(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
-        assert(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
-        assert(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
-
-        assert(sd_dhcp_client_set_index(client, 15) == 0);
-        assert(sd_dhcp_client_set_index(client, -42) == -EINVAL);
-        assert(sd_dhcp_client_set_index(client, -1) == 0);
-
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_SUBNET_MASK) == -EEXIST);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_ROUTER) == -EEXIST);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_HOST_NAME) == -EEXIST);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_DOMAIN_NAME_SERVER)
-                        == -EEXIST);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_NTP_SERVER) == -EEXIST);
-
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_PAD) == -EINVAL);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_END) == -EINVAL);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_OVERLOAD) == -EINVAL);
-        assert(sd_dhcp_client_set_request_option(client,
-                                        DHCP_OPTION_PARAMETER_REQUEST_LIST)
-                        == -EINVAL);
-
-        assert(sd_dhcp_client_set_request_option(client, 33) == 0);
-        assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
-        assert(sd_dhcp_client_set_request_option(client, 44) == 0);
-}
-
-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];
-        }
-
-        return ~((sum & 0xffff) + (sum >> 16));
-}
-
-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
-        };
-
-        uint8_t check[2] = {
-                0x78, 0xae
-        };
-
-        uint16_t *val = (uint16_t *)check;
-
-        assert(client_checksum(&buf, 20) == *val);
-}
-
-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;
-        int res;
-
-        assert(s >= 0);
-        assert(packet);
-
-        size = sizeof(DHCPPacket) + 4;
-        assert(len > size);
-
-        discover = memdup(packet, len);
-
-        assert(memcmp(discover->dhcp.chaddr,
-                      &mac_addr.ether_addr_octet, 6) == 0);
-        assert(discover->ip.ttl == IPDEFTTL);
-        assert(discover->ip.protocol == IPPROTO_UDP);
-        assert(discover->ip.saddr == INADDR_ANY);
-        assert(discover->ip.daddr == INADDR_BROADCAST);
-        assert(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
-        assert(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(udp_check == 0xffff);
-
-        discover->ip.ttl = IPDEFTTL;
-        discover->ip.check = ip_check;
-
-        ip_check = ~client_checksum(&discover->ip, sizeof(discover->ip));
-        assert(ip_check == 0xffff);
-
-        size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
-
-        res = dhcp_option_parse(&discover->dhcp, size, check_options, NULL);
-        if (res < 0)
-                return res;
-
-        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];
-}
-
-static void test_discover_message(sd_event *e)
-{
-        sd_dhcp_client *client;
-        int res;
-
-        client = sd_dhcp_client_new(e);
-        assert(client);
-
-        assert(sd_dhcp_client_set_index(client, 42) >= 0);
-        assert(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
-
-        assert(sd_dhcp_client_set_request_option(client, 248) >= 0);
-
-        res = sd_dhcp_client_start(client);
-
-        assert(res == 0 || res == -EINPROGRESS);
-
-        close(test_fd[0]);
-        close(test_fd[1]);
-}
-
-int main(int argc, char *argv[])
-{
-        sd_event *e;
-
-        assert(sd_event_new(&e) >= 0);
-
-        test_request_basic(e);
-        test_checksum();
-
-        test_discover_message(e);
-        sd_event_run(e, (uint64_t) -1);
-
-        return 0;
-}
diff --git a/src/dhcp/test-dhcp-option.c b/src/dhcp/test-dhcp-option.c
deleted file mode 100644
index b85da79..0000000
--- a/src/dhcp/test-dhcp-option.c
+++ /dev/null
@@ -1,377 +0,0 @@
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <assert.h>
-
-#include "util.h"
-#include "macro.h"
-
-#include "protocol.h"
-#include "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(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
-        assert(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(dhcp_option_parse(message, len, NULL, NULL) == -EINVAL);
-
-        opt[0] = 99;
-        opt[1] = 130;
-        opt[2] = 83;
-        opt[3] = 99;
-
-        assert(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((!desc && !code && !len) || desc);
-
-        if (!desc)
-                return -EINVAL;
-
-        assert(code != DHCP_OPTION_PAD);
-        assert(code != DHCP_OPTION_END);
-        assert(code != DHCP_OPTION_MESSAGE_TYPE);
-        assert(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(descoption && desclen && descpos);
-
-                if (*desclen)
-                        test_ignore_opts(descoption, descpos, desclen);
-
-                if (*descpos < *desclen)
-                        break;
-
-                if (*descpos == *desclen)
-                        *descpos = -1;
-        }
-
-        assert(*descpos != -1);
-
-        optcode = descoption[*descpos];
-        optlen = descoption[*descpos + 1];
-
-        if (verbose)
-                printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
-                                len, optlen);
-
-        assert(code == optcode);
-        assert(len == optlen);
-
-        for (i = 0; i < len; i++) {
-
-                if (verbose)
-                        printf("0x%02x(0x%02x) ", option[i],
-                                        descoption[*descpos + 2 + i]);
-
-                assert(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((res = dhcp_option_parse(message, buflen,
-                                                test_options_cb,
-                                                NULL)) == -ENOMSG);
-        } else if (desc->success) {
-                assert((res = dhcp_option_parse(message, buflen,
-                                                test_options_cb,
-                                                desc)) >= 0);
-                assert(desc->pos == -1 && desc->filepos == -1 &&
-                                desc->snamepos == -1);
-        } else
-                assert((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(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
-
-        len = 0;
-        opt = &result[0];
-        assert(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
-        assert(opt == &result[0] && len == 0);
-
-        assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
-                                  0, NULL) == -ENOBUFS);
-        assert(opt == &result[0] && len == 0);
-
-        opt = &result[4];
-        len = 1;
-        assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
-                                    0, NULL) >= 0);
-        assert(opt == &result[5] && len == 0);
-
-        pos = 4;
-        len = 60;
-        while (pos < 64 && options[pos] != DHCP_OPTION_END) {
-                opt = &result[pos];
-                oldlen = len;
-
-                assert(dhcp_option_append(&opt, &len, options[pos],
-                                          options[pos + 1],
-                                          &options[pos + 2]) >= 0);
-
-                if (options[pos] == DHCP_OPTION_PAD) {
-                        assert(opt == &result[pos + 1]);
-                        assert(len == oldlen - 1);
-                        pos++;
-                } else {
-                        assert(opt == &result[pos + 2 + options[pos + 1]]);
-                        assert(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(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-dhcp/Makefile b/src/libsystemd-dhcp/Makefile
new file mode 120000
index 0000000..d0b0e8e
--- /dev/null
+++ b/src/libsystemd-dhcp/Makefile
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/libsystemd-dhcp/client.c b/src/libsystemd-dhcp/client.c
new file mode 100644
index 0000000..2a479e4
--- /dev/null
+++ b/src/libsystemd-dhcp/client.c
@@ -0,0 +1,1002 @@
+/***
+  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 "util.h"
+#include "list.h"
+
+#include "protocol.h"
+#include "internal.h"
+#include "sd-dhcp-client.h"
+
+#define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
+
+struct DHCPLease {
+        uint32_t t1;
+        uint32_t t2;
+        uint32_t lifetime;
+        uint32_t address;
+        uint32_t server_address;
+        uint32_t subnet_mask;
+        uint32_t router;
+};
+
+typedef struct DHCPLease DHCPLease;
+
+struct sd_dhcp_client {
+        DHCPState state;
+        sd_event *event;
+        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_size;
+        uint32_t last_addr;
+        struct ether_addr mac_addr;
+        uint32_t xid;
+        usec_t start_time;
+        unsigned int attempt;
+        usec_t request_sent;
+        sd_event_source *timeout_t1;
+        sd_event_source *timeout_t2;
+        sd_event_source *timeout_expire;
+        sd_dhcp_client_cb_t cb;
+        void *userdata;
+        DHCPLease *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,
+};
+
+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_size,
+                            client->req_opts_size + 1))
+                return -ENOMEM;
+
+        client->req_opts[client->req_opts_size - 1] = 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_address(sd_dhcp_client *client, struct in_addr *addr)
+{
+        assert_return(client, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        switch (client->state) {
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_REQUESTING:
+                return -EADDRNOTAVAIL;
+
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+                addr->s_addr = client->lease->address;
+
+                break;
+        }
+
+        return 0;
+}
+
+int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr)
+{
+        assert_return(client, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        switch (client->state) {
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_REQUESTING:
+                return -EADDRNOTAVAIL;
+
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+                addr->s_addr = client->lease->subnet_mask;
+
+                break;
+        }
+
+        return 0;
+}
+
+int sd_dhcp_client_prefixlen(const struct in_addr *addr)
+{
+        int len = 0;
+        uint32_t mask;
+
+        assert_return(addr, -EADDRNOTAVAIL);
+
+        mask = be32toh(addr->s_addr);
+        while (mask) {
+                len++;
+                mask = mask << 1;
+        }
+
+        return len;
+}
+
+int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr)
+{
+        assert_return(client, -EINVAL);
+        assert_return(addr, -EINVAL);
+
+        switch (client->state) {
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_REQUESTING:
+                return -EADDRNOTAVAIL;
+
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+                addr->s_addr = client->lease->router;
+
+                break;
+        }
+
+        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);
+        assert_return(client->state != DHCP_STATE_INIT &&
+                      client->state != DHCP_STATE_INIT_REBOOT, -EALREADY);
+
+        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);
+
+        switch (client->state) {
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_BOUND:
+
+                client->start_time = 0;
+                client->state = DHCP_STATE_INIT;
+                break;
+
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+
+                break;
+        }
+
+        if (client->lease) {
+                free(client->lease);
+                client->lease = NULL;
+        }
+
+        return 0;
+}
+
+static int client_packet_init(sd_dhcp_client *client, uint8_t type,
+                              DHCPMessage *message, uint16_t secs,
+                              uint8_t **opt, size_t *optlen)
+{
+        int err;
+        be16_t max_size;
+
+        *opt = (uint8_t *)(message + 1);
+
+        if (*optlen < 4)
+                return -ENOBUFS;
+        *optlen -= 4;
+
+        message->op = BOOTREQUEST;
+        message->htype = 1;
+        message->hlen = ETHER_ADDR_LEN;
+        message->xid = htobe32(client->xid);
+
+        /* 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);
+        (*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;
+
+        /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
+           Identifier option is not set */
+        err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
+                                 ETH_ALEN, &client->mac_addr);
+        if (err < 0)
+                return err;
+
+        if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
+                err = dhcp_option_append(opt, optlen,
+                                         DHCP_OPTION_PARAMETER_REQUEST_LIST,
+                                         client->req_opts_size,
+                                         client->req_opts);
+                if (err < 0)
+                        return err;
+
+                /* 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_CLIENT_MIN_OPTIONS_SIZE);
+                err = dhcp_option_append(opt, optlen,
+                                         DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
+                                         2, &max_size);
+                if (err < 0)
+                        return err;
+        }
+
+        return 0;
+}
+
+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];
+        }
+
+        return ~((sum & 0xffff) + (sum >> 16));
+}
+
+static void client_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 = client_checksum(&packet->ip.ttl, len - 8);
+
+        packet->ip.ttl = IPDEFTTL;
+        packet->ip.check = 0;
+        packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE);
+}
+
+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_CLIENT_MIN_OPTIONS_SIZE;
+        len = sizeof(DHCPPacket) + optlen;
+
+        discover = malloc0(len);
+
+        if (!discover)
+                return -ENOMEM;
+
+        err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
+                                 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;
+
+        client_append_ip_headers(discover, len);
+
+        err = dhcp_network_send_raw_socket(client->fd, &client->link,
+                                           discover, len);
+
+        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_CLIENT_MIN_OPTIONS_SIZE;
+        len = DHCP_MESSAGE_SIZE + optlen;
+
+        request = malloc0(len);
+        if (!request)
+                return -ENOMEM;
+
+        err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, 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;
+
+        client_append_ip_headers(request, len);
+
+        err = dhcp_network_send_raw_socket(client->fd, &client->link,
+                                           request, len);
+
+        return err;
+}
+
+static int client_timeout_resend(sd_event_source *s, uint64_t usec,
+                                 void *userdata)
+{
+        sd_dhcp_client *client = userdata;
+        usec_t next_timeout;
+        uint16_t secs;
+        int err = 0;
+
+        secs = (usec - client->start_time) / USEC_PER_SEC;
+
+        if (client->attempt < 64)
+                client->attempt *= 2;
+
+        next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC +
+                (random_u() & 0x1fffff);
+
+        err = sd_event_add_monotonic(client->event, next_timeout,
+                                     10 * USEC_PER_MSEC,
+                                     client_timeout_resend, client,
+                                     &client->timeout_resend);
+        if (err < 0)
+                goto error;
+
+        switch (client->state) {
+        case DHCP_STATE_INIT:
+                err = client_send_discover(client, secs);
+                if (err >= 0) {
+                        client->state = DHCP_STATE_SELECTING;
+                        client->attempt = 1;
+                } else {
+                        if (client->attempt >= 64)
+                                goto error;
+                }
+
+                break;
+
+        case DHCP_STATE_SELECTING:
+                err = client_send_discover(client, secs);
+                if (err < 0 && client->attempt >= 64)
+                        goto error;
+
+                break;
+
+        case DHCP_STATE_REQUESTING:
+                err = client_send_request(client, secs);
+                if (err < 0 && client->attempt >= 64)
+                         goto error;
+
+                client->request_sent = usec;
+
+                break;
+
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+
+                break;
+        }
+
+        return 0;
+
+error:
+        client_stop(client, err);
+
+        /* Errors were dealt with when stopping the client, don't spill
+           errors into the event loop handler */
+        return 0;
+}
+
+static int client_timeout_expire(sd_event_source *s, uint64_t usec,
+                                 void *userdata)
+{
+        sd_dhcp_client *client = userdata;
+
+        client_stop(client, DHCP_EVENT_EXPIRED);
+
+        return 0;
+}
+
+static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
+{
+        return 0;
+}
+
+static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
+{
+        return 0;
+}
+
+static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
+                              void *user_data)
+{
+        DHCPLease *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_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;
+}
+
+static int client_verify_headers(sd_dhcp_client *client, DHCPPacket *message,
+                                 size_t len)
+{
+        size_t hdrlen;
+
+        if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
+                return -EINVAL;
+
+        hdrlen = message->ip.ihl * 4;
+        if (hdrlen < 20 || hdrlen > len || client_checksum(&message->ip,
+                                                           hdrlen))
+                return -EINVAL;
+
+        message->ip.check = message->udp.len;
+        message->ip.ttl = 0;
+
+        if (hdrlen + be16toh(message->udp.len) > len ||
+            client_checksum(&message->ip.ttl, be16toh(message->udp.len) + 12))
+                return -EINVAL;
+
+        if (be16toh(message->udp.source) != DHCP_PORT_SERVER ||
+            be16toh(message->udp.dest) != DHCP_PORT_CLIENT)
+                return -EINVAL;
+
+        if (message->dhcp.op != BOOTREPLY)
+                return -EINVAL;
+
+        if (be32toh(message->dhcp.xid) != client->xid)
+                return -EINVAL;
+
+        if (memcmp(&message->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
+                    ETHER_ADDR_LEN))
+                return -EINVAL;
+
+        return 0;
+}
+
+static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
+                                size_t len)
+{
+        int err;
+        DHCPLease *lease;
+
+        err = client_verify_headers(client, offer, len);
+        if (err < 0)
+                return err;
+
+        lease = new0(DHCPLease, 1);
+        if (!lease)
+                return -ENOMEM;
+
+        len = len - DHCP_IP_UDP_SIZE;
+        if (dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
+                              lease) != DHCP_OFFER)
+                goto error;
+
+        lease->address = offer->dhcp.yiaddr;
+
+        if (lease->address == INADDR_ANY ||
+            lease->server_address == INADDR_ANY ||
+            lease->subnet_mask == INADDR_ANY ||
+            lease->lifetime == 0)
+                goto error;
+
+        client->lease = lease;
+
+        return 0;
+
+error:
+        free(lease);
+
+        return -ENOMSG;
+}
+
+static int client_receive_ack(sd_dhcp_client *client, DHCPPacket *offer,
+                              size_t len)
+{
+        int r;
+        DHCPLease *lease;
+
+        r = client_verify_headers(client, offer, len);
+        if (r < 0)
+                return r;
+
+        lease = new0(DHCPLease, 1);
+        if (!lease)
+                return -ENOBUFS;
+
+        len = len - DHCP_IP_UDP_SIZE;
+        r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer, lease);
+
+        if (r != DHCP_ACK)
+                goto error;
+
+        lease->address = offer->dhcp.yiaddr;
+
+        if (lease->address == INADDR_ANY ||
+            lease->server_address == INADDR_ANY ||
+            lease->subnet_mask == INADDR_ANY || lease->lifetime == 0) {
+                r = -ENOMSG;
+                goto error;
+        }
+
+        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;
+                }
+
+                free(client->lease);
+        }
+
+        client->lease = lease;
+
+        return r;
+
+error:
+        free(lease);
+
+        return r;
+}
+
+static uint64_t client_compute_timeout(uint64_t request_sent,
+                                       uint32_t lifetime)
+{
+        return request_sent + (lifetime - 3) * USEC_PER_SEC +
+                + (random_u() & 0x1fffff);
+}
+
+static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec)
+{
+        int err;
+        uint64_t next_timeout;
+
+        if (client->lease->lifetime < 10)
+                return -EINVAL;
+
+        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;
+
+        err = sd_event_add_monotonic(client->event, next_timeout,
+                                     10 * USEC_PER_MSEC,
+                                     client_timeout_t1, client,
+                                     &client->timeout_t1);
+        if (err < 0)
+                return err;
+
+        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;
+
+        err = sd_event_add_monotonic(client->event, next_timeout,
+                                     10 * USEC_PER_MSEC,
+                                     client_timeout_t2, client,
+                                     &client->timeout_t2);
+        if (err < 0)
+                return err;
+
+        next_timeout = client_compute_timeout(client->request_sent,
+                                              client->lease->lifetime);
+        if (next_timeout < usec)
+                return -EINVAL;
+
+        err = sd_event_add_monotonic(client->event, next_timeout,
+                                     10 * USEC_PER_MSEC,
+                                     client_timeout_expire, client,
+                                     &client->timeout_expire);
+        if (err < 0)
+                return err;
+
+        return 0;
+}
+
+static int client_receive_raw_message(sd_event_source *s, int fd,
+                                      uint32_t revents, void *userdata)
+{
+        sd_dhcp_client *client = userdata;
+        uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE];
+        int buflen = sizeof(buf);
+        int len, r = 0;
+        DHCPPacket *message;
+        usec_t time_now;
+
+        len = read(fd, &buf, buflen);
+        if (len < 0)
+                return 0;
+
+        r = sd_event_get_now_monotonic(client->event, &time_now);
+        if (r < 0)
+                goto error;
+
+        message = (DHCPPacket *)&buf;
+
+        switch (client->state) {
+        case DHCP_STATE_SELECTING:
+
+                if (client_receive_offer(client, message, len) >= 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, time_now, 0,
+                                                   client_timeout_resend,
+                                                   client,
+                                                   &client->timeout_resend);
+                        if (r < 0)
+                                goto error;
+                }
+
+                break;
+
+        case DHCP_STATE_REQUESTING:
+
+                r = client_receive_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);
+
+                        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;
+
+                        client_notify(client, DHCP_EVENT_IP_ACQUIRE);
+
+                        close(client->fd);
+                        client->fd = -1;
+                        client->receive_message =
+                                sd_event_source_unref(client->receive_message);
+                }
+                break;
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+
+                break;
+        }
+
+error:
+        if (r < 0)
+                return client_stop(client, r);
+
+        return 0;
+}
+
+int sd_dhcp_client_start(sd_dhcp_client *client)
+{
+        int err;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->index >= 0, -EINVAL);
+        assert_return(client->state == DHCP_STATE_INIT ||
+                      client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
+
+        client->xid = random_u();
+
+        client->fd = dhcp_network_bind_raw_socket(client->index,
+                                                  &client->link);
+
+        if (client->fd < 0) {
+                err = client->fd;
+                goto error;
+        }
+
+        err = sd_event_add_io(client->event, client->fd, EPOLLIN,
+                              client_receive_raw_message, client,
+                              &client->receive_message);
+        if (err < 0)
+                goto error;
+
+        client->start_time = now(CLOCK_MONOTONIC);
+        err = sd_event_add_monotonic(client->event, client->start_time, 0,
+                                     client_timeout_resend, client,
+                                     &client->timeout_resend);
+        if (err < 0)
+                goto error;
+
+        return 0;
+
+error:
+        client_stop(client, err);
+
+        return err;
+}
+
+int sd_dhcp_client_stop(sd_dhcp_client *client)
+{
+        return client_stop(client, DHCP_EVENT_STOP);
+}
+
+sd_dhcp_client *sd_dhcp_client_free(sd_dhcp_client *client)
+{
+        assert_return(client, NULL);
+
+        sd_dhcp_client_stop(client);
+
+        sd_event_unref(client->event);
+        free(client->req_opts);
+        free(client);
+
+        return NULL;
+}
+
+sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
+{
+        sd_dhcp_client *client;
+
+        assert_return(event, NULL);
+
+        client = new0(sd_dhcp_client, 1);
+        if (!client)
+                return NULL;
+
+        client->event = sd_event_ref(event);
+        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) {
+                free(client);
+                return NULL;
+        }
+
+        return client;
+}
diff --git a/src/libsystemd-dhcp/internal.h b/src/libsystemd-dhcp/internal.h
new file mode 100644
index 0000000..85bb908
--- /dev/null
+++ b/src/libsystemd-dhcp/internal.h
@@ -0,0 +1,41 @@
+/*-*- 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 <stdint.h>
+#include <linux/if_packet.h>
+
+#include "socket-util.h"
+
+#include "protocol.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link);
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+                                 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);
diff --git a/src/libsystemd-dhcp/network.c b/src/libsystemd-dhcp/network.c
new file mode 100644
index 0000000..3a16947
--- /dev/null
+++ b/src/libsystemd-dhcp/network.c
@@ -0,0 +1,65 @@
+/***
+  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 "internal.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
+ {
+        int s;
+
+        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 (bind(s, &link->sa, sizeof(link->ll)) < 0) {
+                close(s);
+                return -errno;
+        }
+
+        return s;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+                                 const void *packet, size_t len)
+{
+        int err = 0;
+
+        if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 0)
+                err = -errno;
+
+        return err;
+}
diff --git a/src/libsystemd-dhcp/option.c b/src/libsystemd-dhcp/option.c
new file mode 100644
index 0000000..235b387
--- /dev/null
+++ b/src/libsystemd-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 "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/protocol.h b/src/libsystemd-dhcp/protocol.h
new file mode 100644
index 0000000..1e59965
--- /dev/null
+++ b/src/libsystemd-dhcp/protocol.h
@@ -0,0 +1,119 @@
+/*-*- 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;
+        uint32_t ciaddr;
+        uint32_t yiaddr;
+        uint32_t siaddr;
+        uint32_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))
+
+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_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/test-libsystemd-dhcp-client.c b/src/libsystemd-dhcp/test-libsystemd-dhcp-client.c
new file mode 100644
index 0000000..ee0848d
--- /dev/null
+++ b/src/libsystemd-dhcp/test-libsystemd-dhcp-client.c
@@ -0,0 +1,224 @@
+/*-*- 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 "protocol.h"
+#include "internal.h"
+#include "sd-dhcp-client.h"
+
+static struct ether_addr mac_addr = {
+        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+};
+
+static int test_fd[2];
+
+static void test_request_basic(sd_event *e)
+{
+        sd_dhcp_client *client;
+
+        client = sd_dhcp_client_new(e);
+
+        assert(client);
+
+        assert(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
+        assert(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
+        assert(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
+
+        assert(sd_dhcp_client_set_index(client, 15) == 0);
+        assert(sd_dhcp_client_set_index(client, -42) == -EINVAL);
+        assert(sd_dhcp_client_set_index(client, -1) == 0);
+
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_SUBNET_MASK) == -EEXIST);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_ROUTER) == -EEXIST);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_HOST_NAME) == -EEXIST);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_DOMAIN_NAME_SERVER)
+                        == -EEXIST);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_NTP_SERVER) == -EEXIST);
+
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_PAD) == -EINVAL);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_END) == -EINVAL);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_OVERLOAD) == -EINVAL);
+        assert(sd_dhcp_client_set_request_option(client,
+                                        DHCP_OPTION_PARAMETER_REQUEST_LIST)
+                        == -EINVAL);
+
+        assert(sd_dhcp_client_set_request_option(client, 33) == 0);
+        assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
+        assert(sd_dhcp_client_set_request_option(client, 44) == 0);
+}
+
+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];
+        }
+
+        return ~((sum & 0xffff) + (sum >> 16));
+}
+
+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
+        };
+
+        uint8_t check[2] = {
+                0x78, 0xae
+        };
+
+        uint16_t *val = (uint16_t *)check;
+
+        assert(client_checksum(&buf, 20) == *val);
+}
+
+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;
+        int res;
+
+        assert(s >= 0);
+        assert(packet);
+
+        size = sizeof(DHCPPacket) + 4;
+        assert(len > size);
+
+        discover = memdup(packet, len);
+
+        assert(memcmp(discover->dhcp.chaddr,
+                      &mac_addr.ether_addr_octet, 6) == 0);
+        assert(discover->ip.ttl == IPDEFTTL);
+        assert(discover->ip.protocol == IPPROTO_UDP);
+        assert(discover->ip.saddr == INADDR_ANY);
+        assert(discover->ip.daddr == INADDR_BROADCAST);
+        assert(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
+        assert(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(udp_check == 0xffff);
+
+        discover->ip.ttl = IPDEFTTL;
+        discover->ip.check = ip_check;
+
+        ip_check = ~client_checksum(&discover->ip, sizeof(discover->ip));
+        assert(ip_check == 0xffff);
+
+        size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
+
+        res = dhcp_option_parse(&discover->dhcp, size, check_options, NULL);
+        if (res < 0)
+                return res;
+
+        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];
+}
+
+static void test_discover_message(sd_event *e)
+{
+        sd_dhcp_client *client;
+        int res;
+
+        client = sd_dhcp_client_new(e);
+        assert(client);
+
+        assert(sd_dhcp_client_set_index(client, 42) >= 0);
+        assert(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
+
+        assert(sd_dhcp_client_set_request_option(client, 248) >= 0);
+
+        res = sd_dhcp_client_start(client);
+
+        assert(res == 0 || res == -EINPROGRESS);
+
+        close(test_fd[0]);
+        close(test_fd[1]);
+}
+
+int main(int argc, char *argv[])
+{
+        sd_event *e;
+
+        assert(sd_event_new(&e) >= 0);
+
+        test_request_basic(e);
+        test_checksum();
+
+        test_discover_message(e);
+        sd_event_run(e, (uint64_t) -1);
+
+        return 0;
+}
diff --git a/src/libsystemd-dhcp/test-libsystemd-dhcp-option.c b/src/libsystemd-dhcp/test-libsystemd-dhcp-option.c
new file mode 100644
index 0000000..b85da79
--- /dev/null
+++ b/src/libsystemd-dhcp/test-libsystemd-dhcp-option.c
@@ -0,0 +1,377 @@
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include "util.h"
+#include "macro.h"
+
+#include "protocol.h"
+#include "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(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
+        assert(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(dhcp_option_parse(message, len, NULL, NULL) == -EINVAL);
+
+        opt[0] = 99;
+        opt[1] = 130;
+        opt[2] = 83;
+        opt[3] = 99;
+
+        assert(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((!desc && !code && !len) || desc);
+
+        if (!desc)
+                return -EINVAL;
+
+        assert(code != DHCP_OPTION_PAD);
+        assert(code != DHCP_OPTION_END);
+        assert(code != DHCP_OPTION_MESSAGE_TYPE);
+        assert(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(descoption && desclen && descpos);
+
+                if (*desclen)
+                        test_ignore_opts(descoption, descpos, desclen);
+
+                if (*descpos < *desclen)
+                        break;
+
+                if (*descpos == *desclen)
+                        *descpos = -1;
+        }
+
+        assert(*descpos != -1);
+
+        optcode = descoption[*descpos];
+        optlen = descoption[*descpos + 1];
+
+        if (verbose)
+                printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
+                                len, optlen);
+
+        assert(code == optcode);
+        assert(len == optlen);
+
+        for (i = 0; i < len; i++) {
+
+                if (verbose)
+                        printf("0x%02x(0x%02x) ", option[i],
+                                        descoption[*descpos + 2 + i]);
+
+                assert(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((res = dhcp_option_parse(message, buflen,
+                                                test_options_cb,
+                                                NULL)) == -ENOMSG);
+        } else if (desc->success) {
+                assert((res = dhcp_option_parse(message, buflen,
+                                                test_options_cb,
+                                                desc)) >= 0);
+                assert(desc->pos == -1 && desc->filepos == -1 &&
+                                desc->snamepos == -1);
+        } else
+                assert((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(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
+
+        len = 0;
+        opt = &result[0];
+        assert(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
+        assert(opt == &result[0] && len == 0);
+
+        assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
+                                  0, NULL) == -ENOBUFS);
+        assert(opt == &result[0] && len == 0);
+
+        opt = &result[4];
+        len = 1;
+        assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
+                                    0, NULL) >= 0);
+        assert(opt == &result[5] && len == 0);
+
+        pos = 4;
+        len = 60;
+        while (pos < 64 && options[pos] != DHCP_OPTION_END) {
+                opt = &result[pos];
+                oldlen = len;
+
+                assert(dhcp_option_append(&opt, &len, options[pos],
+                                          options[pos + 1],
+                                          &options[pos + 2]) >= 0);
+
+                if (options[pos] == DHCP_OPTION_PAD) {
+                        assert(opt == &result[pos + 1]);
+                        assert(len == oldlen - 1);
+                        pos++;
+                } else {
+                        assert(opt == &result[pos + 2 + options[pos + 1]]);
+                        assert(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(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;
+}
-- 
1.7.10.4



More information about the systemd-devel mailing list