[systemd-commits] 27 commits - Makefile.am src/libsystemd-dhcp src/shared src/systemd

Zbigniew Jędrzejewski-Szmek zbyszek at kemper.freedesktop.org
Thu Dec 12 08:46:05 PST 2013


 Makefile.am                            |   50 +
 src/libsystemd-dhcp/Makefile           |    1 
 src/libsystemd-dhcp/dhcp-client.c      | 1002 +++++++++++++++++++++++++++++++++
 src/libsystemd-dhcp/dhcp-internal.h    |   41 +
 src/libsystemd-dhcp/dhcp-network.c     |   65 ++
 src/libsystemd-dhcp/dhcp-option.c      |  184 ++++++
 src/libsystemd-dhcp/dhcp-protocol.h    |  119 +++
 src/libsystemd-dhcp/test-dhcp-client.c |  224 +++++++
 src/libsystemd-dhcp/test-dhcp-option.c |  377 ++++++++++++
 src/shared/socket-util.h               |    2 
 src/systemd/sd-dhcp-client.h           |   63 ++
 11 files changed, 2121 insertions(+), 7 deletions(-)

New commits:
commit 189b2384d6d2f049fe84ac88d7b85d03d277561f
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Thu Dec 12 11:42:45 2013 -0500

    build-sys: use internal library for dhcp client tests
    
    Also clean up AM_CFLAGS in a few places.

diff --git a/Makefile.am b/Makefile.am
index c933279..0777d01 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -798,9 +798,6 @@ libsystemd_units_la_SOURCES = \
 	src/shared/specifier.c \
 	src/shared/specifier.h
 
-libsystemd_units_la_CFLAGS = \
-	$(AM_CFLAGS)
-
 # ------------------------------------------------------------------------------
 noinst_LTLIBRARIES += \
 	libsystemd-label.la
@@ -1563,11 +1560,10 @@ EXTRA_DIST += \
 # ------------------------------------------------------------------------------
 if ENABLE_TMPFILES
 systemd_tmpfiles_SOURCES = \
-	src/tmpfiles/tmpfiles.c \
-	src/shared/specifier.c \
-	src/shared/specifier.h
+	src/tmpfiles/tmpfiles.c
 
 systemd_tmpfiles_LDADD = \
+	libsystemd-units.la \
 	libsystemd-label.la \
 	libsystemd-capability.la \
 	libsystemd-id128-internal.la \
@@ -2049,7 +2045,7 @@ libsystemd_bus_dump_la_SOURCES = \
 	src/libsystemd-bus/bus-dump.h
 
 libsystemd_bus_dump_la_CFLAGS = \
-	$(AM_CFLAGS)
+	$(AM_CFLAGS) \
 	$(CAP_CFLAGS)
 
 noinst_LTLIBRARIES += \
@@ -3858,12 +3854,6 @@ libsystemd_dhcp_la_SOURCES = \
 noinst_LTLIBRARIES += \
 	libsystemd-dhcp.la
 
-libsystemd_dhcp_la_CFLAGS = \
-	$(AM_CFLAGS)
-
-libsystemd_dhcp_la_LDFLAGS = \
-	$(AM_LDFLAGS)
-
 libsystemd_dhcp_la_LIBADD = \
 	libsystemd-shared.la \
 	libsystemd-bus.la
@@ -3871,21 +3861,20 @@ libsystemd_dhcp_la_LIBADD = \
 test_dhcp_option_SOURCES = \
 	src/libsystemd-dhcp/dhcp-protocol.h \
 	src/libsystemd-dhcp/dhcp-internal.h \
-	src/libsystemd-dhcp/dhcp-option.c \
 	src/libsystemd-dhcp/test-dhcp-option.c
 
 test_dhcp_option_LDADD = \
+	libsystemd-dhcp.la \
 	libsystemd-shared.la
 
 test_dhcp_client_SOURCES = \
 	src/libsystemd-dhcp/dhcp-protocol.h \
 	src/systemd/sd-dhcp-client.h \
-	src/libsystemd-dhcp/dhcp-client.c \
 	src/libsystemd-dhcp/dhcp-internal.h \
-	src/libsystemd-dhcp/dhcp-option.c \
 	src/libsystemd-dhcp/test-dhcp-client.c
 
 test_dhcp_client_LDADD = \
+	libsystemd-dhcp.la \
 	libsystemd-shared.la \
 	libsystemd-bus.la
 

commit d2fe46b514ef3f6e0c0eb16b2d853c6dd6fa1808
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:32 2013 +0200

    dhcp: Add function to free DHCP client data

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index 03a846d..01d406c 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -961,6 +961,19 @@ 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;
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 395c12d..fdc5b2d 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -57,6 +57,7 @@ int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr);
 
 int sd_dhcp_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
+sd_dhcp_client *sd_dhcp_client_free(sd_dhcp_client *client);
 sd_dhcp_client *sd_dhcp_client_new(sd_event *event);
 
 #endif

commit 751246ee37cf0cd72baf378f1b9c1ac04f8b8c9b
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:31 2013 +0200

    dhcp: Add notification callback
    
    Define a notification callback and events for stopping and client
    lease expiry. Add functions to fetch IP parameters from a lease.

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index d03435b..03a846d 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -63,6 +63,8 @@ struct sd_dhcp_client {
         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;
 };
 
@@ -75,6 +77,17 @@ static const uint8_t default_req_opts[] = {
         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;
@@ -143,8 +156,99 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
         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;
 }
 
@@ -169,6 +273,8 @@ static int client_stop(sd_dhcp_client *client, int error)
 
         client->attempt = 1;
 
+        client_notify(client, error);
+
         switch (client->state) {
 
         case DHCP_STATE_INIT:
@@ -463,6 +569,10 @@ error:
 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;
 }
 
@@ -848,7 +958,7 @@ error:
 
 int sd_dhcp_client_stop(sd_dhcp_client *client)
 {
-        return client_stop(client, 0);
+        return client_stop(client, DHCP_EVENT_STOP);
 }
 
 sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index ed72cf1..395c12d 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -28,13 +28,21 @@
 #include "sd-event.h"
 
 enum {
+        DHCP_EVENT_STOP                         = 0,
         DHCP_EVENT_NO_LEASE                     = 1,
         DHCP_EVENT_IP_ACQUIRE                   = 2,
         DHCP_EVENT_IP_CHANGE                    = 3,
+        DHCP_EVENT_EXPIRED                      = 4,
 };
 
 typedef struct sd_dhcp_client sd_dhcp_client;
 
+typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event,
+                                    void *userdata);
+int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
+                                void *userdata);
+
+
 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
                                        const struct in_addr *last_address);
@@ -42,6 +50,11 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index);
 int sd_dhcp_client_set_mac(sd_dhcp_client *client,
                            const struct ether_addr *addr);
 
+int sd_dhcp_client_get_address(sd_dhcp_client *client, struct in_addr *addr);
+int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr);
+int sd_dhcp_client_prefixlen(const struct in_addr *addr);
+int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr);
+
 int sd_dhcp_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
 sd_dhcp_client *sd_dhcp_client_new(sd_event *event);

commit 51debc1e396a14d7c1204d8661c4eb1056c47670
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:30 2013 +0200

    dhcp: Compute expire, T1 and T2 timers
    
    Compute the default T1 and T2 timer values if they were not set by
    the DHCP server. Verify that the values are reasonable.

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index a158669..d03435b 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -33,6 +33,8 @@
 #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;
@@ -57,6 +59,10 @@ struct sd_dhcp_client {
         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;
         DHCPLease *lease;
 };
 
@@ -157,6 +163,10 @@ static int client_stop(sd_dhcp_client *client, int error)
 
         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;
 
         switch (client->state) {
@@ -164,6 +174,7 @@ static int client_stop(sd_dhcp_client *client, int error)
         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;
@@ -171,7 +182,6 @@ static int client_stop(sd_dhcp_client *client, int error)
 
         case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_BOUND:
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
 
@@ -427,6 +437,8 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
                 if (err < 0 && client->attempt >= 64)
                          goto error;
 
+                client->request_sent = usec;
+
                 break;
 
         case DHCP_STATE_INIT_REBOOT:
@@ -448,6 +460,22 @@ error:
         return 0;
 }
 
+static int client_timeout_expire(sd_event_source *s, uint64_t usec,
+                                 void *userdata)
+{
+        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)
 {
@@ -481,6 +509,22 @@ static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
                         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;
@@ -610,6 +654,72 @@ error:
         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)
 {
@@ -666,6 +776,10 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
 
                         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);
diff --git a/src/libsystemd-dhcp/dhcp-protocol.h b/src/libsystemd-dhcp/dhcp-protocol.h
index f5f490d..1e59965 100644
--- a/src/libsystemd-dhcp/dhcp-protocol.h
+++ b/src/libsystemd-dhcp/dhcp-protocol.h
@@ -112,6 +112,8 @@ enum {
         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,
 };

commit 3e3d8f7857a7a59d62cc5bd1e4e792dc4d0f40b3
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:29 2013 +0200

    dhcp: Process DHCP Ack/Nak message
    
    Process a DHCP Ack/Nak in much the same way as an DHCP Offer. Factor
    out header verification and process options sent. Add notification
    functionality with discrete values for the outcome of the DHCP Ack/
    Nak processing.

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index b1bdd38..a158669 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -137,6 +137,11 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
         return 0;
 }
 
+static int client_notify(sd_dhcp_client *client, int event)
+{
+        return 0;
+}
+
 static int client_stop(sd_dhcp_client *client, int error)
 {
         assert_return(client, -EINVAL);
@@ -158,6 +163,7 @@ static int client_stop(sd_dhcp_client *client, int error)
 
         case DHCP_STATE_INIT:
         case DHCP_STATE_SELECTING:
+        case DHCP_STATE_REQUESTING:
 
                 client->start_time = 0;
                 client->state = DHCP_STATE_INIT;
@@ -165,7 +171,6 @@ static int client_stop(sd_dhcp_client *client, int error)
 
         case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_REQUESTING:
         case DHCP_STATE_BOUND:
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
@@ -481,41 +486,53 @@ static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
         return 0;
 }
 
-static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
-                                size_t len)
+static int client_verify_headers(sd_dhcp_client *client, DHCPPacket *message,
+                                 size_t len)
 {
         size_t hdrlen;
-        DHCPLease *lease;
 
         if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
                 return -EINVAL;
 
-        hdrlen = offer->ip.ihl * 4;
-        if (hdrlen < 20 || hdrlen > len || client_checksum(&offer->ip,
+        hdrlen = message->ip.ihl * 4;
+        if (hdrlen < 20 || hdrlen > len || client_checksum(&message->ip,
                                                            hdrlen))
                 return -EINVAL;
 
-        offer->ip.check = offer->udp.len;
-        offer->ip.ttl = 0;
+        message->ip.check = message->udp.len;
+        message->ip.ttl = 0;
 
-        if (hdrlen + be16toh(offer->udp.len) > len ||
-            client_checksum(&offer->ip.ttl, be16toh(offer->udp.len) + 12))
+        if (hdrlen + be16toh(message->udp.len) > len ||
+            client_checksum(&message->ip.ttl, be16toh(message->udp.len) + 12))
                 return -EINVAL;
 
-        if (be16toh(offer->udp.source) != DHCP_PORT_SERVER ||
-            be16toh(offer->udp.dest) != DHCP_PORT_CLIENT)
+        if (be16toh(message->udp.source) != DHCP_PORT_SERVER ||
+            be16toh(message->udp.dest) != DHCP_PORT_CLIENT)
                 return -EINVAL;
 
-        if (offer->dhcp.op != BOOTREPLY)
+        if (message->dhcp.op != BOOTREPLY)
                 return -EINVAL;
 
-        if (be32toh(offer->dhcp.xid) != client->xid)
+        if (be32toh(message->dhcp.xid) != client->xid)
                 return -EINVAL;
 
-        if (memcmp(&offer->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
+        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;
@@ -543,13 +560,63 @@ error:
         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 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, err = 0;
+        int len, r = 0;
         DHCPPacket *message;
         usec_t time_now;
 
@@ -557,8 +624,8 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
         if (len < 0)
                 return 0;
 
-        err = sd_event_get_now_monotonic(client->event, &time_now);
-        if (err < 0)
+        r = sd_event_get_now_monotonic(client->event, &time_now);
+        if (r < 0)
                 goto error;
 
         message = (DHCPPacket *)&buf;
@@ -574,20 +641,43 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
                         client->state = DHCP_STATE_REQUESTING;
                         client->attempt = 1;
 
-                        err = sd_event_add_monotonic(client->event, time_now, 0,
-                                                     client_timeout_resend,
-                                                     client,
-                                                     &client->timeout_resend);
-                        if (err < 0)
+                        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;
+
+                        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_REQUESTING:
         case DHCP_STATE_BOUND:
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
@@ -596,8 +686,8 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
         }
 
 error:
-        if (err < 0)
-                return client_stop(client, err);
+        if (r < 0)
+                return client_stop(client, r);
 
         return 0;
 }
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 5c9c327..ed72cf1 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -27,6 +27,12 @@
 
 #include "sd-event.h"
 
+enum {
+        DHCP_EVENT_NO_LEASE                     = 1,
+        DHCP_EVENT_IP_ACQUIRE                   = 2,
+        DHCP_EVENT_IP_CHANGE                    = 3,
+};
+
 typedef struct sd_dhcp_client sd_dhcp_client;
 
 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);

commit 564ba3b0efbcaf8d5881dbfb80d3c0b174ccaba5
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:28 2013 +0200

    dhcp: Add maximum message size option
    
    Add maximum message size option to keep some DHCP server implementations
    from sending too big messages. See ConnMan commit
    0c5c862749c05193cf4c513628328c6db02b5222.

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index 4980174..b1bdd38 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -186,6 +186,7 @@ static int client_packet_init(sd_dhcp_client *client, uint8_t type,
                               uint8_t **opt, size_t *optlen)
 {
         int err;
+        be16_t max_size;
 
         *opt = (uint8_t *)(message + 1);
 
@@ -229,6 +230,17 @@ static int client_packet_init(sd_dhcp_client *client, uint8_t type,
                                          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;
diff --git a/src/libsystemd-dhcp/dhcp-protocol.h b/src/libsystemd-dhcp/dhcp-protocol.h
index 95c4f4e..f5f490d 100644
--- a/src/libsystemd-dhcp/dhcp-protocol.h
+++ b/src/libsystemd-dhcp/dhcp-protocol.h
@@ -111,6 +111,7 @@ enum {
         DHCP_OPTION_MESSAGE_TYPE                = 53,
         DHCP_OPTION_SERVER_IDENTIFIER           = 54,
         DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
+        DHCP_OPTION_MAXIMUM_MESSAGE_SIZE        = 57,
         DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
         DHCP_OPTION_END                         = 255,
 };

commit e2dfc79f0624dfbfa30156880b31b13510405f54
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:27 2013 +0200

    dhcp: Send DHCP Request to acquire an IP address
    
    Create and send a DHCP Request message reusing already existing parts
    of the code. This causes factoring out IP and UDP header creation and
    moving next timeout calculation to be done every time in the timer
    callback function independent of DHCP state. Also add an exponential
    part to the timer calculation, bail out if there are errors while
    resending the DHCP message for the sixth or more times.

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index d3ba7ae..4980174 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -56,6 +56,7 @@ struct sd_dhcp_client {
         struct ether_addr mac_addr;
         uint32_t xid;
         usec_t start_time;
+        unsigned int attempt;
         DHCPLease *lease;
 };
 
@@ -151,6 +152,8 @@ static int client_stop(sd_dhcp_client *client, int error)
 
         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
 
+        client->attempt = 1;
+
         switch (client->state) {
 
         case DHCP_STATE_INIT:
@@ -252,6 +255,28 @@ static uint16_t client_checksum(void *buf, int 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;
@@ -284,29 +309,55 @@ static int client_send_discover(sd_dhcp_client *client, uint16_t secs)
         if (err < 0)
                 return err;
 
-        discover->ip.version = IPVERSION;
-        discover->ip.ihl = sizeof(discover->ip) >> 2;
-        discover->ip.tot_len = htobe16(len);
+        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;
 
-        discover->ip.protocol = IPPROTO_UDP;
-        discover->ip.saddr = INADDR_ANY;
-        discover->ip.daddr = INADDR_BROADCAST;
+        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;
 
-        discover->udp.source = htobe16(DHCP_PORT_CLIENT);
-        discover->udp.dest = htobe16(DHCP_PORT_SERVER);
-        discover->udp.len = htobe16(len - sizeof(discover->ip));
+                err = dhcp_option_append(&opt, &optlen,
+                                         DHCP_OPTION_SERVER_IDENTIFIER,
+                                         4, &client->lease->server_address);
+                if (err < 0)
+                        return err;
+        }
 
-        discover->ip.check = discover->udp.len;
-        discover->udp.check = client_checksum(&discover->ip.ttl,
-                                              len - 8);
+        err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
+        if (err < 0)
+                return err;
 
-        discover->ip.ttl = IPDEFTTL;
-        discover->ip.check = 0;
-        discover->ip.check = client_checksum(&discover->ip,
-                                             sizeof(discover->ip));
+        client_append_ip_headers(request, len);
 
         err = dhcp_network_send_raw_socket(client->fd, &client->link,
-                                           discover, len);
+                                           request, len);
 
         return err;
 }
@@ -319,32 +370,50 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
         uint16_t secs;
         int err = 0;
 
-        switch (client->state) {
-        case DHCP_STATE_INIT:
-        case DHCP_STATE_SELECTING:
+        secs = (usec - client->start_time) / USEC_PER_SEC;
 
-                if (!client->start_time)
-                        client->start_time = usec;
+        if (client->attempt < 64)
+                client->attempt *= 2;
 
-                secs = (usec - client->start_time) / USEC_PER_SEC;
+        next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC +
+                (random_u() & 0x1fffff);
 
-                next_timeout = usec + 2 * USEC_PER_SEC + (random() & 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;
 
-                err = sd_event_add_monotonic(client->event, next_timeout,
-                                             10 * USEC_PER_MSEC,
-                                             client_timeout_resend, client,
-                                             &client->timeout_resend);
-                if (err < 0)
+        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;
 
-                if (client_send_discover(client, secs) >= 0)
-                        client->state = DHCP_STATE_SELECTING;
+                break;
+
+        case DHCP_STATE_REQUESTING:
+                err = client_send_request(client, secs);
+                if (err < 0 && client->attempt >= 64)
+                         goto error;
 
                 break;
 
         case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_REQUESTING:
         case DHCP_STATE_BOUND:
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
@@ -468,11 +537,16 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
         sd_dhcp_client *client = userdata;
         uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE];
         int buflen = sizeof(buf);
-        int len;
+        int len, err = 0;
         DHCPPacket *message;
+        usec_t time_now;
 
         len = read(fd, &buf, buflen);
         if (len < 0)
+                return 0;
+
+        err = sd_event_get_now_monotonic(client->event, &time_now);
+        if (err < 0)
                 goto error;
 
         message = (DHCPPacket *)&buf;
@@ -482,15 +556,18 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
 
                 if (client_receive_offer(client, message, len) >= 0) {
 
-                        client->receive_message =
-                                sd_event_source_unref(client->receive_message);
-                        close(client->fd);
-                        client->fd = -1;
-
                         client->timeout_resend =
                                 sd_event_source_unref(client->timeout_resend);
 
                         client->state = DHCP_STATE_REQUESTING;
+                        client->attempt = 1;
+
+                        err = sd_event_add_monotonic(client->event, time_now, 0,
+                                                     client_timeout_resend,
+                                                     client,
+                                                     &client->timeout_resend);
+                        if (err < 0)
+                                goto error;
                 }
 
                 break;
@@ -507,6 +584,9 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
         }
 
 error:
+        if (err < 0)
+                return client_stop(client, err);
+
         return 0;
 }
 
@@ -535,7 +615,8 @@ int sd_dhcp_client_start(sd_dhcp_client *client)
         if (err < 0)
                 goto error;
 
-        err = sd_event_add_monotonic(client->event, now(CLOCK_MONOTONIC), 0,
+        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)
@@ -568,6 +649,7 @@ sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
         client->state = DHCP_STATE_INIT;
         client->index = -1;
         client->fd = -1;
+        client->attempt = 1;
 
         client->req_opts_size = ELEMENTSOF(default_req_opts);
 

commit 8c00042c939938818365753023ff2d50f984dec6
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:26 2013 +0200

    dhcp: Handle received DHCP Offer message
    
    Create a function for handling the full IP, UDP and DHCP packet
    and tie it to the main loop. Verify IP and UDP headers and checksum.
    Creat a new lease structure with using the values supplied in the
    DHCP message. Free the lease structure when client is stopped.
    
    Split out socket handling into a creation and a sending part. As a
    result modify the test code.

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index e9c4700..d3ba7ae 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -32,17 +32,31 @@
 
 #define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
 
+struct DHCPLease {
+        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;
+        DHCPLease *lease;
 };
 
 static const uint8_t default_req_opts[] = {
@@ -128,6 +142,13 @@ static int client_stop(sd_dhcp_client *client, int error)
         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);
 
         switch (client->state) {
@@ -135,6 +156,7 @@ static int client_stop(sd_dhcp_client *client, int error)
         case DHCP_STATE_INIT:
         case DHCP_STATE_SELECTING:
 
+                client->start_time = 0;
                 client->state = DHCP_STATE_INIT;
                 break;
 
@@ -148,6 +170,11 @@ static int client_stop(sd_dhcp_client *client, int error)
                 break;
         }
 
+        if (client->lease) {
+                free(client->lease);
+                client->lease = NULL;
+        }
+
         return 0;
 }
 
@@ -278,9 +305,10 @@ static int client_send_discover(sd_dhcp_client *client, uint16_t secs)
         discover->ip.check = client_checksum(&discover->ip,
                                              sizeof(discover->ip));
 
-        err = dhcp_network_send_raw_packet(client->index, discover, len);
+        err = dhcp_network_send_raw_socket(client->fd, &client->link,
+                                           discover, len);
 
-        return 0;
+        return err;
 }
 
 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
@@ -334,6 +362,154 @@ error:
         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;
+        }
+
+        return 0;
+}
+
+static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
+                                size_t len)
+{
+        size_t hdrlen;
+        DHCPLease *lease;
+
+        if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
+                return -EINVAL;
+
+        hdrlen = offer->ip.ihl * 4;
+        if (hdrlen < 20 || hdrlen > len || client_checksum(&offer->ip,
+                                                           hdrlen))
+                return -EINVAL;
+
+        offer->ip.check = offer->udp.len;
+        offer->ip.ttl = 0;
+
+        if (hdrlen + be16toh(offer->udp.len) > len ||
+            client_checksum(&offer->ip.ttl, be16toh(offer->udp.len) + 12))
+                return -EINVAL;
+
+        if (be16toh(offer->udp.source) != DHCP_PORT_SERVER ||
+            be16toh(offer->udp.dest) != DHCP_PORT_CLIENT)
+                return -EINVAL;
+
+        if (offer->dhcp.op != BOOTREPLY)
+                return -EINVAL;
+
+        if (be32toh(offer->dhcp.xid) != client->xid)
+                return -EINVAL;
+
+        if (memcmp(&offer->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
+                    ETHER_ADDR_LEN))
+                return -EINVAL;
+
+        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_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;
+        DHCPPacket *message;
+
+        len = read(fd, &buf, buflen);
+        if (len < 0)
+                goto error;
+
+        message = (DHCPPacket *)&buf;
+
+        switch (client->state) {
+        case DHCP_STATE_SELECTING:
+
+                if (client_receive_offer(client, message, len) >= 0) {
+
+                        client->receive_message =
+                                sd_event_source_unref(client->receive_message);
+                        close(client->fd);
+                        client->fd = -1;
+
+                        client->timeout_resend =
+                                sd_event_source_unref(client->timeout_resend);
+
+                        client->state = DHCP_STATE_REQUESTING;
+                }
+
+                break;
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+
+                break;
+        }
+
+error:
+        return 0;
+}
+
 int sd_dhcp_client_start(sd_dhcp_client *client)
 {
         int err;
@@ -345,6 +521,20 @@ int sd_dhcp_client_start(sd_dhcp_client *client)
 
         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;
+
         err = sd_event_add_monotonic(client->event, now(CLOCK_MONOTONIC), 0,
                                      client_timeout_resend, client,
                                      &client->timeout_resend);
@@ -377,6 +567,7 @@ sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
         client->event = sd_event_ref(event);
         client->state = DHCP_STATE_INIT;
         client->index = -1;
+        client->fd = -1;
 
         client->req_opts_size = ELEMENTSOF(default_req_opts);
 
diff --git a/src/libsystemd-dhcp/dhcp-internal.h b/src/libsystemd-dhcp/dhcp-internal.h
index 814a3cd..4472d95 100644
--- a/src/libsystemd-dhcp/dhcp-internal.h
+++ b/src/libsystemd-dhcp/dhcp-internal.h
@@ -22,10 +22,15 @@
 ***/
 
 #include <stdint.h>
+#include <linux/if_packet.h>
+
+#include "socket-util.h"
 
 #include "dhcp-protocol.h"
 
-int dhcp_network_send_raw_packet(int index, const void *packet, size_t len);
+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);
diff --git a/src/libsystemd-dhcp/dhcp-network.c b/src/libsystemd-dhcp/dhcp-network.c
index ed34228..83a3084 100644
--- a/src/libsystemd-dhcp/dhcp-network.c
+++ b/src/libsystemd-dhcp/dhcp-network.c
@@ -30,26 +30,36 @@
 
 #include "dhcp-internal.h"
 
-int dhcp_network_send_raw_packet(int index, const void *packet, size_t len)
-{
-        _cleanup_close_ int s;
-        union sockaddr_union link = {};
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
+ {
+        int s;
 
-        s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
+        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);
+        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)
+        if (bind(s, &link->sa, sizeof(link->ll)) < 0) {
+                close(s);
                 return -errno;
+        }
 
-        if (sendto(s, packet, len, 0, &link.sa, sizeof(link.ll)) < 0)
-                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 0;
+        return err;
 }
diff --git a/src/libsystemd-dhcp/dhcp-protocol.h b/src/libsystemd-dhcp/dhcp-protocol.h
index fa077fb..95c4f4e 100644
--- a/src/libsystemd-dhcp/dhcp-protocol.h
+++ b/src/libsystemd-dhcp/dhcp-protocol.h
@@ -55,6 +55,10 @@ struct DHCPPacket {
 
 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,
@@ -102,8 +106,10 @@ enum {
         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_CLIENT_IDENTIFIER           = 61,
         DHCP_OPTION_END                         = 255,
diff --git a/src/libsystemd-dhcp/test-dhcp-client.c b/src/libsystemd-dhcp/test-dhcp-client.c
index b8a448d..d398510 100644
--- a/src/libsystemd-dhcp/test-dhcp-client.c
+++ b/src/libsystemd-dhcp/test-dhcp-client.c
@@ -23,8 +23,12 @@
 #include <assert.h>
 #include <errno.h>
 #include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
 
 #include "util.h"
+#include "socket-util.h"
 
 #include "dhcp-protocol.h"
 #include "dhcp-internal.h"
@@ -34,6 +38,8 @@ 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;
@@ -125,14 +131,15 @@ static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
         return 0;
 }
 
-int dhcp_network_send_raw_packet(int index, const void *packet, size_t len)
+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(index == 42);
+        assert(s >= 0);
         assert(packet);
 
         size = sizeof(DHCPPacket) + 4;
@@ -146,8 +153,8 @@ int dhcp_network_send_raw_packet(int index, const void *packet, size_t len)
         assert(discover->ip.protocol == IPPROTO_UDP);
         assert(discover->ip.saddr == INADDR_ANY);
         assert(discover->ip.daddr == INADDR_BROADCAST);
-        assert(discover->udp.source == ntohs(DHCP_PORT_CLIENT));
-        assert(discover->udp.dest == ntohs(DHCP_PORT_SERVER));
+        assert(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
+        assert(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
 
         ip_check = discover->ip.check;
 
@@ -172,6 +179,14 @@ int dhcp_network_send_raw_packet(int index, const void *packet, size_t len)
         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;
@@ -188,6 +203,9 @@ static void test_discover_message(sd_event *e)
         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[])

commit d3d8ac2f2bac721d99f893c0a0128d21db636d4c
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:25 2013 +0200

    dhcp: Add timeout and main loop support
    
    Require a main loop to be set when creating a DHCP client. Set up
    a timer to resend DHCP Discover messages and add a 0-2 second
    delay to the timeout value. Move to state Selecting after successful
    sending of a Discover message.

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index bd28824..e9c4700 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -34,12 +34,15 @@
 
 struct sd_dhcp_client {
         DHCPState state;
+        sd_event *event;
+        sd_event_source *timeout_resend;
         int index;
         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;
 };
 
 static const uint8_t default_req_opts[] = {
@@ -125,6 +128,8 @@ static int client_stop(sd_dhcp_client *client, int error)
         assert_return(client->state != DHCP_STATE_INIT &&
                       client->state != DHCP_STATE_INIT_REBOOT, -EALREADY);
 
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
         switch (client->state) {
 
         case DHCP_STATE_INIT:
@@ -278,8 +283,61 @@ static int client_send_discover(sd_dhcp_client *client, uint16_t secs)
         return 0;
 }
 
+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;
+
+        switch (client->state) {
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+
+                if (!client->start_time)
+                        client->start_time = usec;
+
+                secs = (usec - client->start_time) / USEC_PER_SEC;
+
+                next_timeout = usec + 2 * USEC_PER_SEC + (random() & 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;
+
+                if (client_send_discover(client, secs) >= 0)
+                        client->state = DHCP_STATE_SELECTING;
+
+                break;
+
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_REQUESTING:
+        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;
+}
+
 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 ||
@@ -287,7 +345,18 @@ int sd_dhcp_client_start(sd_dhcp_client *client)
 
         client->xid = random_u();
 
-        return client_send_discover(client, 0);
+        err = sd_event_add_monotonic(client->event, now(CLOCK_MONOTONIC), 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)
@@ -295,14 +364,17 @@ int sd_dhcp_client_stop(sd_dhcp_client *client)
         return client_stop(client, 0);
 }
 
-sd_dhcp_client *sd_dhcp_client_new(void)
+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;
 
diff --git a/src/libsystemd-dhcp/test-dhcp-client.c b/src/libsystemd-dhcp/test-dhcp-client.c
index fdcb6b1..b8a448d 100644
--- a/src/libsystemd-dhcp/test-dhcp-client.c
+++ b/src/libsystemd-dhcp/test-dhcp-client.c
@@ -34,11 +34,11 @@ static struct ether_addr mac_addr = {
         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
 };
 
-static void test_request_basic(void)
+static void test_request_basic(sd_event *e)
 {
         sd_dhcp_client *client;
 
-        client = sd_dhcp_client_new();
+        client = sd_dhcp_client_new(e);
 
         assert(client);
 
@@ -172,12 +172,12 @@ int dhcp_network_send_raw_packet(int index, const void *packet, size_t len)
         return 575;
 }
 
-static void test_discover_message(void)
+static void test_discover_message(sd_event *e)
 {
         sd_dhcp_client *client;
         int res;
 
-        client = sd_dhcp_client_new();
+        client = sd_dhcp_client_new(e);
         assert(client);
 
         assert(sd_dhcp_client_set_index(client, 42) >= 0);
@@ -192,10 +192,15 @@ static void test_discover_message(void)
 
 int main(int argc, char *argv[])
 {
-        test_request_basic();
+        sd_event *e;
+
+        assert(sd_event_new(&e) >= 0);
+
+        test_request_basic(e);
         test_checksum();
 
-        test_discover_message();
+        test_discover_message(e);
+        sd_event_run(e, (uint64_t) -1);
 
         return 0;
 }
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 2d2fdff..5c9c327 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -25,6 +25,8 @@
 #include <netinet/in.h>
 #include <net/ethernet.h>
 
+#include "sd-event.h"
+
 typedef struct sd_dhcp_client sd_dhcp_client;
 
 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
@@ -36,6 +38,6 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
 
 int sd_dhcp_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
-sd_dhcp_client *sd_dhcp_client_new(void);
+sd_dhcp_client *sd_dhcp_client_new(sd_event *event);
 
 #endif

commit 117539f8b76b3517834f4e57e85aeb3a73326a0d
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:24 2013 +0200

    build: Add dependency on libsystemd-bus needed for main loop

diff --git a/Makefile.am b/Makefile.am
index 5828ae1..c933279 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3865,7 +3865,8 @@ libsystemd_dhcp_la_LDFLAGS = \
 	$(AM_LDFLAGS)
 
 libsystemd_dhcp_la_LIBADD = \
-	libsystemd-shared.la
+	libsystemd-shared.la \
+	libsystemd-bus.la
 
 test_dhcp_option_SOURCES = \
 	src/libsystemd-dhcp/dhcp-protocol.h \
@@ -3885,7 +3886,8 @@ test_dhcp_client_SOURCES = \
 	src/libsystemd-dhcp/test-dhcp-client.c
 
 test_dhcp_client_LDADD = \
-	libsystemd-shared.la
+	libsystemd-shared.la \
+	libsystemd-bus.la
 
 tests += \
 	test-dhcp-option \

commit bbdf06d9f242e2c5a699c3c5820bf6e09e8d174d
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:23 2013 +0200

    dhcp: Add function to stop the DHCP client
    
    The client is stopped and brought back to its initial state.

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index 181c6f8..bd28824 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -119,6 +119,33 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
         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);
+
+        switch (client->state) {
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+
+                client->state = DHCP_STATE_INIT;
+                break;
+
+        case DHCP_STATE_INIT_REBOOT:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_REQUESTING:
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+
+                break;
+        }
+
+        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)
@@ -263,6 +290,11 @@ int sd_dhcp_client_start(sd_dhcp_client *client)
         return client_send_discover(client, 0);
 }
 
+int sd_dhcp_client_stop(sd_dhcp_client *client)
+{
+        return client_stop(client, 0);
+}
+
 sd_dhcp_client *sd_dhcp_client_new(void)
 {
         sd_dhcp_client *client;
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index acfa3fa..2d2fdff 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -34,6 +34,7 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index);
 int sd_dhcp_client_set_mac(sd_dhcp_client *client,
                            const struct ether_addr *addr);
 
+int sd_dhcp_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
 sd_dhcp_client *sd_dhcp_client_new(void);
 

commit f5a70de7a9a9e8a37ba96f89ad75fcc31336bfa3
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:22 2013 +0200

    dhcp: Support seconds elapsed since start of DHCP negotiation
    
    It was noticed by Grant Erickson in ConnMan commit
    95e15c09350acf58d4707056ae2614570883ef66 that:
    
       "Certain DHCP servers, such as that implemented in Mac OS X
        (< 10.7) for its "Internet Sharing" feature, refuse to issue
        a DHCP lease to clients that have not set a non-zero value
        in their DISCOVER or REQUEST packets."

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index 4d1722f..181c6f8 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -120,8 +120,8 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
 }
 
 static int client_packet_init(sd_dhcp_client *client, uint8_t type,
-                              DHCPMessage *message, uint8_t **opt,
-                              size_t *optlen)
+                              DHCPMessage *message, uint16_t secs,
+                              uint8_t **opt, size_t *optlen)
 {
         int err;
 
@@ -136,6 +136,10 @@ static int client_packet_init(sd_dhcp_client *client, uint8_t type,
         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;
@@ -189,7 +193,7 @@ static uint16_t client_checksum(void *buf, int len)
         return ~((sum & 0xffff) + (sum >> 16));
 }
 
-static int client_send_discover(sd_dhcp_client *client)
+static int client_send_discover(sd_dhcp_client *client, uint16_t secs)
 {
         int err = 0;
         _cleanup_free_ DHCPPacket *discover;
@@ -205,7 +209,7 @@ static int client_send_discover(sd_dhcp_client *client)
                 return -ENOMEM;
 
         err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
-                                 &opt, &optlen);
+                                 secs, &opt, &optlen);
         if (err < 0)
                 return err;
 
@@ -256,7 +260,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client)
 
         client->xid = random_u();
 
-        return client_send_discover(client);
+        return client_send_discover(client, 0);
 }
 
 sd_dhcp_client *sd_dhcp_client_new(void)

commit 290c7324ca6ed22bcb6f4d644765aefeb47dc9de
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:21 2013 +0200

    dhcp: Add test for discover DHCP packet creation
    
    Set a fake MAC address and emulate raw packet sending. When the buffer
    containing the Discover message is received, check selected IP and
    UDP headers and compute IP header and UDP message checksums. Also
    send the DHCP message for option parsing and expect a successful
    outcome.

diff --git a/Makefile.am b/Makefile.am
index 7c15514..5828ae1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3880,6 +3880,8 @@ test_dhcp_client_SOURCES = \
 	src/libsystemd-dhcp/dhcp-protocol.h \
 	src/systemd/sd-dhcp-client.h \
 	src/libsystemd-dhcp/dhcp-client.c \
+	src/libsystemd-dhcp/dhcp-internal.h \
+	src/libsystemd-dhcp/dhcp-option.c \
 	src/libsystemd-dhcp/test-dhcp-client.c
 
 test_dhcp_client_LDADD = \
diff --git a/src/libsystemd-dhcp/test-dhcp-client.c b/src/libsystemd-dhcp/test-dhcp-client.c
index d097c7d..fdcb6b1 100644
--- a/src/libsystemd-dhcp/test-dhcp-client.c
+++ b/src/libsystemd-dhcp/test-dhcp-client.c
@@ -24,9 +24,16 @@
 #include <errno.h>
 #include <stdio.h>
 
+#include "util.h"
+
 #include "dhcp-protocol.h"
+#include "dhcp-internal.h"
 #include "sd-dhcp-client.h"
 
+static struct ether_addr mac_addr = {
+        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+};
+
 static void test_request_basic(void)
 {
         sd_dhcp_client *client;
@@ -112,10 +119,83 @@ static void test_checksum(void)
         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_packet(int index, const void *packet, size_t len)
+{
+        size_t size;
+        _cleanup_free_ DHCPPacket *discover;
+        uint16_t ip_check, udp_check;
+        int res;
+
+        assert(index == 42);
+        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 == ntohs(DHCP_PORT_CLIENT));
+        assert(discover->udp.dest == ntohs(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;
+}
+
+static void test_discover_message(void)
+{
+        sd_dhcp_client *client;
+        int res;
+
+        client = sd_dhcp_client_new();
+        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);
+}
+
 int main(int argc, char *argv[])
 {
         test_request_basic();
         test_checksum();
 
+        test_discover_message();
+
         return 0;
 }

commit e1c244dea0248116a61152b3433981f1b90b2273
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:20 2013 +0200

    build: Add libsystemd-dhcp

diff --git a/Makefile.am b/Makefile.am
index 6d1e27b..7c15514 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3847,6 +3847,26 @@ lib_LTLIBRARIES += \
 endif
 
 # ------------------------------------------------------------------------------
+libsystemd_dhcp_la_SOURCES = \
+	src/systemd/sd-dhcp-client.h \
+	src/libsystemd-dhcp/dhcp-protocol.h \
+	src/libsystemd-dhcp/dhcp-internal.h \
+	src/libsystemd-dhcp/dhcp-network.c \
+	src/libsystemd-dhcp/dhcp-option.c \
+	src/libsystemd-dhcp/dhcp-client.c
+
+noinst_LTLIBRARIES += \
+	libsystemd-dhcp.la
+
+libsystemd_dhcp_la_CFLAGS = \
+	$(AM_CFLAGS)
+
+libsystemd_dhcp_la_LDFLAGS = \
+	$(AM_LDFLAGS)
+
+libsystemd_dhcp_la_LIBADD = \
+	libsystemd-shared.la
+
 test_dhcp_option_SOURCES = \
 	src/libsystemd-dhcp/dhcp-protocol.h \
 	src/libsystemd-dhcp/dhcp-internal.h \

commit 46a66b794a00e0f34981ac25dbf2eae2423b6215
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:19 2013 +0200

    dhcp: Add DHCP discover sending
    
    On starting the client, use the supplied interface mac address and create
    a transaction id. Puzzle together an IP/UDP/DHCP Discover message, compute
    checksums and send it out as a raw packet.
    
    Create an additional function that constructs default options common to
    all DHCP messages.
    
    Set the DHCP Client ID option as noticed by Grant Erickson in ConnMan
    commit b18d9798b3a0ae46ed87d6d2be8d5a474bf3ab1e:
    
       "Some Internet gateways and Wi-Fi access points are unhappy when the
        DHCPv4 client-id option (61) is missing and will refuse to issue a
        DHCP lease."

diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
index 85c8040..4d1722f 100644
--- a/src/libsystemd-dhcp/dhcp-client.c
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -21,19 +21,25 @@
 #include <errno.h>
 #include <string.h>
 #include <stdio.h>
+#include <net/ethernet.h>
 
 #include "util.h"
 #include "list.h"
 
 #include "dhcp-protocol.h"
+#include "dhcp-internal.h"
 #include "sd-dhcp-client.h"
 
+#define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
+
 struct sd_dhcp_client {
         DHCPState state;
         int index;
         uint8_t *req_opts;
         size_t req_opts_size;
         uint32_t last_addr;
+        struct ether_addr mac_addr;
+        uint32_t xid;
 };
 
 static const uint8_t default_req_opts[] = {
@@ -102,6 +108,157 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int 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;
+}
+
+static int client_packet_init(sd_dhcp_client *client, uint8_t type,
+                              DHCPMessage *message, uint8_t **opt,
+                              size_t *optlen)
+{
+        int err;
+
+        *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);
+
+        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;
+        }
+
+        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 int client_send_discover(sd_dhcp_client *client)
+{
+        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,
+                                 &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;
+
+        discover->ip.version = IPVERSION;
+        discover->ip.ihl = sizeof(discover->ip) >> 2;
+        discover->ip.tot_len = htobe16(len);
+
+        discover->ip.protocol = IPPROTO_UDP;
+        discover->ip.saddr = INADDR_ANY;
+        discover->ip.daddr = INADDR_BROADCAST;
+
+        discover->udp.source = htobe16(DHCP_PORT_CLIENT);
+        discover->udp.dest = htobe16(DHCP_PORT_SERVER);
+        discover->udp.len = htobe16(len - sizeof(discover->ip));
+
+        discover->ip.check = discover->udp.len;
+        discover->udp.check = client_checksum(&discover->ip.ttl,
+                                              len - 8);
+
+        discover->ip.ttl = IPDEFTTL;
+        discover->ip.check = 0;
+        discover->ip.check = client_checksum(&discover->ip,
+                                             sizeof(discover->ip));
+
+        err = dhcp_network_send_raw_packet(client->index, discover, len);
+
+        return 0;
+}
+
+int sd_dhcp_client_start(sd_dhcp_client *client)
+{
+        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();
+
+        return client_send_discover(client);
+}
+
 sd_dhcp_client *sd_dhcp_client_new(void)
 {
         sd_dhcp_client *client;
diff --git a/src/libsystemd-dhcp/dhcp-protocol.h b/src/libsystemd-dhcp/dhcp-protocol.h
index d375467..fa077fb 100644
--- a/src/libsystemd-dhcp/dhcp-protocol.h
+++ b/src/libsystemd-dhcp/dhcp-protocol.h
@@ -55,6 +55,11 @@ struct DHCPPacket {
 
 typedef struct DHCPPacket DHCPPacket;
 
+enum {
+        DHCP_PORT_SERVER                        = 67,
+        DHCP_PORT_CLIENT                        = 68,
+};
+
 enum DHCPState {
         DHCP_STATE_INIT                         = 0,
         DHCP_STATE_SELECTING                    = 1,
@@ -100,5 +105,6 @@ enum {
         DHCP_OPTION_OVERLOAD                    = 52,
         DHCP_OPTION_MESSAGE_TYPE                = 53,
         DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
+        DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
         DHCP_OPTION_END                         = 255,
 };
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 9b19a1d..acfa3fa 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -23,6 +23,7 @@
 ***/
 
 #include <netinet/in.h>
+#include <net/ethernet.h>
 
 typedef struct sd_dhcp_client sd_dhcp_client;
 
@@ -30,7 +31,10 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
                                        const struct in_addr *last_address);
 int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index);
+int sd_dhcp_client_set_mac(sd_dhcp_client *client,
+                           const struct ether_addr *addr);
 
+int sd_dhcp_client_start(sd_dhcp_client *client);
 sd_dhcp_client *sd_dhcp_client_new(void);
 
 #endif

commit 8b4a96932de0c56048fbd7f7386090dc202704f7
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:18 2013 +0200

    dhcp: Add function for sending a raw packet
    
    Open a packet socket, create a link level header, send packet and
    close socket. Adding it to a separate file makes testing of the
    DHCP sending much easier, as the test program can supply any socket
    to the DHCP client code.

diff --git a/src/libsystemd-dhcp/dhcp-internal.h b/src/libsystemd-dhcp/dhcp-internal.h
index 1f9c9d2..814a3cd 100644
--- a/src/libsystemd-dhcp/dhcp-internal.h
+++ b/src/libsystemd-dhcp/dhcp-internal.h
@@ -25,6 +25,8 @@
 
 #include "dhcp-protocol.h"
 
+int dhcp_network_send_raw_packet(int index, 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);
 
diff --git a/src/libsystemd-dhcp/dhcp-network.c b/src/libsystemd-dhcp/dhcp-network.c
new file mode 100644
index 0000000..ed34228
--- /dev/null
+++ b/src/libsystemd-dhcp/dhcp-network.c
@@ -0,0 +1,55 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "socket-util.h"
+
+#include "dhcp-internal.h"
+
+int dhcp_network_send_raw_packet(int index, const void *packet, size_t len)
+{
+        _cleanup_close_ int s;
+        union sockaddr_union link = {};
+
+        s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 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)
+                return -errno;
+
+        if (sendto(s, packet, len, 0, &link.sa, sizeof(link.ll)) < 0)
+                return -errno;
+
+        return 0;
+}

commit e88bc7958c02305968f2d22dd4455b50fbf911f0
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:17 2013 +0200

    shared: Add struct sockaddr_ll to sockaddr_union

diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
index d42a2fe..84ebc30 100644
--- a/src/shared/socket-util.h
+++ b/src/shared/socket-util.h
@@ -27,6 +27,7 @@
 #include <net/if.h>
 #include <asm/types.h>
 #include <linux/netlink.h>
+#include <linux/if_packet.h>
 
 #include "macro.h"
 #include "util.h"
@@ -38,6 +39,7 @@ union sockaddr_union {
         struct sockaddr_un un;
         struct sockaddr_nl nl;
         struct sockaddr_storage storage;
+        struct sockaddr_ll ll;
 };
 
 typedef struct SocketAddress {

commit 39b7f5960044511b72b40853d1c6c64e5d618b1b
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:16 2013 +0200

    dhcp: Add test function for computing checksum

diff --git a/src/libsystemd-dhcp/test-dhcp-client.c b/src/libsystemd-dhcp/test-dhcp-client.c
index 863f196..d097c7d 100644
--- a/src/libsystemd-dhcp/test-dhcp-client.c
+++ b/src/libsystemd-dhcp/test-dhcp-client.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <assert.h>
 #include <errno.h>
+#include <stdio.h>
 
 #include "dhcp-protocol.h"
 #include "sd-dhcp-client.h"
@@ -73,9 +74,48 @@ static void test_request_basic(void)
         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);
+}
+
 int main(int argc, char *argv[])
 {
         test_request_basic();
+        test_checksum();
 
         return 0;
 }

commit d8b61a1dc9153c6f22c923b303b6235ff55122a3
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:15 2013 +0200

    dhcp: Add option append tests
    
    Add checks for invalid lengths and parameters when using the option
    appending function. Add also checks for adding options, see to it
    that the resulting array is identical to the array of options added.

diff --git a/src/libsystemd-dhcp/test-dhcp-option.c b/src/libsystemd-dhcp/test-dhcp-option.c
index df717af..e2a6800 100644
--- a/src/libsystemd-dhcp/test-dhcp-option.c
+++ b/src/libsystemd-dhcp/test-dhcp-option.c
@@ -289,6 +289,76 @@ static void test_options(struct option_desc *desc)
                 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;
@@ -301,5 +371,7 @@ int main(int argc, char *argv[])
         for (i = 0; i < ELEMENTSOF(option_tests); i++)
                 test_options(&option_tests[i]);
 
+        test_option_set();
+
         return 0;
 }

commit a10c375e02da66efec40e28142bc22fd8955e968
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:14 2013 +0200

    dhcp: Add tests for DHCP options, file and sname fields
    
    Add a structure describing the DHCP file, sname and trailing options
    fields. Create a messge holding these fields and call the internal
    option parsing function.
    
    In the test callback function verify that only regular options are
    passed and figure out which part of the DHCP message is the one that
    is being processed. As the test program knows the full contents of
    the test options in the test structure, skip all non-regular fields
    and verify that the option provided to the callback indeed is the
    one expected. Check also if non-regular option fields are to be
    ignored in the end of the option field as the callback is not called
    again and the final check when the whole message has been processed
    needs to be successful.
    
    Add a boolean flag for pretty-printing, anticipate there will be a
    nice option to toggle it in the future.

diff --git a/src/libsystemd-dhcp/test-dhcp-option.c b/src/libsystemd-dhcp/test-dhcp-option.c
index cde8c29..df717af 100644
--- a/src/libsystemd-dhcp/test-dhcp-option.c
+++ b/src/libsystemd-dhcp/test-dhcp-option.c
@@ -5,11 +5,74 @@
 #include <string.h>
 #include <assert.h>
 
-#include <util.h>
+#include "util.h"
+#include "macro.h"
 
 #include "dhcp-protocol.h"
 #include "dhcp-internal.h"
 
+struct option_desc {
+        uint8_t sname[64];
+        int snamelen;
+        uint8_t file[128];
+        int filelen;
+        uint8_t options[128];
+        int len;
+        bool success;
+        int filepos;
+        int snamepos;
+        int pos;
+};
+
+static bool verbose = false;
+
+static struct option_desc option_tests[] = {
+        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
+        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
+                          DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
+        { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
+        { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
+                          0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
+                          0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
+                          0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
+                          0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
+          40, true, },
+        { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
+                          42, 3, 0, 0, 0 }, 8, true, },
+        { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
+
+        { {}, 0,
+          { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
+          { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
+
+        { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
+          { 222, 3, 1, 2, 3 }, 5,
+          { DHCP_OPTION_OVERLOAD, 1,
+            DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
+};
+
+static const char *dhcp_type(int type)
+{
+        switch(type) {
+        case DHCP_DISCOVER:
+                return "DHCPDISCOVER";
+        case DHCP_OFFER:
+                return "DHCPOFFER";
+        case DHCP_REQUEST:
+                return "DHCPREQUEST";
+        case DHCP_DECLINE:
+                return "DHCPDECLINE";
+        case DHCP_ACK:
+                return "DHCPACK";
+        case DHCP_NAK:
+                return "DHCPNAK";
+        case DHCP_RELEASE:
+                return "DHCPRELEASE";
+        default:
+                return "unknown";
+        }
+}
+
 static void test_invalid_buffer_length(void)
 {
         DHCPMessage message;
@@ -21,7 +84,7 @@ static void test_invalid_buffer_length(void)
 
 static void test_cookie(void)
 {
-        DHCPMessage *message;
+        _cleanup_free_ DHCPMessage *message;
         size_t len = sizeof(DHCPMessage) + 4;
         uint8_t *opt;
 
@@ -38,14 +101,205 @@ static void test_cookie(void)
         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;
 
-        free(message);
+        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));
 }
 
 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]);
+
         return 0;
 }

commit 78628cd27355a157bf44df1cb91f782150a7ca20
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:13 2013 +0200

    build: Add DHCP option test

diff --git a/Makefile.am b/Makefile.am
index 9b5c496..6d1e27b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3847,8 +3847,17 @@ lib_LTLIBRARIES += \
 endif
 
 # ------------------------------------------------------------------------------
+test_dhcp_option_SOURCES = \
+	src/libsystemd-dhcp/dhcp-protocol.h \
+	src/libsystemd-dhcp/dhcp-internal.h \
+	src/libsystemd-dhcp/dhcp-option.c \
+	src/libsystemd-dhcp/test-dhcp-option.c
+
+test_dhcp_option_LDADD = \
+	libsystemd-shared.la
+
 test_dhcp_client_SOURCES = \
-	src/libsystemd-dhcp/protocol.h \
+	src/libsystemd-dhcp/dhcp-protocol.h \
 	src/systemd/sd-dhcp-client.h \
 	src/libsystemd-dhcp/dhcp-client.c \
 	src/libsystemd-dhcp/test-dhcp-client.c
@@ -3857,6 +3866,7 @@ test_dhcp_client_LDADD = \
 	libsystemd-shared.la
 
 tests += \
+	test-dhcp-option \
 	test-dhcp-client
 
 # ------------------------------------------------------------------------------

commit a0ae95c9be347c1e7fbfe4977f35bfd92775eeb6
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:12 2013 +0200

    dhcp: Add buffer length and invalid cookie tests for DHCP options
    
    Create an initial simple test program for these two cases.

diff --git a/src/libsystemd-dhcp/test-dhcp-option.c b/src/libsystemd-dhcp/test-dhcp-option.c
new file mode 100644
index 0000000..cde8c29
--- /dev/null
+++ b/src/libsystemd-dhcp/test-dhcp-option.c
@@ -0,0 +1,51 @@
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include <util.h>
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+
+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)
+{
+        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);
+
+        free(message);
+}
+
+int main(int argc, char *argv[])
+{
+        test_invalid_buffer_length();
+        test_cookie();
+
+        return 0;
+}

commit 524cf45894e8219bb66caca58a451bffdc5167ad
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:11 2013 +0200

    dhcp: Add option appending and parsing
    
    Add functions to append and parse DHCP options. Not all options
    are passed to the callback function, the ones not exposed are
    pad, end, message type and overload. If indicated by the overload
    option, file and sname fields will be examined for more options.
    
    The option functions are internal to DHCP, add a new header files
    for interal function prototypes.

diff --git a/src/libsystemd-dhcp/dhcp-internal.h b/src/libsystemd-dhcp/dhcp-internal.h
new file mode 100644
index 0000000..1f9c9d2
--- /dev/null
+++ b/src/libsystemd-dhcp/dhcp-internal.h
@@ -0,0 +1,34 @@
+/*-*- 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 "dhcp-protocol.h"
+
+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/dhcp-option.c b/src/libsystemd-dhcp/dhcp-option.c
new file mode 100644
index 0000000..4d45b3b
--- /dev/null
+++ b/src/libsystemd-dhcp/dhcp-option.c
@@ -0,0 +1,184 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "dhcp-internal.h"
+
+int dhcp_option_append(uint8_t **buf, size_t *buflen, uint8_t code,
+                       size_t optlen, const void *optval)
+{
+        if (!buf || !buflen)
+                return -EINVAL;
+
+        switch (code) {
+
+        case DHCP_OPTION_PAD:
+        case DHCP_OPTION_END:
+                if (*buflen < 1)
+                        return -ENOBUFS;
+
+                (*buf)[0] = code;
+                *buf += 1;
+                *buflen -= 1;
+                break;
+
+        default:
+                if (*buflen < optlen + 2)
+                        return -ENOBUFS;
+
+                if (!optval)
+                        return -EINVAL;
+
+                (*buf)[0] = code;
+                (*buf)[1] = optlen;
+                memcpy(&(*buf)[2], optval, optlen);
+
+                *buf += optlen + 2;
+                *buflen -= (optlen + 2);
+
+                break;
+        }
+
+        return 0;
+}
+
+static int parse_options(const uint8_t *buf, size_t buflen, uint8_t *overload,
+                         uint8_t *message_type, dhcp_option_cb_t cb,
+                         void *user_data)
+{
+        const uint8_t *code = buf;
+        const uint8_t *len;
+
+        while (buflen > 0) {
+                switch (*code) {
+                case DHCP_OPTION_PAD:
+                        buflen -= 1;
+                        code++;
+                        break;
+
+                case DHCP_OPTION_END:
+                        return 0;
+
+                case DHCP_OPTION_MESSAGE_TYPE:
+                        if (buflen < 3)
+                                return -ENOBUFS;
+                        buflen -= 3;
+
+                        len = code + 1;
+                        if (*len != 1)
+                                return -EINVAL;
+
+                        if (message_type)
+                                *message_type = *(len + 1);
+
+                        code += 3;
+
+                        break;
+
+                case DHCP_OPTION_OVERLOAD:
+                        if (buflen < 3)
+                                return -ENOBUFS;
+                        buflen -= 3;
+
+                        len = code + 1;
+                        if (*len != 1)
+                                return -EINVAL;
+
+                        if (overload)
+                                *overload = *(len + 1);
+
+                        code += 3;
+
+                        break;
+
+                default:
+                        if (buflen < 3)
+                                return -ENOBUFS;
+
+                        len = code + 1;
+
+                        if (buflen < (size_t)*len + 2)
+                                return -EINVAL;
+                        buflen -= *len + 2;
+
+                        if (cb)
+                                cb(*code, *len, len + 1, user_data);
+
+                        code += *len + 2;
+
+                        break;
+                }
+        }
+
+        if (buflen)
+                return -EINVAL;
+
+        return 0;
+}
+
+int dhcp_option_parse(DHCPMessage *message, size_t len,
+                      dhcp_option_cb_t cb, void *user_data)
+{
+        uint8_t overload = 0;
+        uint8_t message_type = 0;
+        uint8_t *opt = (uint8_t *)(message + 1);
+        int res;
+
+        if (!message)
+                return -EINVAL;
+
+        if (len < sizeof(DHCPMessage) + 4)
+                return -EINVAL;
+
+        len -= sizeof(DHCPMessage) + 4;
+
+        if (opt[0] != 0x63 && opt[1] != 0x82 && opt[2] != 0x53 &&
+                        opt[3] != 0x63)
+                return -EINVAL;
+
+        res = parse_options(&opt[4], len, &overload, &message_type,
+                        cb, user_data);
+        if (res < 0)
+                return res;
+
+        if (overload & DHCP_OVERLOAD_FILE) {
+                res = parse_options(message->file, sizeof(message->file),
+                                NULL, &message_type, cb, user_data);
+                if (res < 0)
+                        return res;
+        }
+
+        if (overload & DHCP_OVERLOAD_SNAME) {
+                res = parse_options(message->sname, sizeof(message->sname),
+                                NULL, &message_type, cb, user_data);
+                if (res < 0)
+                        return res;
+        }
+
+        if (message_type)
+                return message_type;
+
+        return -ENOMSG;
+}

commit b5d01d174c5cb3d2ae49875d3bc1f233c5412120
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:10 2013 +0200

    build: Add initial build support
    
    The client test program is the only one to be built so far.

diff --git a/Makefile.am b/Makefile.am
index 25bbcab..9b5c496 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3847,6 +3847,19 @@ lib_LTLIBRARIES += \
 endif
 
 # ------------------------------------------------------------------------------
+test_dhcp_client_SOURCES = \
+	src/libsystemd-dhcp/protocol.h \
+	src/systemd/sd-dhcp-client.h \
+	src/libsystemd-dhcp/dhcp-client.c \
+	src/libsystemd-dhcp/test-dhcp-client.c
+
+test_dhcp_client_LDADD = \
+	libsystemd-shared.la
+
+tests += \
+	test-dhcp-client
+
+# ------------------------------------------------------------------------------
 if ENABLE_MACHINED
 systemd_machined_SOURCES = \
 	src/machine/machined.c \
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

commit be391925d52bde04ea9b14f868a401d545c67af9
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:09 2013 +0200

    dhcp: Add test for DHCP client initialization and parameter setting

diff --git a/src/libsystemd-dhcp/test-dhcp-client.c b/src/libsystemd-dhcp/test-dhcp-client.c
new file mode 100644
index 0000000..863f196
--- /dev/null
+++ b/src/libsystemd-dhcp/test-dhcp-client.c
@@ -0,0 +1,81 @@
+/*-*- 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 "dhcp-protocol.h"
+#include "sd-dhcp-client.h"
+
+static void test_request_basic(void)
+{
+        sd_dhcp_client *client;
+
+        client = sd_dhcp_client_new();
+
+        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);
+}
+
+int main(int argc, char *argv[])
+{
+        test_request_basic();
+
+        return 0;
+}

commit 011feef852de96a1adaba476037ce01b5efefc35
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:08 2013 +0200

    dhcp: Add DHCP client initialization
    
    Provide functionality for initializing a DHCP client struct, setting
    interface index, last used address and additional options to request.
    On initialization the most useful options are added by default.

diff --git a/src/dhcp/protocol.h b/src/dhcp/protocol.h
deleted file mode 100644
index d375467..0000000
--- a/src/dhcp/protocol.h
+++ /dev/null
@@ -1,104 +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;
-
-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_OVERLOAD                    = 52,
-        DHCP_OPTION_MESSAGE_TYPE                = 53,
-        DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
-        DHCP_OPTION_END                         = 255,
-};
diff --git a/src/libsystemd-dhcp/dhcp-client.c b/src/libsystemd-dhcp/dhcp-client.c
new file mode 100644
index 0000000..85c8040
--- /dev/null
+++ b/src/libsystemd-dhcp/dhcp-client.c
@@ -0,0 +1,125 @@
+/***
+  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 "util.h"
+#include "list.h"
+
+#include "dhcp-protocol.h"
+#include "sd-dhcp-client.h"
+
+struct sd_dhcp_client {
+        DHCPState state;
+        int index;
+        uint8_t *req_opts;
+        size_t req_opts_size;
+        uint32_t last_addr;
+};
+
+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_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;
+}
+
+sd_dhcp_client *sd_dhcp_client_new(void)
+{
+        sd_dhcp_client *client;
+
+        client = new0(sd_dhcp_client, 1);
+        if (!client)
+                return NULL;
+
+        client->state = DHCP_STATE_INIT;
+        client->index = -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/dhcp-protocol.h b/src/libsystemd-dhcp/dhcp-protocol.h
new file mode 100644
index 0000000..d375467
--- /dev/null
+++ b/src/libsystemd-dhcp/dhcp-protocol.h
@@ -0,0 +1,104 @@
+/*-*- 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;
+
+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_OVERLOAD                    = 52,
+        DHCP_OPTION_MESSAGE_TYPE                = 53,
+        DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
+        DHCP_OPTION_END                         = 255,
+};
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
new file mode 100644
index 0000000..9b19a1d
--- /dev/null
+++ b/src/systemd/sd-dhcp-client.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddhcpclienthfoo
+#define foosddhcpclienthfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2013 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+
+typedef struct sd_dhcp_client sd_dhcp_client;
+
+int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
+int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
+                                       const struct in_addr *last_address);
+int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index);
+
+sd_dhcp_client *sd_dhcp_client_new(void);
+
+#endif

commit 5f404b1e680cdac9f8149e73296d1d89044af773
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Mon Dec 9 23:43:07 2013 +0200

    dhcp: Add DHCP protocol structures and initial defines
    
    Create a new directory to host DHCP components.

diff --git a/src/dhcp/protocol.h b/src/dhcp/protocol.h
new file mode 100644
index 0000000..d375467
--- /dev/null
+++ b/src/dhcp/protocol.h
@@ -0,0 +1,104 @@
+/*-*- 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;
+
+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_OVERLOAD                    = 52,
+        DHCP_OPTION_MESSAGE_TYPE                = 53,
+        DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
+        DHCP_OPTION_END                         = 255,
+};



More information about the systemd-commits mailing list