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

Patrik Flykt pflykt at kemper.freedesktop.org
Thu Jun 19 05:50:51 PDT 2014


 Makefile.am                                   |   38 -
 src/libsystemd-network/dhcp6-internal.h       |   79 ++
 src/libsystemd-network/dhcp6-lease-internal.h |   57 +
 src/libsystemd-network/dhcp6-network.c        |  194 +++++
 src/libsystemd-network/dhcp6-option.c         |  313 ++++++++
 src/libsystemd-network/dhcp6-protocol.h       |  124 +++
 src/libsystemd-network/network-internal.c     |   26 
 src/libsystemd-network/network-internal.h     |    1 
 src/libsystemd-network/sd-dhcp6-client.c      |  978 ++++++++++++++++++++++++++
 src/libsystemd-network/sd-dhcp6-lease.c       |  178 ++++
 src/libsystemd-network/sd-icmp6-nd.c          |  325 ++++++++
 src/libsystemd-network/test-dhcp6-client.c    |  581 +++++++++++++++
 src/libsystemd-network/test-icmp6-rs.c        |  155 ++++
 src/network/networkd-link.c                   |  158 ++++
 src/network/networkd-network-gperf.gperf      |    1 
 src/network/networkd.h                        |    6 
 src/systemd/sd-dhcp6-client.h                 |   61 +
 src/systemd/sd-dhcp6-lease.h                  |   42 +
 src/systemd/sd-icmp6-nd.h                     |   56 +
 19 files changed, 3364 insertions(+), 9 deletions(-)

New commits:
commit 4138fb2c7936758da709eaed3f6b4f3df1d04eff
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:40:01 2014 +0300

    networkd: Add initial DHCPv6 support
    
    Enable DHCPv6 support by creating a DHCPv6 boolean in the Network
    section. Add necessary DHCPv6 structures and initial function calls.

diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 5b2dabc..5ab7aa7 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -125,6 +125,8 @@ static void link_free(Link *link) {
 
         sd_ipv4ll_unref(link->ipv4ll);
 
+        sd_dhcp6_client_unref(link->dhcp6_client);
+
         hashmap_remove(link->manager->links, &link->ifindex);
 
         free(link->ifname);
@@ -233,6 +235,16 @@ static int link_stop_clients(Link *link) {
                 }
         }
 
+        if (link->network->dhcp6) {
+                assert(link->dhcp6_client);
+
+                k = sd_dhcp6_client_stop(link->dhcp6_client);
+                if (k < 0) {
+                        log_warning_link(link, "Could not stop DHCPv6 client: %s", strerror(-r));
+                        r = k;
+                }
+        }
+
         return r;
 }
 
@@ -1277,6 +1289,104 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){
         }
 }
 
+static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
+        Link *link = userdata;
+
+        assert(link);
+        assert(link->network);
+        assert(link->manager);
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return;
+
+        switch(event) {
+        case DHCP6_EVENT_STOP:
+        case DHCP6_EVENT_RESEND_EXPIRE:
+        case DHCP6_EVENT_RETRANS_MAX:
+        case DHCP6_EVENT_IP_ACQUIRE:
+                log_debug_link(link, "DHCPv6 event %d", event);
+
+                break;
+
+        default:
+                if (event < 0)
+                        log_warning_link(link, "DHCPv6 error: %s",
+                                         strerror(-event));
+                else
+                        log_warning_link(link, "DHCPv6 unknown event: %d",
+                                         event);
+                return;
+        }
+}
+
+static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
+        Link *link = userdata;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(link->manager);
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return;
+
+        switch(event) {
+        case ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE:
+        case ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER:
+                return;
+
+        case ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
+        case ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED:
+                break;
+
+        default:
+                if (event < 0)
+                        log_warning_link(link, "ICMPv6 error: %s",
+                                         strerror(-event));
+                else
+                        log_warning_link(link, "ICMPv6 unknown event: %d",
+                                         event);
+
+                return;
+        }
+
+        if (link->dhcp6_client)
+                return;
+
+        r = sd_dhcp6_client_new(&link->dhcp6_client);
+        if (r < 0)
+                return;
+
+        r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0);
+        if (r < 0) {
+                link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
+                return;
+        }
+
+        r = sd_dhcp6_client_set_mac(link->dhcp6_client, &link->mac);
+        if (r < 0) {
+                link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
+                return;
+        }
+
+        r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex);
+        if (r < 0) {
+                link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
+                return;
+        }
+
+        r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler,
+                                         link);
+        if (r < 0) {
+                link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
+                return;
+        }
+
+        r = sd_dhcp6_client_start(link->dhcp6_client);
+        if (r < 0)
+                link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
+}
+
 static int link_acquire_conf(Link *link) {
         int r;
 
@@ -1311,6 +1421,18 @@ static int link_acquire_conf(Link *link) {
                 }
         }
 
+        if (link->network->dhcp6) {
+                assert(link->icmp6_router_discovery);
+
+                log_debug_link(link, "discovering IPv6 routers");
+
+                r = sd_icmp6_router_solicitation_start(link->icmp6_router_discovery);
+                if (r < 0) {
+                        log_warning_link(link, "could not start IPv6 router discovery");
+                        return r;
+                }
+        }
+
         return 0;
 }
 
@@ -1793,6 +1915,32 @@ static int link_configure(Link *link) {
                         return r;
         }
 
+        if (link->network->dhcp6) {
+                r = sd_icmp6_nd_new(&link->icmp6_router_discovery);
+                if (r < 0)
+                        return r;
+
+                r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery,
+                                             NULL, 0);
+                if (r < 0)
+                        return r;
+
+                r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery,
+                                        &link->mac);
+                if (r < 0)
+                        return r;
+
+                r = sd_icmp6_nd_set_index(link->icmp6_router_discovery,
+                                          link->ifindex);
+                if (r < 0)
+                        return r;
+
+                r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery,
+                                             icmp6_router_handler, link);
+                if (r < 0)
+                        return r;
+        }
+
         if (link_has_carrier(link->flags, link->kernel_operstate)) {
                 r = link_acquire_conf(link);
                 if (r < 0)
@@ -2134,6 +2282,16 @@ int link_update(Link *link, sd_rtnl_message *m) {
                                         return r;
                                 }
                         }
+
+                        if (link->dhcp6_client) {
+                                r = sd_dhcp6_client_set_mac(link->dhcp6_client,
+                                                            &link->mac);
+                                if (r < 0) {
+                                        log_warning_link(link, "Could not update MAC address in DHCPv6 client: %s",
+                                                         strerror(-r));
+                                        return r;
+                                }
+                        }
                 }
         }
 
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 5e21805..469e028 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -33,6 +33,7 @@ Network.VXLAN,               config_parse_netdev,                0,
 Network.DHCP,                config_parse_bool,                  0,                             offsetof(Network, dhcp)
 Network.DHCPServer,          config_parse_bool,                  0,                             offsetof(Network, dhcp_server)
 Network.IPv4LL,              config_parse_bool,                  0,                             offsetof(Network, ipv4ll)
+Network.DHCPv6,              config_parse_bool,                  0,                             offsetof(Network, dhcp6)
 Network.Address,             config_parse_address,               0,                             0
 Network.Gateway,             config_parse_gateway,               0,                             0
 Network.DNS,                 config_parse_dns,                   0,                             offsetof(Network, dns)
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 7069a11..b7b1d90 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -29,6 +29,8 @@
 #include "sd-dhcp-client.h"
 #include "sd-dhcp-server.h"
 #include "sd-ipv4ll.h"
+#include "sd-icmp6-nd.h"
+#include "sd-dhcp6-client.h"
 #include "udev.h"
 
 #include "rtnl-util.h"
@@ -159,6 +161,7 @@ struct Network {
         bool dhcp_domainname;
         bool dhcp_critical;
         bool ipv4ll;
+        bool dhcp6;
 
         bool dhcp_server;
 
@@ -263,6 +266,9 @@ struct Link {
         LIST_HEAD(Address, pool_addresses);
 
         sd_dhcp_server *dhcp_server;
+
+        sd_icmp6_nd *icmp6_router_discovery;
+        sd_dhcp6_client *dhcp6_client;
 };
 
 struct AddressPool {

commit 947527f8326d3591f252c48fee5426a563f03544
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:58 2014 +0300

    sd-dhcp6-client: Add reply sending for test
    
    Enhance the test case by generating a Reply. With a properly formed
    Reply the callback function will be called and the additional
    earlier event loop exit can now be removed.

diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 40fd8f5..c5729db 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -49,7 +49,6 @@ static int test_index = 42;
 static int test_client_message_num;
 static be32_t test_iaid = 0;
 static uint8_t test_duid[14] = { };
-static sd_event *e_solicit;
 
 static int test_client_basic(sd_event *e) {
         sd_dhcp6_client *client;
@@ -171,6 +170,31 @@ static uint8_t msg_advertise[198] = {
         0x53, 0x00, 0x07, 0x00, 0x01, 0x00
 };
 
+static uint8_t msg_reply[173] = {
+        0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e,
+        0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53,
+        0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01,
+        0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b,
+        0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d,
+        0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d,
+        0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78,
+        0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8,
+        0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3,
+        0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96,
+        0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e,
+        0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64,
+        0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20,
+        0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73,
+        0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17,
+        0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
+        0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c,
+        0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61,
+        0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d,
+        0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01
+};
+
 static int test_advertise_option(sd_event *e) {
         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
         DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
@@ -307,6 +331,20 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
 }
 
 static int test_client_send_reply(DHCP6Message *request) {
+        DHCP6Message reply;
+
+        reply.transaction_id = request->transaction_id;
+        reply.type = DHCP6_REPLY;
+
+        memcpy(msg_reply, &reply.transaction_id, 4);
+
+        memcpy(&msg_reply[26], test_duid, sizeof(test_duid));
+
+        memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid));
+
+        assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply))
+                  == sizeof(msg_reply));
+
         return 0;
 }
 
@@ -380,8 +418,6 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
         assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
                                                   &lt_valid) == -ENOMSG);
 
-        sd_event_exit(e_solicit, 0);
-
         return 0;
 }
 
@@ -482,6 +518,7 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
         sd_event *e = userdata;
 
         assert_se(e);
+        assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
 
         if (verbose)
                 printf("  got DHCPv6 event %d\n", event);
@@ -511,8 +548,6 @@ static int test_client_solicit(sd_event *e) {
                                     time_now + 2 * USEC_PER_SEC, 0,
                                     test_hangcheck, NULL) >= 0);
 
-        e_solicit = e;
-
         assert_se(sd_dhcp6_client_start(client) >= 0);
 
         sd_event_loop(e);

commit a34b57c0d43b8bf819ccd4f62c314b41b625454d
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:57 2014 +0300

    sd-dhcp6-client: Receive and parse a reply and set T1 and T2 timers
    
    Receive and parse a Reply from the server. Set up T1 and T2 timers and
    notify the library user of an acquired DHCPv6 lease.

diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index 754e800..8b3a819 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -70,6 +70,7 @@ enum DHCP6State {
         DHCP6_STATE_RS                          = 1,
         DHCP6_STATE_SOLICITATION                = 2,
         DHCP6_STATE_REQUEST                     = 3,
+        DHCP6_STATE_BOUND                       = 4,
 };
 
 enum {
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 55dd65c..01d9ec4 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -236,6 +236,7 @@ static int client_send_message(sd_dhcp6_client *client) {
 
         case DHCP6_STATE_STOPPED:
         case DHCP6_STATE_RS:
+        case DHCP6_STATE_BOUND:
                 return -EINVAL;
         }
 
@@ -255,6 +256,38 @@ static int client_send_message(sd_dhcp6_client *client) {
         return 0;
 }
 
+static int client_timeout_t2(sd_event_source *s, uint64_t usec,
+                             void *userdata) {
+        sd_dhcp6_client *client = userdata;
+
+        assert_return(s, -EINVAL);
+        assert_return(client, -EINVAL);
+        assert_return(client->lease, -EINVAL);
+
+        client->lease->ia.timeout_t2 =
+                sd_event_source_unref(client->lease->ia.timeout_t2);
+
+        log_dhcp6_client(client, "Timeout T2");
+
+        return 0;
+}
+
+static int client_timeout_t1(sd_event_source *s, uint64_t usec,
+                             void *userdata) {
+        sd_dhcp6_client *client = userdata;
+
+        assert_return(s, -EINVAL);
+        assert_return(client, -EINVAL);
+        assert_return(client->lease, -EINVAL);
+
+        client->lease->ia.timeout_t1 =
+                sd_event_source_unref(client->lease->ia.timeout_t1);
+
+        log_dhcp6_client(client, "Timeout T1");
+
+        return 0;
+}
+
 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
                                         void *userdata) {
         sd_dhcp6_client *client = userdata;
@@ -313,6 +346,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
 
         case DHCP6_STATE_STOPPED:
         case DHCP6_STATE_RS:
+        case DHCP6_STATE_BOUND:
                 return 0;
         }
 
@@ -537,6 +571,32 @@ static int client_parse_message(sd_dhcp6_client *client,
         return r;
 }
 
+static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
+                                size_t len)
+{
+        int r;
+        _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+
+        if (reply->type != DHCP6_REPLY)
+                return -EINVAL;
+
+        r = dhcp6_lease_new(&lease);
+        if (r < 0)
+                return -ENOMEM;
+
+        r = client_parse_message(client, reply, len, lease);
+        if (r < 0)
+                return r;
+
+        dhcp6_lease_clear_timers(&client->lease->ia);
+
+        client->lease = sd_dhcp6_lease_unref(client->lease);
+        client->lease = lease;
+        lease = NULL;
+
+        return DHCP6_STATE_BOUND;
+}
+
 static int client_receive_advertise(sd_dhcp6_client *client,
                                     DHCP6Message *advertise, size_t len) {
         int r;
@@ -634,6 +694,29 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
                 break;
 
         case DHCP6_STATE_REQUEST:
+                r = client_receive_reply(client, message, len);
+                if (r < 0)
+                        return 0;
+
+                if (r == DHCP6_STATE_BOUND) {
+
+                        r = client_start(client, DHCP6_STATE_BOUND);
+                        if (r < 0) {
+                                client_stop(client, r);
+                                return 0;
+                        }
+
+                        client = client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
+                        if (!client)
+                                return 0;
+                }
+
+                break;
+
+        case DHCP6_STATE_BOUND:
+
+                break;
+
         case DHCP6_STATE_STOPPED:
         case DHCP6_STATE_RS:
                 return 0;
@@ -650,6 +733,8 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
 {
         int r;
+        usec_t timeout, time_now;
+        char time_string[FORMAT_TIMESPAN_MAX];
 
         assert_return(client, -EINVAL);
         assert_return(client->event, -EINVAL);
@@ -693,9 +778,68 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
                 break;
 
         case DHCP6_STATE_REQUEST:
+
                 client->state = state;
 
                 break;
+
+        case DHCP6_STATE_BOUND:
+
+                r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
+                if (r < 0)
+                        return r;
+
+                if (client->lease->ia.lifetime_t1 == 0xffffffff ||
+                    client->lease->ia.lifetime_t2 == 0xffffffff) {
+
+                        log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
+                                         be32toh(client->lease->ia.lifetime_t1),
+                                         be32toh(client->lease->ia.lifetime_t2));
+
+                        return 0;
+                }
+
+                timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
+
+                log_dhcp6_client(client, "T1 expires in %s",
+                                 format_timespan(time_string,
+                                                 FORMAT_TIMESPAN_MAX,
+                                                 timeout, 0));
+
+                r = sd_event_add_time(client->event,
+                                      &client->lease->ia.timeout_t1,
+                                      CLOCK_MONOTONIC, time_now + timeout,
+                                      10 * USEC_PER_SEC, client_timeout_t1,
+                                      client);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
+                                                 client->event_priority);
+                if (r < 0)
+                        return r;
+
+                timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
+
+                log_dhcp6_client(client, "T2 expires in %s",
+                                 format_timespan(time_string,
+                                                 FORMAT_TIMESPAN_MAX,
+                                                 timeout, 0));
+
+                r = sd_event_add_time(client->event,
+                                      &client->lease->ia.timeout_t2,
+                                      CLOCK_MONOTONIC, time_now + timeout,
+                                      10 * USEC_PER_SEC, client_timeout_t2,
+                                      client);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
+                                                 client->event_priority);
+                if (r < 0)
+                        return r;
+
+                return 0;
         }
 
         client->transaction_id = random_u32() & htobe32(0x00ffffff);
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 92ea8b8..15e633b 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -32,6 +32,7 @@ enum {
         DHCP6_EVENT_STOP                        = 0,
         DHCP6_EVENT_RESEND_EXPIRE               = 10,
         DHCP6_EVENT_RETRANS_MAX                 = 11,
+        DHCP6_EVENT_IP_ACQUIRE                  = 12,
 };
 
 typedef struct sd_dhcp6_client sd_dhcp6_client;

commit 5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53e
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:54 2014 +0300

    sd-dhcp6-client: Add Advertise sending for test case
    
    Enhance the test case by replying with an Advertise message to the
    client. Copy the transaction id, IAID and DUID from the Solicit
    message. Verify the Request message created by the DHCPv6 client
    implementation and move the main loop exit to the end of the Request
    message verification.

diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 326a56c..40fd8f5 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -46,6 +46,9 @@ static bool verbose = false;
 static sd_event_source *hangcheck;
 static int test_dhcp_fd[2];
 static int test_index = 42;
+static int test_client_message_num;
+static be32_t test_iaid = 0;
+static uint8_t test_duid[14] = { };
 static sd_event *e_solicit;
 
 static int test_client_basic(sd_event *e) {
@@ -303,7 +306,106 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
         return test_dhcp_fd[0];
 }
 
-static int verify_solicit(DHCP6Message *solicit, uint8_t *option, size_t len) {
+static int test_client_send_reply(DHCP6Message *request) {
+        return 0;
+}
+
+static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
+                                      size_t len) {
+        _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+        uint8_t *optval;
+        uint16_t optcode;
+        size_t optlen;
+        bool found_clientid = false, found_iana = false, found_serverid = false;
+        int r;
+        struct in6_addr addr;
+        be32_t val;
+        uint32_t lt_pref, lt_valid;
+
+        assert_se(request->type == DHCP6_REQUEST);
+
+        assert_se(dhcp6_lease_new(&lease) >= 0);
+
+        while ((r = dhcp6_option_parse(&option, &len,
+                                       &optcode, &optlen, &optval)) >= 0) {
+                switch(optcode) {
+                case DHCP6_OPTION_CLIENTID:
+                        assert_se(!found_clientid);
+                        found_clientid = true;
+
+                        assert_se(!memcmp(optval, &test_duid,
+                                          sizeof(test_duid)));
+
+                        break;
+
+                case DHCP6_OPTION_IA_NA:
+                        assert_se(!found_iana);
+                        found_iana = true;
+
+
+                        assert_se(optlen == 40);
+                        assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
+
+                        val = htobe32(80);
+                        assert_se(!memcmp(optval + 4, &val, sizeof(val)));
+
+                        val = htobe32(120);
+                        assert_se(!memcmp(optval + 8, &val, sizeof(val)));
+
+                        assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
+                                                         optcode, &lease->ia));
+
+                        break;
+
+                case DHCP6_OPTION_SERVERID:
+                        assert_se(!found_serverid);
+                        found_serverid = true;
+
+                        assert_se(optlen == 14);
+                        assert_se(!memcmp(&msg_advertise[179], optval, optlen));
+
+                        break;
+                }
+        }
+
+        assert_se(r == -ENOMSG);
+        assert_se(found_clientid && found_iana && found_serverid);
+
+        assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
+                                                   &lt_valid) >= 0);
+        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
+
+        assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
+                                                  &lt_valid) == -ENOMSG);
+
+        sd_event_exit(e_solicit, 0);
+
+        return 0;
+}
+
+static int test_client_send_advertise(DHCP6Message *solicit)
+{
+        DHCP6Message advertise;
+
+        advertise.transaction_id = solicit->transaction_id;
+        advertise.type = DHCP6_ADVERTISE;
+
+        memcpy(msg_advertise, &advertise.transaction_id, 4);
+
+        memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
+
+        memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
+
+        assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise))
+                  == sizeof(msg_advertise));
+
+        return 0;
+}
+
+static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
+                                      size_t len) {
         uint8_t *optval;
         uint16_t optcode;
         size_t optlen;
@@ -319,7 +421,8 @@ static int verify_solicit(DHCP6Message *solicit, uint8_t *option, size_t len) {
                         assert_se(!found_clientid);
                         found_clientid = true;
 
-                        assert_se(optlen == 14);
+                        assert_se(optlen == sizeof(test_duid));
+                        memcpy(&test_duid, optval, sizeof(test_duid));
 
                         break;
 
@@ -329,6 +432,8 @@ static int verify_solicit(DHCP6Message *solicit, uint8_t *option, size_t len) {
 
                         assert_se(optlen == 12);
 
+                        memcpy(&test_iaid, optval, sizeof(test_iaid));
+
                         break;
                 }
         }
@@ -336,8 +441,6 @@ static int verify_solicit(DHCP6Message *solicit, uint8_t *option, size_t len) {
         assert_se(r == -ENOMSG);
         assert_se(found_clientid && found_iana);
 
-        sd_event_exit(e_solicit, 0);
-
         return 0;
 }
 
@@ -361,7 +464,15 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
 
         assert_se(message->transaction_id & 0x00ffffff);
 
-        verify_solicit(message, option, len);
+        if (test_client_message_num == 0) {
+                test_client_verify_solicit(message, option, len);
+                test_client_send_advertise(message);
+                test_client_message_num++;
+        } else if (test_client_message_num == 1) {
+                test_client_verify_request(message, option, len);
+                test_client_send_reply(message);
+                test_client_message_num++;
+        }
 
         return len;
 }

commit 7246333cb803b03440d3bd0bdaa233564d09b5ae
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:53 2014 +0300

    sd-dhcp6-client: Add Request message sending
    
    As described in RFC 3315, Section 17.1.2, a client has to wait until the
    first timeout has elapsed before it is allowed to request IPv6 addresses
    from the DHCPv6 server. This is indicated by a non-NULL lease and a
    non-zero resend count. Should the Advertisement contain a preference
    value of 255 or be received after the first timeout, IPv6 address
    requesting is started immediately.
    
    In response to these events, create a Request message and set up proper
    resend timers to send the message to the server.

diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index 1303a55..754e800 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -54,6 +54,9 @@ enum {
 #define DHCP6_SOL_MAX_DELAY                     1 * USEC_PER_SEC
 #define DHCP6_SOL_TIMEOUT                       1 * USEC_PER_SEC
 #define DHCP6_SOL_MAX_RT                        120 * USEC_PER_SEC
+#define DHCP6_REQ_TIMEOUT                       1 * USEC_PER_SEC
+#define DHCP6_REQ_MAX_RT                        120 * USEC_PER_SEC
+#define DHCP6_REQ_MAX_RC                        10
 
 enum {
         DHCP6_DUID_LLT                          = 1,
@@ -66,6 +69,7 @@ enum DHCP6State {
         DHCP6_STATE_STOPPED                     = 0,
         DHCP6_STATE_RS                          = 1,
         DHCP6_STATE_SOLICITATION                = 2,
+        DHCP6_STATE_REQUEST                     = 3,
 };
 
 enum {
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 1a59cc2..55dd65c 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -213,12 +213,22 @@ static int client_send_message(sd_dhcp6_client *client) {
         case DHCP6_STATE_SOLICITATION:
                 message->type = DHCP6_SOLICIT;
 
-                r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
-                                        sizeof(client->duid), &client->duid);
+                r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
                 if (r < 0)
                         return r;
 
-                r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
+                break;
+
+        case DHCP6_STATE_REQUEST:
+                message->type = DHCP6_REQUEST;
+
+                r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
+                                        client->lease->serverid_len,
+                                        client->lease->serverid);
+                if (r < 0)
+                        return r;
+
+                r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
                 if (r < 0)
                         return r;
 
@@ -229,6 +239,11 @@ static int client_send_message(sd_dhcp6_client *client) {
                 return -EINVAL;
         }
 
+        r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
+                                sizeof(client->duid), &client->duid);
+        if (r < 0)
+                return r;
+
         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
                                           len - optlen);
         if (r < 0)
@@ -275,6 +290,12 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
 
         switch (client->state) {
         case DHCP6_STATE_SOLICITATION:
+
+                if (client->retransmit_count && client->lease) {
+                        client_start(client, DHCP6_STATE_REQUEST);
+                        return 0;
+                }
+
                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
                 max_retransmit_time = DHCP6_SOL_MAX_RT;
                 max_retransmit_count = 0;
@@ -282,6 +303,14 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
 
                 break;
 
+        case DHCP6_STATE_REQUEST:
+                init_retransmit_time = DHCP6_REQ_TIMEOUT;
+                max_retransmit_time = DHCP6_REQ_MAX_RT;
+                max_retransmit_count = DHCP6_REQ_MAX_RC;
+                max_retransmit_duration = 0;
+
+                break;
+
         case DHCP6_STATE_STOPPED:
         case DHCP6_STATE_RS:
                 return 0;
@@ -537,6 +566,9 @@ static int client_receive_advertise(sd_dhcp6_client *client,
                 r = 0;
         }
 
+        if (pref_advertise == 255 || client->retransmit_count > 1)
+                r = DHCP6_STATE_REQUEST;
+
         return r;
 }
 
@@ -596,8 +628,12 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
         case DHCP6_STATE_SOLICITATION:
                 r = client_receive_advertise(client, message, len);
 
+                if (r == DHCP6_STATE_REQUEST)
+                        client_start(client, r);
+
                 break;
 
+        case DHCP6_STATE_REQUEST:
         case DHCP6_STATE_STOPPED:
         case DHCP6_STATE_RS:
                 return 0;
@@ -655,6 +691,11 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
                 client->state = DHCP6_STATE_SOLICITATION;
 
                 break;
+
+        case DHCP6_STATE_REQUEST:
+                client->state = state;
+
+                break;
         }
 
         client->transaction_id = random_u32() & htobe32(0x00ffffff);

commit c3e2adeaba8e043caed0ef139eeaea016bd152d0
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:49 2014 +0300

    sd-dhcp6-client: Update start function to take a state
    
    Update the start function so that the client state can be conveniently
    changed with the previous message resend timers cleared. On initial
    startup also create and bind to the UDP socket.

diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 65679b7..1a59cc2 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -95,6 +95,8 @@ const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
 
+static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
+
 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
                                  sd_dhcp6_client_cb_t cb, void *userdata)
 {
@@ -161,7 +163,7 @@ static int client_initialize(sd_dhcp6_client *client)
         if (client->fd > 0)
                 client->fd = safe_close(client->fd);
 
-        client->transaction_id = random_u32() & 0x00ffffff;
+        client->transaction_id = 0;
 
         client->ia_na.timeout_t1 =
                 sd_event_source_unref(client->ia_na.timeout_t1);
@@ -609,36 +611,53 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
         return 0;
 }
 
-static int client_start(sd_dhcp6_client *client)
+static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
 {
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(client->event, -EINVAL);
         assert_return(client->index > 0, -EINVAL);
+        assert_return(client->state != state, -EINVAL);
 
-        r = client_ensure_iaid(client);
-        if (r < 0)
-                return r;
+        client->timeout_resend_expire =
+                sd_event_source_unref(client->timeout_resend_expire);
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+        client->retransmit_time = 0;
+        client->retransmit_count = 0;
 
-        r = dhcp6_network_bind_udp_socket(client->index, NULL);
-        if (r < 0)
-                return r;
+        switch (state) {
+        case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_RS:
+        case DHCP6_STATE_SOLICITATION:
 
-        client->fd = r;
+                r = client_ensure_iaid(client);
+                if (r < 0)
+                        return r;
 
-        r = sd_event_add_io(client->event, &client->receive_message,
-                            client->fd, EPOLLIN, client_receive_message,
-                            client);
-        if (r < 0)
-                return r;
+                r = dhcp6_network_bind_udp_socket(client->index, NULL);
+                if (r < 0)
+                        return r;
 
-        r = sd_event_source_set_priority(client->receive_message,
-                                         client->event_priority);
-        if (r < 0)
-                return r;
+                client->fd = r;
+
+                r = sd_event_add_io(client->event, &client->receive_message,
+                                    client->fd, EPOLLIN, client_receive_message,
+                                    client);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_priority(client->receive_message,
+                                                 client->event_priority);
+                if (r < 0)
+                        return r;
+
+                client->state = DHCP6_STATE_SOLICITATION;
+
+                break;
+        }
 
-        client->state = DHCP6_STATE_SOLICITATION;
+        client->transaction_id = random_u32() & htobe32(0x00ffffff);
 
         r = sd_event_add_time(client->event, &client->timeout_resend,
                               CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
@@ -673,7 +692,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
         if (r < 0)
                 return r;
 
-        return client_start(client);
+        return client_start(client, DHCP6_STATE_SOLICITATION);
 }
 
 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,

commit 859cca44f834ab1cc3e41fa6b94744f1856ab027
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:46 2014 +0300

    sd-dhcp6-client: Add test case for Advertise message parsing
    
    Add a basic test case excersising once more option parsing function
    in addition to lease handling. Check that the address iteration
    functions return the correct IPv6 address and lifetimes and that
    only one address is returned. Also verify that the server ID and
    preference values are read correctly.

diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 43b1275..326a56c 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -35,6 +35,7 @@
 #include "sd-dhcp6-client.h"
 #include "dhcp6-protocol.h"
 #include "dhcp6-internal.h"
+#include "dhcp6-lease-internal.h"
 
 static struct ether_addr mac_addr = {
         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
@@ -139,6 +140,142 @@ static int test_option(sd_event *e) {
         return 0;
 }
 
+static uint8_t msg_advertise[198] = {
+        0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
+        0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
+        0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03,
+        0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00,
+        0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05,
+        0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
+        0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c,
+        0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
+        0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00,
+        0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28,
+        0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65,
+        0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65,
+        0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66,
+        0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e,
+        0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68,
+        0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8,
+        0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b,
+        0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
+        0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20,
+        0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19,
+        0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d,
+        0x53, 0x00, 0x07, 0x00, 0x01, 0x00
+};
+
+static int test_advertise_option(sd_event *e) {
+        _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+        DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
+        uint8_t *optval, *opt = &msg_advertise[sizeof(DHCP6Message)];
+        uint16_t optcode;
+        size_t optlen, len = sizeof(msg_advertise);
+        be32_t val;
+        uint8_t preference = 255;
+        struct in6_addr addr;
+        uint32_t lt_pref, lt_valid;
+        int r;
+        bool opt_clientid = false;
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        assert_se(dhcp6_lease_new(&lease) >= 0);
+
+        assert_se(advertise->type == DHCP6_ADVERTISE);
+        assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
+                  0x0fb4e5);
+
+        while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
+                                       &optval)) >= 0) {
+
+                switch(optcode) {
+                case DHCP6_OPTION_CLIENTID:
+                        assert_se(optlen == 14);
+
+                        opt_clientid = true;
+                        break;
+
+                case DHCP6_OPTION_IA_NA:
+                        assert_se(optlen == 94);
+                        assert_se(!memcmp(optval, &msg_advertise[26], optlen));
+
+                        val = htobe32(0x0ecfa37d);
+                        assert_se(!memcmp(optval, &val, sizeof(val)));
+
+                        val = htobe32(80);
+                        assert_se(!memcmp(optval + 4, &val, sizeof(val)));
+
+                        val = htobe32(120);
+                        assert_se(!memcmp(optval + 8, &val, sizeof(val)));
+
+                        assert_se(dhcp6_option_parse_ia(&optval, &optlen,
+                                                        optcode,
+                                                        &lease->ia) >= 0);
+
+                        break;
+
+                case DHCP6_OPTION_SERVERID:
+                        assert_se(optlen == 14);
+                        assert_se(!memcmp(optval, &msg_advertise[179], optlen));
+
+                        assert_se(dhcp6_lease_set_serverid(lease, optval,
+                                                           optlen) >= 0);
+                        break;
+
+                case DHCP6_OPTION_PREFERENCE:
+                        assert_se(optlen == 1);
+                        assert_se(!*optval);
+
+                        assert_se(dhcp6_lease_set_preference(lease,
+                                                             *optval) >= 0);
+                        break;
+
+                default:
+                        break;
+                }
+        }
+
+
+        assert_se(r == -ENOMSG);
+
+        assert_se(opt_clientid);
+
+        assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
+                                                   &lt_valid) >= 0);
+        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
+        assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
+                                                  &lt_valid) == -ENOMSG);
+
+        assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
+                                                   &lt_valid) >= 0);
+        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
+        assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
+                                                  &lt_valid) == -ENOMSG);
+        assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
+                                                  &lt_valid) == -ENOMSG);
+        assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
+                                                   &lt_valid) >= 0);
+        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
+        assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
+                                                  &lt_valid) == -ENOMSG);
+
+        assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0);
+        assert_se(len == 14);
+        assert_se(!memcmp(opt, &msg_advertise[179], len));
+
+        assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
+        assert_se(preference == 0);
+
+        return 0;
+}
+
 static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
         assert_not_reached("Test case should have completed in 2 seconds");
 
@@ -289,6 +426,7 @@ int main(int argc, char *argv[]) {
 
         test_client_basic(e);
         test_option(e);
+        test_advertise_option(e);
         test_client_solicit(e);
 
         assert_se(!sd_event_unref(e));

commit ea3b3a75abb3f8b853f7da454b9b8e258a120eea
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:45 2014 +0300

    sd-dhcp6-lease: Add functions for accessing lease and addresses
    
    Add support functions for accessing the current client lease as well
    as iterating over the addresses and get their preferred and valid
    lifetimes.

diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h
index f4f1488..295c223 100644
--- a/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libsystemd-network/dhcp6-lease-internal.h
@@ -37,6 +37,8 @@ struct sd_dhcp6_lease {
         uint8_t preference;
 
         DHCP6IA ia;
+
+        DHCP6Address *addr_iter;
 };
 
 int dhcp6_lease_clear_timers(DHCP6IA *ia);
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 431801d..65679b7 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -129,6 +129,18 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
         return 0;
 }
 
+int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
+        assert_return(client, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!client->lease)
+                return -ENOMSG;
+
+        *ret = sd_dhcp6_lease_ref(client->lease);
+
+        return 0;
+}
+
 static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
         if (client->cb) {
                 client = sd_dhcp6_client_ref(client);
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index 41d6a5a..cbda7d8 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -105,6 +105,45 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
         return 0;
 }
 
+int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease,
+                                    struct in6_addr *addr,
+                                    uint32_t *lifetime_preferred,
+                                    uint32_t *lifetime_valid) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(lifetime_preferred, -EINVAL);
+        assert_return(lifetime_valid, -EINVAL);
+
+        if (!lease->addr_iter)
+                return -ENOMSG;
+
+        memcpy(addr, &lease->addr_iter->address, sizeof(struct in6_addr));
+        *lifetime_preferred = be32toh(lease->addr_iter->lifetime_preferred);
+        *lifetime_valid = be32toh(lease->addr_iter->lifetime_valid);
+
+        lease->addr_iter = lease->addr_iter->addresses_next;
+
+        return 0;
+}
+
+int sd_dhcp6_lease_get_first_address(sd_dhcp6_lease *lease,
+                                     struct in6_addr *addr,
+                                     uint32_t *lifetime_preferred,
+                                     uint32_t *lifetime_valid) {
+        assert_return(lease, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(lifetime_preferred, -EINVAL);
+        assert_return(lifetime_valid, -EINVAL);
+
+        if (!lease->ia.addresses)
+                return -ENOMSG;
+
+        lease->addr_iter = lease->ia.addresses;
+
+        return sd_dhcp6_lease_get_next_address(lease, addr, lifetime_preferred,
+                                               lifetime_valid);
+}
+
 sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
         if (lease)
                 assert_se(REFCNT_INC(lease->n_ref) >= 2);
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 72267f9..92ea8b8 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -26,6 +26,8 @@
 
 #include "sd-event.h"
 
+#include "sd-dhcp6-lease.h"
+
 enum {
         DHCP6_EVENT_STOP                        = 0,
         DHCP6_EVENT_RESEND_EXPIRE               = 10,
@@ -43,6 +45,8 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
                             const struct ether_addr *mac_addr);
 
+int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret);
+
 int sd_dhcp6_client_stop(sd_dhcp6_client *client);
 int sd_dhcp6_client_start(sd_dhcp6_client *client);
 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h
index 0b2765c..1126f1a 100644
--- a/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/sd-dhcp6-lease.h
@@ -23,8 +23,19 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <netinet/in.h>
+
 typedef struct sd_dhcp6_lease sd_dhcp6_lease;
 
+int sd_dhcp6_lease_get_first_address(sd_dhcp6_lease *lease,
+                                     struct in6_addr *addr,
+                                     uint32_t *lifetime_preferred,
+                                     uint32_t *lifetime_valid);
+int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease,
+                                    struct in6_addr *addr,
+                                    uint32_t *lifetime_preferred,
+                                    uint32_t *lifetime_valid);
+
 sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
 sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
 

commit 631bbe71298ec892f77f44f94feb612646fe6853
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:42 2014 +0300

    sd-dhcp6-client: Receive and parse Advertise messages
    
    When receiving DHCPv6 messages, discard the ones that are not meant
    for DHCPv6 clients and verify the transaction id. Once that is done,
    process the Advertise message and select the Advertise with the
    highest preference.
    
    Create a separate function for lease information parsing so that it
    can be reused in other parts of the protocol. Verify both DUID and
    IAID in the received message and store other necessary information
    with the lease structure.

diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index ec1d82a..94e3a5d 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -75,3 +75,5 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
 
 const char *dhcp6_message_type_to_string(int s) _const_;
 int dhcp6_message_type_from_string(const char *s) _pure_;
+const char *dhcp6_message_status_to_string(int s) _const_;
+int dhcp6_message_status_from_string(const char *s) _pure_;
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index c58a07b..1303a55 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -21,6 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+
 #include "macro.h"
 #include "sparse-endian.h"
 
@@ -36,6 +39,9 @@ struct DHCP6Message {
 
 typedef struct DHCP6Message DHCP6Message;
 
+#define DHCP6_MIN_OPTIONS_SIZE \
+        1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
+
 #define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
         { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
               0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 7be6424..431801d 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -21,6 +21,7 @@
 
 #include <errno.h>
 #include <string.h>
+#include <sys/ioctl.h>
 
 #include "udev.h"
 #include "udev-util.h"
@@ -33,6 +34,7 @@
 #include "sd-dhcp6-client.h"
 #include "dhcp6-protocol.h"
 #include "dhcp6-internal.h"
+#include "dhcp6-lease-internal.h"
 
 #define SYSTEMD_PEN 43793
 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
@@ -47,6 +49,7 @@ struct sd_dhcp6_client {
         struct ether_addr mac_addr;
         DHCP6IA ia_na;
         be32_t transaction_id;
+        struct sd_dhcp6_lease *lease;
         int fd;
         sd_event_source *receive_message;
         usec_t retransmit_time;
@@ -81,6 +84,17 @@ const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
 
+const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
+        [DHCP6_STATUS_SUCCESS] = "Success",
+        [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
+        [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
+        [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
+        [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
+        [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
+
 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
                                  sd_dhcp6_client_cb_t cb, void *userdata)
 {
@@ -377,9 +391,209 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
         return 0;
 }
 
+static int client_parse_message(sd_dhcp6_client *client,
+                                DHCP6Message *message, size_t len,
+                                sd_dhcp6_lease *lease) {
+        int r;
+        uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
+        uint16_t optcode, status;
+        size_t optlen, id_len;
+        bool clientid = false;
+        be32_t iaid_lease;
+
+        while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
+                                       &optval)) >= 0) {
+                switch (optcode) {
+                case DHCP6_OPTION_CLIENTID:
+                        if (clientid) {
+                                log_dhcp6_client(client, "%s contains multiple clientids",
+                                                 dhcp6_message_type_to_string(message->type));
+                                return -EINVAL;
+                        }
+
+                        if (optlen != sizeof(client->duid) ||
+                            memcmp(&client->duid, optval, optlen) != 0) {
+                                log_dhcp6_client(client, "%s DUID does not match",
+                                                 dhcp6_message_type_to_string(message->type));
+
+                                return -EINVAL;
+                        }
+                        clientid = true;
+
+                        break;
+
+                case DHCP6_OPTION_SERVERID:
+                        r = dhcp6_lease_get_serverid(lease, &id, &id_len);
+                        if (r >= 0 && id) {
+                                log_dhcp6_client(client, "%s contains multiple serverids",
+                                                 dhcp6_message_type_to_string(message->type));
+                                return -EINVAL;
+                        }
+
+                        r = dhcp6_lease_set_serverid(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case DHCP6_OPTION_PREFERENCE:
+                        if (optlen != 1)
+                                return -EINVAL;
+
+                        r = dhcp6_lease_set_preference(lease, *optval);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case DHCP6_OPTION_STATUS_CODE:
+                        if (optlen < 2)
+                                return -EINVAL;
+
+                        status = optval[0] << 8 | optval[1];
+                        if (status) {
+                                log_dhcp6_client(client, "%s Status %s",
+                                                 dhcp6_message_type_to_string(message->type),
+                                                 dhcp6_message_status_to_string(status));
+                                return -EINVAL;
+                        }
+
+                        break;
+
+                case DHCP6_OPTION_IA_NA:
+                        r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
+                                                  &lease->ia);
+                        if (r < 0 && r != -ENOMSG)
+                                return r;
+
+                        r = dhcp6_lease_get_iaid(lease, &iaid_lease);
+                        if (r < 0)
+                                return r;
+
+                        if (client->ia_na.id != iaid_lease) {
+                                log_dhcp6_client(client, "%s has wrong IAID",
+                                                 dhcp6_message_type_to_string(message->type));
+                                return -EINVAL;
+                        }
+
+                        break;
+                }
+        }
+
+        if ((r < 0 && r != -ENOMSG) || !clientid) {
+                log_dhcp6_client(client, "%s has incomplete options",
+                                 dhcp6_message_type_to_string(message->type));
+                return -EINVAL;
+        }
+
+        r = dhcp6_lease_get_serverid(lease, &id, &id_len);
+        if (r < 0)
+                log_dhcp6_client(client, "%s has no server id",
+                                 dhcp6_message_type_to_string(message->type));
+
+        return r;
+}
+
+static int client_receive_advertise(sd_dhcp6_client *client,
+                                    DHCP6Message *advertise, size_t len) {
+        int r;
+        _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
+        uint8_t pref_advertise = 0, pref_lease = 0;
+
+        if (advertise->type != DHCP6_ADVERTISE)
+                return -EINVAL;
+
+        r = dhcp6_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        r = client_parse_message(client, advertise, len, lease);
+        if (r < 0)
+                return r;
+
+        r = dhcp6_lease_get_preference(lease, &pref_advertise);
+        if (r < 0)
+                return r;
+
+        r = dhcp6_lease_get_preference(client->lease, &pref_lease);
+        if (!client->lease || r < 0 || pref_advertise > pref_lease) {
+                sd_dhcp6_lease_unref(client->lease);
+                client->lease = lease;
+                lease = NULL;
+                r = 0;
+        }
+
+        return r;
+}
+
 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
-                                  void *userdata)
-{
+                                  void *userdata) {
+        sd_dhcp6_client *client = userdata;
+        _cleanup_free_ DHCP6Message *message;
+        int r, buflen, len;
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        r = ioctl(fd, FIONREAD, &buflen);
+        if (r < 0 || buflen <= 0)
+                buflen = DHCP6_MIN_OPTIONS_SIZE;
+
+        message = malloc0(buflen);
+        if (!message)
+                return -ENOMEM;
+
+        len = read(fd, message, buflen);
+        if ((size_t)len < sizeof(DHCP6Message)) {
+                log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
+                return 0;
+        }
+
+        switch(message->type) {
+        case DHCP6_SOLICIT:
+        case DHCP6_REQUEST:
+        case DHCP6_CONFIRM:
+        case DHCP6_RENEW:
+        case DHCP6_REBIND:
+        case DHCP6_RELEASE:
+        case DHCP6_DECLINE:
+        case DHCP6_INFORMATION_REQUEST:
+        case DHCP6_RELAY_FORW:
+        case DHCP6_RELAY_REPL:
+                return 0;
+
+        case DHCP6_ADVERTISE:
+        case DHCP6_REPLY:
+        case DHCP6_RECONFIGURE:
+                break;
+
+        default:
+                log_dhcp6_client(client, "unknown message type %d",
+                                 message->type);
+                return 0;
+        }
+
+        if (client->transaction_id != (message->transaction_id &
+                                       htobe32(0x00ffffff)))
+                return 0;
+
+        switch (client->state) {
+        case DHCP6_STATE_SOLICITATION:
+                r = client_receive_advertise(client, message, len);
+
+                break;
+
+        case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_RS:
+                return 0;
+        }
+
+        if (r >= 0) {
+                log_dhcp6_client(client, "Recv %s",
+                                 dhcp6_message_type_to_string(message->type));
+        }
+
         return 0;
 }
 

commit c6affce8740bb0cee42eebf6d1d44dd518035e88
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:39 2014 +0300

    sd-dhcp6-client: Add IA Address option parsing
    
    Add functionality to parse DHCPv6 Identity Association for
    Non-temporary (IA_NA) and Temporary Addresses (IA_TA) options.
    Both of them contain one or more IA Address (IAADDR) options
    and optinally a status code option. Only the IA_NA option
    contains lease lifetimes. See RFC 3315, sections 22.4., 22.5.,
    22.6., 22.13. and appendix B. for details. If the lease
    timeouts are not set, use the ones recommended for servers in
    section 22.4.
    
    Factor out common code in the form of an option header parsing
    helper function.

diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 31f5bd2..ec1d82a 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -66,6 +66,8 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
+int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
+                          DHCP6IA *ia);
 
 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
 int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index cc4d261..f488832 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -129,22 +129,185 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
         return 0;
 }
 
+
+static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *opt,
+                            size_t *optlen) {
+        uint16_t len;
+
+        assert_return(buf, -EINVAL);
+        assert_return(opt, -EINVAL);
+        assert_return(optlen, -EINVAL);
+
+        if (*buflen < 4)
+                return -ENOMSG;
+
+        len = (*buf)[2] << 8 | (*buf)[3];
+
+        if (len > *buflen)
+                return -ENOMSG;
+
+        *opt = (*buf)[0] << 8 | (*buf)[1];
+        *optlen = len;
+
+        *buf += 4;
+        *buflen -= 4;
+
+        return 0;
+}
+
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue) {
-        assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
+        int r;
 
-        if (*buflen == 0)
-                return -ENOMSG;
+        assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
 
-        *optcode = (*buf)[0] << 8 | (*buf)[1];
-        *optlen = (*buf)[2] << 8 | (*buf)[3];
+        r = option_parse_hdr(buf, buflen, optcode, optlen);
+        if (r < 0)
+                return r;
 
-        if (*optlen > *buflen - 4)
+        if (*optlen > *buflen)
                 return -ENOBUFS;
 
-        *optvalue = &(*buf)[4];
-        *buflen -= (*optlen + 4);
-        (*buf) += (*optlen + 4);
+        *optvalue = *buf;
+        *buflen -= *optlen;
+        *buf += *optlen;
 
         return 0;
 }
+
+int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
+                          DHCP6IA *ia) {
+        int r;
+        uint16_t opt, status;
+        size_t optlen;
+        size_t iaaddr_offset;
+        DHCP6Address *addr;
+        uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
+
+        assert_return(ia, -EINVAL);
+        assert_return(!ia->addresses, -EINVAL);
+
+        switch (iatype) {
+        case DHCP6_OPTION_IA_NA:
+
+                if (*buflen < DHCP6_OPTION_IA_NA_LEN + DHCP6_OPTION_HDR_LEN +
+                    DHCP6_OPTION_IAADDR_LEN) {
+                        r = -ENOBUFS;
+                        goto error;
+                }
+
+                iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
+                memcpy(&ia->id, *buf, iaaddr_offset);
+
+                lt_t1 = be32toh(ia->lifetime_t1);
+                lt_t2 = be32toh(ia->lifetime_t2);
+
+                if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
+                        log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
+                                         lt_t1, lt_t2);
+                        r = -EINVAL;
+                        goto error;
+                }
+
+                break;
+
+        case DHCP6_OPTION_IA_TA:
+                if (*buflen < DHCP6_OPTION_IA_TA_LEN + DHCP6_OPTION_HDR_LEN +
+                    DHCP6_OPTION_IAADDR_LEN) {
+                        r = -ENOBUFS;
+                        goto error;
+                }
+
+                iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
+                memcpy(&ia->id, *buf, iaaddr_offset);
+
+                ia->lifetime_t1 = 0;
+                ia->lifetime_t2 = 0;
+
+                break;
+
+        default:
+                r = -ENOMSG;
+                goto error;
+        }
+
+        ia->type = iatype;
+
+        *buflen -= iaaddr_offset;
+        *buf += iaaddr_offset;
+
+        while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
+
+                switch (opt) {
+                case DHCP6_OPTION_IAADDR:
+
+                        addr = new0(DHCP6Address, 1);
+                        if (!addr) {
+                                r = -ENOMEM;
+                                goto error;
+                        }
+
+                        LIST_INIT(addresses, addr);
+
+                        memcpy(&addr->address, *buf, DHCP6_OPTION_IAADDR_LEN);
+
+                        lt_valid = be32toh(addr->lifetime_valid);
+                        lt_pref = be32toh(addr->lifetime_valid);
+
+                        if (!lt_valid || lt_pref > lt_valid) {
+                                log_dhcp6_client(client, "IA preferred %ds > valid %ds",
+                                                 lt_pref, lt_valid);
+                                free(addr);
+                        } else {
+                                LIST_PREPEND(addresses, ia->addresses, addr);
+                                if (lt_valid < lt_min)
+                                        lt_min = lt_valid;
+                        }
+
+                        break;
+
+                case DHCP6_OPTION_STATUS_CODE:
+                        if (optlen < sizeof(status))
+                                break;
+
+                        status = (*buf)[0] << 8 | (*buf)[1];
+                        if (status) {
+                                log_dhcp6_client(client, "IA status %d",
+                                                 status);
+                                r = -EINVAL;
+                                goto error;
+                        }
+
+                        break;
+
+                default:
+                        log_dhcp6_client(client, "Unknown IA option %d", opt);
+                        break;
+                }
+
+                *buflen -= optlen;
+                *buf += optlen;
+        }
+
+        if (r == -ENOMSG)
+                r = 0;
+
+        if (!ia->lifetime_t1 && !ia->lifetime_t2) {
+                lt_t1 = lt_min / 2;
+                lt_t2 = lt_min / 10 * 8;
+                ia->lifetime_t1 = htobe32(lt_t1);
+                ia->lifetime_t2 = htobe32(lt_t2);
+
+                log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
+                                 lt_t1, lt_t2);
+        }
+
+        if (*buflen)
+                r = -ENOMSG;
+
+error:
+        *buf += *buflen;
+        *buflen = 0;
+
+        return r;
+}

commit 3fb2c57038cf8dad396421989f43697fcf4ac4a4
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:35 2014 +0300

    sd-dhcp6-lease: Add DHCPv6 lease handling
    
    Create a structure describing a DHCPv6 lease. Add internal functions
    for creating a new lease and accessing the server ID, preference and
    IAID. Provide functions for clearing addresses and associated timers.
    
    External users are initially given only the capabilities of
    referencing and unreferencing the lease structure.

diff --git a/Makefile.am b/Makefile.am
index f1f5873..bafb80e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2596,13 +2596,16 @@ libsystemd_network_la_SOURCES = \
 	src/libsystemd-network/ipv4ll-internal.h \
 	src/libsystemd-network/network-internal.c \
 	src/libsystemd-network/network-internal.h \
-	src/systemd/sd-dhcp6-client.h \
 	src/systemd/sd-icmp6-nd.h \
-	src/libsystemd-network/sd-dhcp6-client.c \
+	src/systemd/sd-dhcp6-client.h \
+	src/systemd/sd-dhcp6-lease.h \
 	src/libsystemd-network/sd-icmp6-nd.c \
+	src/libsystemd-network/sd-dhcp6-client.c \
 	src/libsystemd-network/dhcp6-internal.h \
 	src/libsystemd-network/dhcp6-network.c \
-	src/libsystemd-network/dhcp6-option.c
+	src/libsystemd-network/dhcp6-option.c \
+	src/libsystemd-network/dhcp6-lease-internal.h \
+	src/libsystemd-network/sd-dhcp6-lease.c
 
 libsystemd_network_la_LIBADD = \
 	libudev-internal.la \
diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h
new file mode 100644
index 0000000..f4f1488
--- /dev/null
+++ b/src/libsystemd-network/dhcp6-lease-internal.h
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+
+#include "refcnt.h"
+
+#include "sd-dhcp6-lease.h"
+#include "dhcp6-internal.h"
+
+struct sd_dhcp6_lease {
+        RefCount n_ref;
+
+        uint8_t *serverid;
+        size_t serverid_len;
+        uint8_t preference;
+
+        DHCP6IA ia;
+};
+
+int dhcp6_lease_clear_timers(DHCP6IA *ia);
+DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
+
+int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
+                             size_t len);
+int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len);
+int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference);
+int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference);
+int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
+
+int dhcp6_lease_new(sd_dhcp6_lease **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref);
+#define _cleanup_dhcp6_lease_free_ _cleanup_(sd_dhcp6_lease_unrefp)
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
new file mode 100644
index 0000000..41d6a5a
--- /dev/null
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -0,0 +1,139 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "util.h"
+
+#include "dhcp6-lease-internal.h"
+
+int dhcp6_lease_clear_timers(DHCP6IA *ia) {
+        assert_return(ia, -EINVAL);
+
+        ia->timeout_t1 = sd_event_source_unref(ia->timeout_t1);
+        ia->timeout_t2 = sd_event_source_unref(ia->timeout_t2);
+
+        return 0;
+}
+
+DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
+        DHCP6Address *address;
+
+        if (!ia)
+                return NULL;
+
+        dhcp6_lease_clear_timers(ia);
+
+        while (ia->addresses) {
+                address = ia->addresses;
+
+                LIST_REMOVE(addresses, ia->addresses, address);
+
+                free(address);
+        }
+
+        return NULL;
+}
+
+int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
+                             size_t len) {
+        assert_return(lease, -EINVAL);
+        assert_return(id, -EINVAL);
+
+        free(lease->serverid);
+
+        lease->serverid = memdup(id, len);
+        if (!lease->serverid)
+                return -EINVAL;
+
+        lease->serverid_len = len;
+
+        return 0;
+}
+
+int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) {
+        assert_return(lease, -EINVAL);
+        assert_return(id, -EINVAL);
+        assert_return(len, -EINVAL);
+
+        *id = lease->serverid;
+        *len = lease->serverid_len;
+
+        return 0;
+}
+
+int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
+        assert_return(lease, -EINVAL);
+
+        lease->preference = preference;
+
+        return 0;
+}
+
+int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) {
+        assert_return(lease, -EINVAL);
+        assert_return(preference, -EINVAL);
+
+        *preference = lease->preference;
+
+        return 0;
+}
+
+int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
+        assert_return(lease, -EINVAL);
+        assert_return(iaid, -EINVAL);
+
+        *iaid = lease->ia.id;
+
+        return 0;
+}
+
+sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
+        if (lease)
+                assert_se(REFCNT_INC(lease->n_ref) >= 2);
+
+        return lease;
+}
+
+sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
+        if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
+                free(lease->serverid);
+                dhcp6_lease_free_ia(&lease->ia);
+
+                free(lease);
+        }
+
+        return NULL;
+}
+
+int dhcp6_lease_new(sd_dhcp6_lease **ret) {
+        sd_dhcp6_lease *lease;
+
+        lease = new0(sd_dhcp6_lease, 1);
+        if (!lease)
+                return -ENOMEM;
+
+        lease->n_ref = REFCNT_INIT;
+
+        LIST_HEAD_INIT(lease->ia.addresses);
+
+        *ret = lease;
+        return 0;
+}
diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h
new file mode 100644
index 0000000..0b2765c
--- /dev/null
+++ b/src/systemd/sd-dhcp6-lease.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddhcp6leasehfoo
+#define foosddhcp6leasehfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct sd_dhcp6_lease sd_dhcp6_lease;
+
+sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
+sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
+
+#endif

commit 2ea8857effb833615b16d10fc7a19a7104c19e13
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:30 2014 +0300

    sd-dhcp6-client: Add DHCPv6 Solicit test case
    
    Verify the Solicit message created by the DHCPv6 client code.
    
    Provide local variants for detect_vm(), detect_container() and
    detect_virtualization() defined in virt.h. This makes the DHCPv6
    library believe it is run in a container and does not try to request
    interface information from udev for the non-existing interface index
    used by the test case code.

diff --git a/Makefile.am b/Makefile.am
index 82f187d..f1f5873 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2665,6 +2665,7 @@ test_icmp6_rs_LDADD = \
 
 test_dhcp6_client_SOURCES = \
 	src/systemd/sd-dhcp6-client.h \
+	src/libsystemd-network/dhcp6-icmp6.h \
 	src/libsystemd-network/dhcp6-internal.h \
 	src/libsystemd-network/test-dhcp6-client.c
 
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index b52f407..43b1275 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -21,10 +21,16 @@
 
 #include <stdbool.h>
 #include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <net/ethernet.h>
 
+#include "socket-util.h"
 #include "macro.h"
 #include "sd-event.h"
 #include "event-util.h"
+#include "virt.h"
 
 #include "sd-dhcp6-client.h"
 #include "dhcp6-protocol.h"
@@ -36,6 +42,11 @@ static struct ether_addr mac_addr = {
 
 static bool verbose = false;
 
+static sd_event_source *hangcheck;
+static int test_dhcp_fd[2];
+static int test_index = 42;
+static sd_event *e_solicit;
+
 static int test_client_basic(sd_event *e) {
         sd_dhcp6_client *client;
 
@@ -128,6 +139,145 @@ static int test_option(sd_event *e) {
         return 0;
 }
 
+static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
+        assert_not_reached("Test case should have completed in 2 seconds");
+
+        return 0;
+}
+
+int detect_vm(const char **id) {
+        return 1;
+}
+
+int detect_container(const char **id) {
+        return 1;
+}
+
+int detect_virtualization(const char **id) {
+        return 1;
+}
+
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+        assert_se(index == test_index);
+
+        if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
+                return -errno;
+
+        return test_dhcp_fd[0];
+}
+
+static int verify_solicit(DHCP6Message *solicit, uint8_t *option, size_t len) {
+        uint8_t *optval;
+        uint16_t optcode;
+        size_t optlen;
+        bool found_clientid = false, found_iana = false;
+        int r;
+
+        assert_se(solicit->type == DHCP6_SOLICIT);
+
+        while ((r = dhcp6_option_parse(&option, &len,
+                                       &optcode, &optlen, &optval)) >= 0) {
+                switch(optcode) {
+                case DHCP6_OPTION_CLIENTID:
+                        assert_se(!found_clientid);
+                        found_clientid = true;
+
+                        assert_se(optlen == 14);
+
+                        break;
+
+                case DHCP6_OPTION_IA_NA:
+                        assert_se(!found_iana);
+                        found_iana = true;
+
+                        assert_se(optlen == 12);
+
+                        break;
+                }
+        }
+
+        assert_se(r == -ENOMSG);
+        assert_se(found_clientid && found_iana);
+
+        sd_event_exit(e_solicit, 0);
+
+        return 0;
+}
+
+int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
+                                  const void *packet, size_t len) {
+        struct in6_addr mcast =
+                IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+        DHCP6Message *message;
+        uint8_t *option;
+
+        assert_se(s == test_dhcp_fd[0]);
+        assert_se(server_address);
+        assert_se(packet);
+        assert_se(len > sizeof(DHCP6Message) + 4);
+
+        assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
+
+        message = (DHCP6Message *)packet;
+        option = (uint8_t *)(message + 1);
+        len -= sizeof(DHCP6Message);
+
+        assert_se(message->transaction_id & 0x00ffffff);
+
+        verify_solicit(message, option, len);
+
+        return len;
+}
+
+static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
+                                   void *userdata) {
+        sd_event *e = userdata;
+
+        assert_se(e);
+
+        if (verbose)
+                printf("  got DHCPv6 event %d\n", event);
+
+        sd_event_exit(e, 0);
+}
+
+static int test_client_solicit(sd_event *e) {
+        sd_dhcp6_client *client;
+        usec_t time_now = now(CLOCK_MONOTONIC);
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        assert_se(sd_dhcp6_client_new(&client) >= 0);
+        assert_se(client);
+
+        assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
+
+        assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
+        assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
+
+        assert_se(sd_dhcp6_client_set_callback(client,
+                                               test_client_solicit_cb, e) >= 0);
+
+        assert_se(sd_event_add_time(e, &hangcheck, CLOCK_MONOTONIC,
+                                    time_now + 2 * USEC_PER_SEC, 0,
+                                    test_hangcheck, NULL) >= 0);
+
+        e_solicit = e;
+
+        assert_se(sd_dhcp6_client_start(client) >= 0);
+
+        sd_event_loop(e);
+
+        hangcheck = sd_event_source_unref(hangcheck);
+
+        assert_se(!sd_dhcp6_client_unref(client));
+
+        test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
+
+        return 0;
+}
+
 int main(int argc, char *argv[]) {
         _cleanup_event_unref_ sd_event *e;
 
@@ -139,6 +289,7 @@ int main(int argc, char *argv[]) {
 
         test_client_basic(e);
         test_option(e);
+        test_client_solicit(e);
 
         assert_se(!sd_event_unref(e));
 

commit a9aff3615b430f86bd0a824214d95f634efaf894
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:27 2014 +0300

    sd-dhcp6-client: Add DHCPv6 Solicit message creation and sending
    
    Implement the initial functionality used for creating a DHCPv6 Solicit
    message containing the needed options and send it to the DHCPv6
    broadcast address. Increase the sent message count and ensure that
    the Solicit Initial Retransmission Time is strictly greater than
    the Solicitation IRT as described in RFC 3315, section 17.1.2.

diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 7a491fb..31f5bd2 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -27,6 +27,7 @@
 #include "sparse-endian.h"
 #include "sd-event.h"
 #include "list.h"
+#include "macro.h"
 
 typedef struct DHCP6Address DHCP6Address;
 
@@ -69,3 +70,6 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
 int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
                                   const void *packet, size_t len);
+
+const char *dhcp6_message_type_to_string(int s) _const_;
+int dhcp6_message_type_from_string(const char *s) _pure_;
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index d98ae02..7be6424 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -46,6 +46,9 @@ struct sd_dhcp6_client {
         int index;
         struct ether_addr mac_addr;
         DHCP6IA ia_na;
+        be32_t transaction_id;
+        int fd;
+        sd_event_source *receive_message;
         usec_t retransmit_time;
         uint8_t retransmit_count;
         sd_event_source *timeout_resend;
@@ -60,6 +63,24 @@ struct sd_dhcp6_client {
         } _packed_ duid;
 };
 
+const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
+        [DHCP6_SOLICIT] = "SOLICIT",
+        [DHCP6_ADVERTISE] = "ADVERTISE",
+        [DHCP6_REQUEST] = "REQUEST",
+        [DHCP6_CONFIRM] = "CONFIRM",
+        [DHCP6_RENEW] = "RENEW",
+        [DHCP6_REBIND] = "REBIND",
+        [DHCP6_REPLY] = "REPLY",
+        [DHCP6_RELEASE] = "RELEASE",
+        [DHCP6_DECLINE] = "DECLINE",
+        [DHCP6_RECONFIGURE] = "RECONFIGURE",
+        [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
+        [DHCP6_RELAY_FORW] = "RELAY-FORW",
+        [DHCP6_RELAY_REPL] = "RELAY-REPL",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
+
 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
                                  sd_dhcp6_client_cb_t cb, void *userdata)
 {
@@ -108,6 +129,14 @@ static int client_initialize(sd_dhcp6_client *client)
 {
         assert_return(client, -EINVAL);
 
+        client->receive_message =
+                sd_event_source_unref(client->receive_message);
+
+        if (client->fd > 0)
+                client->fd = safe_close(client->fd);
+
+        client->transaction_id = random_u32() & 0x00ffffff;
+
         client->ia_na.timeout_t1 =
                 sd_event_source_unref(client->ia_na.timeout_t1);
         client->ia_na.timeout_t2 =
@@ -134,6 +163,55 @@ static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
         return client;
 }
 
+static int client_send_message(sd_dhcp6_client *client) {
+        _cleanup_free_ DHCP6Message *message = NULL;
+        struct in6_addr all_servers =
+                IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+        size_t len, optlen = 512;
+        uint8_t *opt;
+        int r;
+
+        len = sizeof(DHCP6Message) + optlen;
+
+        message = malloc0(len);
+        if (!message)
+                return -ENOMEM;
+
+        opt = (uint8_t *)(message + 1);
+
+        message->transaction_id = client->transaction_id;
+
+        switch(client->state) {
+        case DHCP6_STATE_SOLICITATION:
+                message->type = DHCP6_SOLICIT;
+
+                r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
+                                        sizeof(client->duid), &client->duid);
+                if (r < 0)
+                        return r;
+
+                r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_RS:
+                return -EINVAL;
+        }
+
+        r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
+                                          len - optlen);
+        if (r < 0)
+                return r;
+
+        log_dhcp6_client(client, "Sent %s",
+                         dhcp6_message_type_to_string(message->type));
+
+        return 0;
+}
+
 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
                                         void *userdata) {
         sd_dhcp6_client *client = userdata;
@@ -187,6 +265,11 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
                 return 0;
         }
 
+        r = client_send_message(client);
+        if (r >= 0)
+                client->retransmit_count++;
+
+
         r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
         if (r < 0)
                 goto error;
@@ -194,6 +277,10 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
         if (!client->retransmit_time) {
                 client->retransmit_time =
                         client_timeout_compute_random(init_retransmit_time);
+
+                if (client->state == DHCP6_STATE_SOLICITATION)
+                        client->retransmit_time += init_retransmit_time / 10;
+
         } else {
                 if (max_retransmit_time &&
                     client->retransmit_time > max_retransmit_time / 2)
@@ -290,6 +377,12 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
         return 0;
 }
 
+static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
+                                  void *userdata)
+{
+        return 0;
+}
+
 static int client_start(sd_dhcp6_client *client)
 {
         int r;
@@ -302,6 +395,23 @@ static int client_start(sd_dhcp6_client *client)
         if (r < 0)
                 return r;
 
+        r = dhcp6_network_bind_udp_socket(client->index, NULL);
+        if (r < 0)
+                return r;
+
+        client->fd = r;
+
+        r = sd_event_add_io(client->event, &client->receive_message,
+                            client->fd, EPOLLIN, client_receive_message,
+                            client);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(client->receive_message,
+                                         client->event_priority);
+        if (r < 0)
+                return r;
+
         client->state = DHCP6_STATE_SOLICITATION;
 
         r = sd_event_add_time(client->event, &client->timeout_resend,

commit 34e8c5a23cd1c53ef3c1169388dabe1f6dfd7319
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:23 2014 +0300

    sd-dhcp6-client: Add functions to bind to DHCPv6 UDP socket
    
    Add a function that creates a UDP socket bound to the given interface
    and optionally to an IPv6 address. Add another function that will
    send the DHCPv6 UDP packet to its destination.
    
    Using IPV6_PKTINFO in setsockopt to bind the IPv6 socket to an
    interface is documented in section 4. of RFC 3542, "Advanced Sockets
    Application Program Interface (API) for IPv6"
    
    Add a define for DHCPv6 Relay Agents and Servers multicast address as
    its not available elsewhere.

diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 30b624d..7a491fb 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -65,3 +65,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
+
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
+int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
+                                  const void *packet, size_t len);
diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c
index 53ce23d..fe56c10 100644
--- a/src/libsystemd-network/dhcp6-network.c
+++ b/src/libsystemd-network/dhcp6-network.c
@@ -31,6 +31,7 @@
 #include "socket-util.h"
 
 #include "dhcp6-internal.h"
+#include "dhcp6-protocol.h"
 
 #define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
         { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
@@ -129,3 +130,65 @@ int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *
 
         return 0;
 }
+
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+        struct in6_pktinfo pktinfo = {
+                .ipi6_ifindex = index,
+        };
+        union sockaddr_union src = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
+                .in6.sin6_addr = IN6ADDR_ANY_INIT,
+        };
+        _cleanup_close_ int s = -1;
+        int r, off = 0, on = 1;
+
+        if (local_address)
+                memcpy(&src.in6.sin6_addr, local_address,
+                       sizeof(src.in6.sin6_addr));
+
+        s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                   IPPROTO_UDP);
+        if (s < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo,
+                       sizeof(pktinfo));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
+        if (r < 0)
+                return -errno;
+
+        r = bind(s, &src.sa, sizeof(src.in6));
+        if (r < 0)
+                return -errno;
+
+        r = s;
+        s = -1;
+        return r;
+}
+
+int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
+                                  const void *packet, size_t len) {
+        union sockaddr_union dest = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
+        };
+        int r;
+
+        assert(server_address);
+
+        memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr));
+
+        r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6));
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index 6ca72ec..c58a07b 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -36,6 +36,10 @@ struct DHCP6Message {
 
 typedef struct DHCP6Message DHCP6Message;
 
+#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
+        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
+
 enum {
         DHCP6_PORT_SERVER                       = 547,
         DHCP6_PORT_CLIENT                       = 546,

commit f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0c
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:20 2014 +0300

    sd-dhcp6-client: Add basic DHCPv6 option handling
    
    Add option appending and parsing. DHCPv6 options are not aligned, thus
    the option handling code must be able to handle options starting at
    any byte boundary.
    
    Add a test case for the basic option handling.

diff --git a/Makefile.am b/Makefile.am
index 2926e98..82f187d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2601,7 +2601,8 @@ libsystemd_network_la_SOURCES = \
 	src/libsystemd-network/sd-dhcp6-client.c \
 	src/libsystemd-network/sd-icmp6-nd.c \
 	src/libsystemd-network/dhcp6-internal.h \
-	src/libsystemd-network/dhcp6-network.c
+	src/libsystemd-network/dhcp6-network.c \
+	src/libsystemd-network/dhcp6-option.c
 
 libsystemd_network_la_LIBADD = \
 	libudev-internal.la \
@@ -2664,6 +2665,7 @@ test_icmp6_rs_LDADD = \
 
 test_dhcp6_client_SOURCES = \
 	src/systemd/sd-dhcp6-client.h \
+	src/libsystemd-network/dhcp6-internal.h \
 	src/libsystemd-network/test-dhcp6-client.c
 
 test_dhcp6_client_LDADD = \
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 1cdb912..30b624d 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -59,3 +59,9 @@ typedef struct DHCP6IA DHCP6IA;
 
 int dhcp_network_icmp6_bind_router_solicitation(int index);
 int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
+
+int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
+                        size_t optlen, const void *optval);
+int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
+int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
+                       size_t *optlen, uint8_t **optvalue);
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
new file mode 100644
index 0000000..cc4d261
--- /dev/null
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -0,0 +1,150 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+#include <errno.h>
+#include <string.h>
+
+#include "sparse-endian.h"
+#include "util.h"
+
+#include "dhcp6-internal.h"
+#include "dhcp6-protocol.h"
+
+#define DHCP6_OPTION_HDR_LEN                    4
+#define DHCP6_OPTION_IA_NA_LEN                  12
+#define DHCP6_OPTION_IA_TA_LEN                  4
+#define DHCP6_OPTION_IAADDR_LEN                 24
+
+static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
+                             size_t optlen) {
+        assert_return(buf, -EINVAL);
+        assert_return(*buf, -EINVAL);
+        assert_return(buflen, -EINVAL);
+
+        if (optlen > 0xffff || *buflen < optlen + DHCP6_OPTION_HDR_LEN)
+                return -ENOBUFS;
+
+        (*buf)[0] = optcode >> 8;
+        (*buf)[1] = optcode & 0xff;
+        (*buf)[2] = optlen >> 8;
+        (*buf)[3] = optlen & 0xff;
+
+        *buf += DHCP6_OPTION_HDR_LEN;
+        *buflen -= DHCP6_OPTION_HDR_LEN;
+
+        return 0;
+}
+
+int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
+                        size_t optlen, const void *optval) {
+        int r;
+
+        assert_return(optval, -EINVAL);
+
+        r = option_append_hdr(buf, buflen, code, optlen);
+        if (r < 0)
+                return r;
+
+        memcpy(*buf, optval, optlen);
+
+        *buf += optlen;
+        *buflen -= optlen;
+
+        return 0;
+}
+
+int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
+        uint16_t len;
+        uint8_t *ia_hdr;
+        size_t ia_buflen, ia_addrlen = 0;
+        DHCP6Address *addr;
+        int r;
+
+        assert_return(buf && *buf && buflen && ia, -EINVAL);
+
+        switch (ia->type) {
+        case DHCP6_OPTION_IA_NA:
+                len = DHCP6_OPTION_IA_NA_LEN;
+                break;
+
+        case DHCP6_OPTION_IA_TA:
+                len = DHCP6_OPTION_IA_TA_LEN;
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        if (*buflen < len)
+                return -ENOBUFS;
+
+        ia_hdr = *buf;
+        ia_buflen = *buflen;
+
+        *buf += DHCP6_OPTION_HDR_LEN;
+        *buflen -= DHCP6_OPTION_HDR_LEN;
+
+        memcpy(*buf, &ia->id, len);
+
+        *buf += len;
+        *buflen -= len;
+
+        LIST_FOREACH(addresses, addr, ia->addresses) {
+                r = option_append_hdr(buf, buflen, DHCP6_OPTION_IAADDR,
+                                      DHCP6_OPTION_IAADDR_LEN);
+                if (r < 0)
+                        return r;
+
+                memcpy(*buf, &addr->address, DHCP6_OPTION_IAADDR_LEN);
+
+                *buf += DHCP6_OPTION_IAADDR_LEN;
+                *buflen -= DHCP6_OPTION_IAADDR_LEN;
+
+                ia_addrlen += DHCP6_OPTION_HDR_LEN + DHCP6_OPTION_IAADDR_LEN;
+        }
+
+        r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
+                       size_t *optlen, uint8_t **optvalue) {
+        assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
+
+        if (*buflen == 0)
+                return -ENOMSG;
+
+        *optcode = (*buf)[0] << 8 | (*buf)[1];
+        *optlen = (*buf)[2] << 8 | (*buf)[3];
+
+        if (*optlen > *buflen - 4)
+                return -ENOBUFS;
+
+        *optvalue = &(*buf)[4];
+        *buflen -= (*optlen + 4);
+        (*buf) += (*optlen + 4);
+
+        return 0;
+}
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index b2dc8ba..b52f407 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -28,6 +28,7 @@
 
 #include "sd-dhcp6-client.h"
 #include "dhcp6-protocol.h"
+#include "dhcp6-internal.h"
 
 static struct ether_addr mac_addr = {
         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
@@ -61,6 +62,72 @@ static int test_client_basic(sd_event *e) {
         return 0;
 }
 
+static int test_option(sd_event *e) {
+        uint8_t packet[] = {
+                'F', 'O', 'O',
+                0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
+                'A', 'B', 'C', 'D', 'E', 'F', 'G',
+                0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
+                '1', '2', '3', '4', '5', '6', '7', '8', '9',
+                'B', 'A', 'R',
+        };
+        uint8_t result[] = {
+                'F', 'O', 'O',
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                'B', 'A', 'R',
+        };
+        uint16_t optcode;
+        size_t optlen;
+        uint8_t *optval, *buf, *out;
+        size_t zero = 0, pos = 3;
+        size_t buflen = sizeof(packet), outlen = sizeof(result);
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        assert_se(buflen == outlen);
+
+        assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
+                                     &optval) == -ENOMSG);
+
+        buflen -= 3;
+        buf = &packet[3];
+        outlen -= 3;
+        out = &result[3];
+
+        assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
+                                     &optval) >= 0);
+        pos += 4 + optlen;
+        assert_se(buf == &packet[pos]);
+        assert_se(optcode == DHCP6_OPTION_ORO);
+        assert_se(optlen == 7);
+        assert_se(buflen + pos == sizeof(packet));
+
+        assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
+                                      optval) >= 0);
+        assert_se(out == &result[pos]);
+        assert_se(*out == 0x00);
+
+        assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
+                                     &optval) >= 0);
+        pos += 4 + optlen;
+        assert_se(buf == &packet[pos]);
+        assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
+        assert_se(optlen == 9);
+        assert_se(buflen + pos == sizeof(packet));
+
+        assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
+                                      optval) >= 0);
+        assert_se(out == &result[pos]);
+        assert_se(*out == 'B');
+
+        assert_se(memcmp(packet, result, sizeof(packet)) == 0);
+
+        return 0;
+}
+
 int main(int argc, char *argv[]) {
         _cleanup_event_unref_ sd_event *e;
 
@@ -71,6 +138,9 @@ int main(int argc, char *argv[]) {
         log_open();
 
         test_client_basic(e);
+        test_option(e);
+
+        assert_se(!sd_event_unref(e));
 
         return 0;
 }

commit d1b0afe3653b4316a6361d204169620726d468a0
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:15 2014 +0300

    sd-dhcp6-client: Add DHCPv6 client Solicitation timeout handling
    
    Add the core of DHCPv6 client message retransmission and upper bound
    timer and message count handling according to RFC 3315 Secions 7.1.2
    and 14. Omit the DHCPv6 initial delay; for now it is assumed that
    systemd-networkd will provide decent startup randomization that will
    desynchronize the clients.
    
    When reinitializing the client, clear all timers.

diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index 0f408bc..6ca72ec 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -41,6 +41,10 @@ enum {
         DHCP6_PORT_CLIENT                       = 546,
 };
 
+#define DHCP6_SOL_MAX_DELAY                     1 * USEC_PER_SEC
+#define DHCP6_SOL_TIMEOUT                       1 * USEC_PER_SEC
+#define DHCP6_SOL_MAX_RT                        120 * USEC_PER_SEC
+
 enum {
         DHCP6_DUID_LLT                          = 1,
         DHCP6_DUID_EN                           = 2,
@@ -51,6 +55,7 @@ enum {
 enum DHCP6State {
         DHCP6_STATE_STOPPED                     = 0,
         DHCP6_STATE_RS                          = 1,
+        DHCP6_STATE_SOLICITATION                = 2,
 };
 
 enum {
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 15d174b..d98ae02 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -46,6 +46,10 @@ struct sd_dhcp6_client {
         int index;
         struct ether_addr mac_addr;
         DHCP6IA ia_na;
+        usec_t retransmit_time;
+        uint8_t retransmit_count;
+        sd_event_source *timeout_resend;
+        sd_event_source *timeout_resend_expire;
         sd_dhcp6_client_cb_t cb;
         void *userdata;
 
@@ -109,6 +113,12 @@ static int client_initialize(sd_dhcp6_client *client)
         client->ia_na.timeout_t2 =
                 sd_event_source_unref(client->ia_na.timeout_t2);
 
+        client->retransmit_time = 0;
+        client->retransmit_count = 0;
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+        client->timeout_resend_expire =
+                sd_event_source_unref(client->timeout_resend_expire);
+
         client->state = DHCP6_STATE_STOPPED;
 
         return 0;
@@ -124,6 +134,118 @@ static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
         return client;
 }
 
+static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
+                                        void *userdata) {
+        sd_dhcp6_client *client = userdata;
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
+
+        return 0;
+}
+
+static usec_t client_timeout_compute_random(usec_t val) {
+        return val - val / 10 +
+                (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
+}
+
+static int client_timeout_resend(sd_event_source *s, uint64_t usec,
+                                 void *userdata) {
+        int r = 0;
+        sd_dhcp6_client *client = userdata;
+        usec_t time_now, init_retransmit_time, max_retransmit_time;
+        usec_t max_retransmit_duration;
+        uint8_t max_retransmit_count;
+        char time_string[FORMAT_TIMESPAN_MAX];
+
+        assert(s);
+        assert(client);
+        assert(client->event);
+
+        client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
+        switch (client->state) {
+        case DHCP6_STATE_SOLICITATION:
+                init_retransmit_time = DHCP6_SOL_TIMEOUT;
+                max_retransmit_time = DHCP6_SOL_MAX_RT;
+                max_retransmit_count = 0;
+                max_retransmit_duration = 0;
+
+                break;
+
+        case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_RS:
+                return 0;
+        }
+
+        if (max_retransmit_count &&
+            client->retransmit_count >= max_retransmit_count) {
+                client_stop(client, DHCP6_EVENT_RETRANS_MAX);
+                return 0;
+        }
+
+        r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
+        if (r < 0)
+                goto error;
+
+        if (!client->retransmit_time) {
+                client->retransmit_time =
+                        client_timeout_compute_random(init_retransmit_time);
+        } else {
+                if (max_retransmit_time &&
+                    client->retransmit_time > max_retransmit_time / 2)
+                        client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
+                else
+                        client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
+        }
+
+        log_dhcp6_client(client, "Next retransmission in %s",
+                         format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                                         client->retransmit_time, 0));
+
+        r = sd_event_add_time(client->event, &client->timeout_resend,
+                              CLOCK_MONOTONIC,
+                              time_now + client->retransmit_time,
+                              10 * USEC_PER_MSEC, client_timeout_resend,
+                              client);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(client->timeout_resend,
+                                         client->event_priority);
+        if (r < 0)
+                goto error;
+
+        if (max_retransmit_duration && !client->timeout_resend_expire) {
+
+                log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
+                                 max_retransmit_duration / USEC_PER_SEC);
+
+                r = sd_event_add_time(client->event,
+                                      &client->timeout_resend_expire,
+                                      CLOCK_MONOTONIC,
+                                      time_now + max_retransmit_duration,
+                                      USEC_PER_SEC,
+                                      client_timeout_resend_expire, client);
+                if (r < 0)
+                        goto error;
+
+                r = sd_event_source_set_priority(client->timeout_resend_expire,
+                                                 client->event_priority);
+                if (r < 0)
+                        goto error;
+        }
+
+error:
+        if (r < 0)
+                client_stop(client, r);
+
+        return 0;
+}
+
 static int client_ensure_iaid(sd_dhcp6_client *client) {
         const char *name = NULL;
         uint64_t id;
@@ -180,6 +302,19 @@ static int client_start(sd_dhcp6_client *client)
         if (r < 0)
                 return r;
 
+        client->state = DHCP6_STATE_SOLICITATION;
+
+        r = sd_event_add_time(client->event, &client->timeout_resend,
+                              CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
+                              client);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_priority(client->timeout_resend,
+                                         client->event_priority);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 4965011..72267f9 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -28,6 +28,8 @@
 
 enum {
         DHCP6_EVENT_STOP                        = 0,
+        DHCP6_EVENT_RESEND_EXPIRE               = 10,
+        DHCP6_EVENT_RETRANS_MAX                 = 11,
 };
 
 typedef struct sd_dhcp6_client sd_dhcp6_client;

commit 813e3a6ffcd094696001716480bbd68008cc54c8
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:12 2014 +0300

    sd-dhcp6-client: Add basic DHCPv6 test cases
    
    Add test cases for basic DHCPv6 client handling, e.g. setting
    interface index, mac address and attaching event loop.

diff --git a/Makefile.am b/Makefile.am
index 71c16cb..2926e98 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2662,12 +2662,22 @@ test_icmp6_rs_LDADD = \
 	libsystemd-internal.la \
 	libsystemd-shared.la
 
+test_dhcp6_client_SOURCES = \
+	src/systemd/sd-dhcp6-client.h \
+	src/libsystemd-network/test-dhcp6-client.c
+
+test_dhcp6_client_LDADD = \
+	libsystemd-network.la \
+	libsystemd-internal.la \
+	libsystemd-shared.la
+
 tests += \
 	test-dhcp-option \
 	test-dhcp-client \
 	test-dhcp-server \
 	test-ipv4ll \
-	test-icmp6-rs
+	test-icmp6-rs \
+	test-dhcp6-client
 
 # ------------------------------------------------------------------------------
 if ENABLE_GTK_DOC
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
new file mode 100644
index 0000000..b2dc8ba
--- /dev/null
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -0,0 +1,76 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "macro.h"
+#include "sd-event.h"
+#include "event-util.h"
+
+#include "sd-dhcp6-client.h"
+#include "dhcp6-protocol.h"
+
+static struct ether_addr mac_addr = {
+        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+};
+
+static bool verbose = false;
+
+static int test_client_basic(sd_event *e) {
+        sd_dhcp6_client *client;
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        assert_se(sd_dhcp6_client_new(&client) >= 0);
+        assert_se(client);
+
+        assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
+
+        assert_se(sd_dhcp6_client_set_index(client, 15) == 0);
+        assert_se(sd_dhcp6_client_set_index(client, -42) == -EINVAL);
+        assert_se(sd_dhcp6_client_set_index(client, -1) == 0);
+        assert_se(sd_dhcp6_client_set_index(client, 42) >= 0);
+
+        assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0);
+
+        assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
+
+        assert_se(sd_dhcp6_client_detach_event(client) >= 0);
+        assert_se(!sd_dhcp6_client_unref(client));
+
+        return 0;
+}
+
+int main(int argc, char *argv[]) {
+        _cleanup_event_unref_ sd_event *e;
+
+        assert_se(sd_event_new(&e) >= 0);
+
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        test_client_basic(e);
+
+        return 0;
+}

commit f12abb48fc510b8b349c05e35ba048134debaf25
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:39:08 2014 +0300

    sd-dhcp6-client: Add DHCPv6 IAID functionality
    
    Create structures describing Identity Association IDentifiers and
    IPv6 lease addresses.
    
    [tomegun: initialize the IAID when client is started. Base this off of the
    predictable udev names, if available, as these satisfy the requirement of
    the IAID, and base it off the mac addres otherwise, as that is the best we
    have.]

diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 52283d7..1cdb912 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -22,6 +22,38 @@
 ***/
 
 #include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include "sparse-endian.h"
+#include "sd-event.h"
+#include "list.h"
+
+typedef struct DHCP6Address DHCP6Address;
+
+struct DHCP6Address {
+        LIST_FIELDS(DHCP6Address, addresses);
+
+        struct {
+                struct in6_addr address;
+                be32_t lifetime_preferred;
+                be32_t lifetime_valid;
+        } _packed_;
+};
+
+struct DHCP6IA {
+        uint16_t type;
+        struct {
+                be32_t id;
+                be32_t lifetime_t1;
+                be32_t lifetime_t2;
+        } _packed_;
+        sd_event_source *timeout_t1;
+        sd_event_source *timeout_t2;
+
+        LIST_HEAD(DHCP6Address, addresses);
+};
+
+typedef struct DHCP6IA DHCP6IA;
 
 #define log_dhcp6_client(p, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
 
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 8718324..15d174b 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -22,12 +22,17 @@
 #include <errno.h>
 #include <string.h>
 
+#include "udev.h"
+#include "udev-util.h"
+#include "virt.h"
 #include "siphash24.h"
 #include "util.h"
 #include "refcnt.h"
 
+#include "network-internal.h"
 #include "sd-dhcp6-client.h"
 #include "dhcp6-protocol.h"
+#include "dhcp6-internal.h"
 
 #define SYSTEMD_PEN 43793
 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
@@ -40,6 +45,7 @@ struct sd_dhcp6_client {
         int event_priority;
         int index;
         struct ether_addr mac_addr;
+        DHCP6IA ia_na;
         sd_dhcp6_client_cb_t cb;
         void *userdata;
 
@@ -98,6 +104,11 @@ static int client_initialize(sd_dhcp6_client *client)
 {
         assert_return(client, -EINVAL);
 
+        client->ia_na.timeout_t1 =
+                sd_event_source_unref(client->ia_na.timeout_t1);
+        client->ia_na.timeout_t2 =
+                sd_event_source_unref(client->ia_na.timeout_t2);
+
         client->state = DHCP6_STATE_STOPPED;
 
         return 0;
@@ -113,6 +124,65 @@ static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
         return client;
 }
 
+static int client_ensure_iaid(sd_dhcp6_client *client) {
+        const char *name = NULL;
+        uint64_t id;
+
+        assert(client);
+
+        if (client->ia_na.id)
+                return 0;
+
+        if (detect_container(NULL) <= 0) {
+                /* not in a container, udev will be around */
+                _cleanup_udev_unref_ struct udev *udev;
+                _cleanup_udev_device_unref_ struct udev_device *device;
+                char ifindex_str[2 + DECIMAL_STR_MAX(int)];
+
+                udev = udev_new();
+                if (!udev)
+                        return -ENOMEM;
+
+                sprintf(ifindex_str, "n%d", client->index);
+                device = udev_device_new_from_device_id(udev, ifindex_str);
+                if (!device)
+                        return -errno;
+
+                if (udev_device_get_is_initialized(device) <= 0)
+                        /* not yet ready */
+                        return -EBUSY;
+
+                name = net_get_name(device);
+        }
+
+        if (name)
+                siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
+        else
+                /* fall back to mac address if no predictable name available */
+                siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
+                          HASH_KEY.bytes);
+
+        /* fold into 32 bits */
+        client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
+
+        return 0;
+}
+
+static int client_start(sd_dhcp6_client *client)
+{
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->event, -EINVAL);
+        assert_return(client->index > 0, -EINVAL);
+
+        r = client_ensure_iaid(client);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
 {
         client_stop(client, DHCP6_EVENT_STOP);
@@ -128,7 +198,11 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
         assert_return(client->event, -EINVAL);
         assert_return(client->index > 0, -EINVAL);
 
-        return r;
+        r = client_initialize(client);
+        if (r < 0)
+                return r;
+
+        return client_start(client);
 }
 
 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
@@ -176,7 +250,6 @@ sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
 
 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
         if (client && REFCNT_DEC(client->n_ref) <= 0) {
-
                 client_initialize(client);
 
                 sd_dhcp6_client_detach_event(client);
@@ -206,6 +279,8 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret)
 
         client->n_ref = REFCNT_INIT;
 
+        client->ia_na.type = DHCP6_OPTION_IA_NA;
+
         client->index = -1;
 
         /* initialize DUID */

commit fc5414305d04799e0bdb6f8ca46ec70acd487f89
Author: Tom Gundersen <teg at jklm.no>
Date:   Thu Jun 19 15:39:05 2014 +0300

    network-internal: split out net_get_name()

diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index 2e9667c..97217c1 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -34,21 +34,33 @@
 #include "conf-parser.h"
 #include "condition.h"
 
-#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
-
-int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) {
-        size_t l, sz = 0;
+const char *net_get_name(struct udev_device *device) {
         const char *name = NULL, *field = NULL;
-        int r;
-        uint8_t *v;
+
+        assert(device);
 
         /* fetch some persistent data unique (on this machine) to this device */
-        FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") {
+        FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT",
+                       "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") {
                 name = udev_device_get_property_value(device, field);
                 if (name)
                         break;
         }
 
+        return name;
+}
+
+#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
+
+int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) {
+        size_t l, sz = 0;
+        const char *name = NULL;
+        int r;
+        uint8_t *v;
+
+        assert(device);
+
+        name = net_get_name(device);
         if (!name)
                 return -ENOENT;
 
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index 2aeecf0..db48c2c 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -65,6 +65,7 @@ int config_parse_ifalias(const char *unit, const char *filename, unsigned line,
 int net_parse_inaddr(const char *address, unsigned char *family, void *dst);
 
 int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]);
+const char *net_get_name(struct udev_device *device);
 
 void serialize_in_addrs(FILE *f, const char *key, struct in_addr *addresses, size_t size);
 int deserialize_in_addrs(struct in_addr **addresses, size_t *size, const char *string);

commit a276e6d68606861b552140cbcc003f4af10626fc
Author: Tom Gundersen <teg at jklm.no>
Date:   Thu Jun 19 15:39:01 2014 +0300

    sd-dhcp6-client: Initialize DUID
    
    Initialize DHCP Unique Identifier when creating the client. The
    DUID is generated based on the machine-id, which satisfies all the
    requirements of what an DUID should be. The DUID type is DUID-EN.
    
    Based on patch by Patrik Flykt.

diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 5063d4a..8718324 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -22,12 +22,16 @@
 #include <errno.h>
 #include <string.h>
 
+#include "siphash24.h"
 #include "util.h"
 #include "refcnt.h"
 
 #include "sd-dhcp6-client.h"
 #include "dhcp6-protocol.h"
 
+#define SYSTEMD_PEN 43793
+#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+
 struct sd_dhcp6_client {
         RefCount n_ref;
 
@@ -38,6 +42,12 @@ struct sd_dhcp6_client {
         struct ether_addr mac_addr;
         sd_dhcp6_client_cb_t cb;
         void *userdata;
+
+        struct duid_en {
+                uint16_t type; /* DHCP6_DUID_EN */
+                uint32_t pen;
+                uint8_t id[8];
+        } _packed_ duid;
 };
 
 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
@@ -185,6 +195,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
 {
         _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
+        sd_id128_t machine_id;
+        int r;
 
         assert_return(ret, -EINVAL);
 
@@ -196,6 +208,19 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret)
 
         client->index = -1;
 
+        /* initialize DUID */
+        client->duid.type = htobe16(DHCP6_DUID_EN);
+        client->duid.pen = htobe32(SYSTEMD_PEN);
+
+        r = sd_id128_get_machine(&machine_id);
+        if (r < 0)
+                return r;
+
+        /* a bit of snake-oil perhaps, but no need to expose the machine-id
+           directly */
+        siphash24(client->duid.id, &machine_id, sizeof(machine_id),
+                  HASH_KEY.bytes);
+
         *ret = client;
         client = NULL;
 

commit f20a35cc0d537dd4cfc1054cf7936b04a1700f3a
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:38:59 2014 +0300

    sd-icmp6-nd: Add initial Router Advertisement test case
    
    Feed a Router Advertisement to the code and expect proper events
    each time. The sending part is ignored, as all of it is static code
    in the real dhcp_network_icmp6_send_rs() function.

diff --git a/Makefile.am b/Makefile.am
index 35190e5..71c16cb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2651,11 +2651,23 @@ test_ipv4ll_LDADD = \
 	libsystemd-internal.la \
 	libsystemd-shared.la
 
+test_icmp6_rs_SOURCES = \
+	src/systemd/sd-dhcp6-client.h \
+	src/libsystemd-network/sd-icmp6-nd.h \
+	src/libsystemd-network/dhcp6-internal.h \
+	src/libsystemd-network/test-icmp6-rs.c
+
+test_icmp6_rs_LDADD = \
+	libsystemd-network.la \
+	libsystemd-internal.la \
+	libsystemd-shared.la
+
 tests += \
 	test-dhcp-option \
 	test-dhcp-client \
 	test-dhcp-server \
-	test-ipv4ll
+	test-ipv4ll \
+	test-icmp6-rs
 
 # ------------------------------------------------------------------------------
 if ENABLE_GTK_DOC
diff --git a/src/libsystemd-network/test-icmp6-rs.c b/src/libsystemd-network/test-icmp6-rs.c
new file mode 100644
index 0000000..86e02cc
--- /dev/null
+++ b/src/libsystemd-network/test-icmp6-rs.c
@@ -0,0 +1,155 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/icmp6.h>
+
+#include "socket-util.h"
+
+#include "dhcp6-internal.h"
+#include "sd-icmp6-nd.h"
+
+static struct ether_addr mac_addr = {
+        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+};
+
+static bool verbose = false;
+static sd_event_source *test_hangcheck;
+static int test_fd[2];
+
+static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
+                             void *userdata) {
+        assert(false);
+
+        return 0;
+}
+
+int dhcp_network_icmp6_bind_router_solicitation(int index) {
+        assert(index == 42);
+
+        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0)
+                return -errno;
+
+        return test_fd[0];
+}
+
+static int send_ra(uint8_t flags) {
+        uint8_t advertisement[] = {
+                0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
+                0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
+                0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+                0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+                0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+                0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
+                0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
+        };
+
+        advertisement[5] = flags;
+
+        assert(write(test_fd[1], advertisement, sizeof(advertisement)) ==
+               sizeof(advertisement));
+
+        if (verbose)
+                printf("  sent RA with flag 0x%02x\n", flags);
+
+        return 0;
+}
+
+int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
+        return send_ra(0);
+}
+
+static void test_rs_done(sd_icmp6_nd *nd, int event, void *userdata) {
+        sd_event *e = userdata;
+        static int idx = 0;
+        struct {
+                uint8_t flag;
+                int event;
+        } flag_event[] = {
+                { 0, ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE },
+                { ND_RA_FLAG_OTHER, ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER },
+                { ND_RA_FLAG_MANAGED, ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED }
+        };
+        assert(nd);
+
+        assert(event == flag_event[idx].event);
+        idx++;
+
+        if (verbose)
+                printf("  got event %d\n", event);
+
+        if (idx < 3)
+                send_ra(flag_event[idx].flag);
+        else
+                sd_event_exit(e, 0);
+}
+
+static void test_rs(sd_event *e) {
+        usec_t time_now = now(CLOCK_MONOTONIC);
+        sd_icmp6_nd *nd;
+
+        if (verbose)
+                printf("* %s\n", __FUNCTION__);
+
+        assert(sd_icmp6_nd_new(&nd) >= 0);
+        assert(nd);
+
+        assert(sd_icmp6_nd_attach_event(nd, e, 0) >= 0);
+
+        assert(sd_icmp6_nd_set_index(nd, 42) >= 0);
+        assert(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0);
+        assert(sd_icmp6_nd_set_callback(nd, test_rs_done, e) >= 0);
+
+        assert(sd_event_add_time(e, &test_hangcheck, CLOCK_MONOTONIC,
+                                 time_now + 2 *USEC_PER_SEC, 0,
+                                 test_rs_hangcheck, NULL) >= 0);
+
+        assert(sd_icmp6_router_solicitation_start(nd) >= 0);
+
+        sd_event_loop(e);
+
+        test_hangcheck = sd_event_source_unref(test_hangcheck);
+
+        nd = sd_icmp6_nd_unref(nd);
+        assert(!nd);
+
+        close(test_fd[0]);
+        close(test_fd[1]);
+}
+
+int main(int argc, char *argv[]) {
+        sd_event *e;
+
+        assert(sd_event_new(&e) >= 0);
+
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        test_rs(e);
+
+        return 0;
+}

commit e3169126793f43be3d840874ffb3935a51097001
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:38:55 2014 +0300

    sd-icmp6-nd: Add Router Solicitation and Advertisement support
    
    Provide functions to bind the ICMPv6 socket to the approriate interface
    and set multicast sending and receiving according to RFC 3493, section
    5.2. and RFC 3542, sections 3. and 3.3. Filter out all ICMPv6 messages
    except Router Advertisements for the socket in question according to
    RFC 3542, section 3.2.
    
    Send Router Solicitations to the all routers multicast group as
    described in RFC 4861, section 6. and act on the received Router
    Advertisments according to section 6.3.7.
    
    Implement a similar API for ICMPv6 handling as is done for DHCPv4 and
    DHCPv6.

diff --git a/Makefile.am b/Makefile.am
index d7fa9da..35190e5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2595,7 +2595,13 @@ libsystemd_network_la_SOURCES = \
 	src/libsystemd-network/ipv4ll-packet.c \
 	src/libsystemd-network/ipv4ll-internal.h \
 	src/libsystemd-network/network-internal.c \
-	src/libsystemd-network/network-internal.h
+	src/libsystemd-network/network-internal.h \
+	src/systemd/sd-dhcp6-client.h \
+	src/systemd/sd-icmp6-nd.h \
+	src/libsystemd-network/sd-dhcp6-client.c \
+	src/libsystemd-network/sd-icmp6-nd.c \
+	src/libsystemd-network/dhcp6-internal.h \
+	src/libsystemd-network/dhcp6-network.c
 
 libsystemd_network_la_LIBADD = \
 	libudev-internal.la \
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
new file mode 100644
index 0000000..52283d7
--- /dev/null
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/ethernet.h>
+
+#define log_dhcp6_client(p, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
+
+int dhcp_network_icmp6_bind_router_solicitation(int index);
+int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c
new file mode 100644
index 0000000..53ce23d
--- /dev/null
+++ b/src/libsystemd-network/dhcp6-network.c
@@ -0,0 +1,131 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <linux/if_packet.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+
+#include "socket-util.h"
+
+#include "dhcp6-internal.h"
+
+#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
+        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
+
+#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
+        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
+
+int dhcp_network_icmp6_bind_router_solicitation(int index)
+{
+        struct icmp6_filter filter = { };
+        struct ipv6_mreq mreq = {
+                .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+                .ipv6mr_interface = index,
+        };
+        _cleanup_close_ int s = -1;
+        int r, zero = 0, hops = 255;
+
+        s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                   IPPROTO_ICMPV6);
+        if (s < 0)
+                return -errno;
+
+        ICMP6_FILTER_SETBLOCKALL(&filter);
+        ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+        r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
+                       sizeof(filter));
+        if (r < 0)
+                return -errno;
+
+        /* RFC 3315, section 6.7, bullet point 2 may indicate that an
+           IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
+           Empirical experiments indicates otherwise and therefore an
+           IPV6_MULTICAST_IF socket option is used here instead */
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index,
+                       sizeof(index));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero,
+                       sizeof(zero));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
+                       sizeof(hops));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+                       sizeof(mreq));
+        if (r < 0)
+                return -errno;
+
+        r = s;
+        s = -1;
+        return r;
+}
+
+int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr)
+{
+        struct sockaddr_in6 dst = {
+                .sin6_family = AF_INET6,
+                .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+        };
+        struct {
+                struct nd_router_solicit rs;
+                struct nd_opt_hdr rs_opt;
+                struct ether_addr rs_opt_mac;
+        } _packed_ rs = {
+                .rs.nd_rs_type = ND_ROUTER_SOLICIT,
+        };
+        struct iovec iov[1] = {
+                { &rs, },
+        };
+        struct msghdr msg = {
+                .msg_name = &dst,
+                .msg_namelen = sizeof(dst),
+                .msg_iov = iov,
+                .msg_iovlen = 1,
+        };
+        int r;
+
+        if (ether_addr) {
+                memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN);
+                rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+                rs.rs_opt.nd_opt_len = 1;
+                iov[0].iov_len = sizeof(rs);
+        } else
+                iov[0].iov_len = sizeof(rs.rs);
+
+        r = sendmsg(s, &msg, 0);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c
new file mode 100644
index 0000000..f820a9c
--- /dev/null
+++ b/src/libsystemd-network/sd-icmp6-nd.c
@@ -0,0 +1,325 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/icmp6.h>
+#include <string.h>
+#include <stdbool.h>
+#include <netinet/in.h>
+
+#include "socket-util.h"
+#include "refcnt.h"
+#include "async.h"
+
+#include "dhcp6-internal.h"
+#include "sd-icmp6-nd.h"
+
+#define ICMP6_ROUTER_SOLICITATION_INTERVAL      4 * USEC_PER_SEC
+#define ICMP6_MAX_ROUTER_SOLICITATIONS          3
+
+enum icmp6_nd_state {
+        ICMP6_NEIGHBOR_DISCOVERY_IDLE           = 0,
+        ICMP6_ROUTER_SOLICITATION_SENT          = 10,
+        ICMP6_ROUTER_ADVERTISMENT_LISTEN        = 11,
+};
+
+struct sd_icmp6_nd {
+        RefCount n_ref;
+
+        enum icmp6_nd_state state;
+        sd_event *event;
+        int event_priority;
+        int index;
+        struct ether_addr mac_addr;
+        int fd;
+        sd_event_source *recv;
+        sd_event_source *timeout;
+        int nd_sent;
+        sd_icmp6_nd_callback_t callback;
+        void *userdata;
+};
+
+#define log_icmp6_nd(p, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__)
+
+static void icmp6_nd_notify(sd_icmp6_nd *nd, int event)
+{
+        if (nd->callback)
+                nd->callback(nd, event, nd->userdata);
+}
+
+int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t callback,
+                             void *userdata) {
+        assert(nd);
+
+        nd->callback = callback;
+        nd->userdata = userdata;
+
+        return 0;
+}
+
+int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index) {
+        assert(nd);
+        assert(interface_index >= -1);
+
+        nd->index = interface_index;
+
+        return 0;
+}
+
+int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr) {
+        assert(nd);
+
+        if (mac_addr)
+                memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
+        else
+                memset(&nd->mac_addr, 0x00, sizeof(nd->mac_addr));
+
+        return 0;
+
+}
+
+int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority) {
+        int r;
+
+        assert_return(nd, -EINVAL);
+        assert_return(!nd->event, -EBUSY);
+
+        if (event)
+                nd->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&nd->event);
+                if (r < 0)
+                        return 0;
+        }
+
+        nd->event_priority = priority;
+
+        return 0;
+}
+
+int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd) {
+        assert_return(nd, -EINVAL);
+
+        nd->event = sd_event_unref(nd->event);
+
+        return 0;
+}
+
+sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) {
+        assert(nd);
+
+        return nd->event;
+}
+
+sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) {
+        assert (nd);
+
+        assert_se(REFCNT_INC(nd->n_ref) >= 2);
+
+        return nd;
+}
+
+static int icmp6_nd_init(sd_icmp6_nd *nd) {
+        assert(nd);
+
+        nd->recv = sd_event_source_unref(nd->recv);
+        nd->fd = asynchronous_close(nd->fd);
+        nd->timeout = sd_event_source_unref(nd->timeout);
+
+        return 0;
+}
+
+sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) {
+        if (nd && REFCNT_DEC(nd->n_ref) <= 0) {
+
+                icmp6_nd_init(nd);
+                sd_icmp6_nd_detach_event(nd);
+
+                free(nd);
+        }
+
+        return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_icmp6_nd*, sd_icmp6_nd_unref);
+#define _cleanup_sd_icmp6_nd_free_ _cleanup_(sd_icmp6_nd_unrefp)
+
+int sd_icmp6_nd_new(sd_icmp6_nd **ret) {
+        _cleanup_sd_icmp6_nd_free_ sd_icmp6_nd *nd = NULL;
+
+        assert(ret);
+
+        nd = new0(sd_icmp6_nd, 1);
+        if (!nd)
+                return -ENOMEM;
+
+        nd->n_ref = REFCNT_INIT;
+
+        nd->index = -1;
+
+        *ret = nd;
+        nd = NULL;
+
+        return 0;
+}
+
+static int icmp6_router_advertisment_recv(sd_event_source *s, int fd,
+                                          uint32_t revents, void *userdata)
+{
+        sd_icmp6_nd *nd = userdata;
+        ssize_t len;
+        struct nd_router_advert ra;
+        int event = ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE;
+
+        assert(s);
+        assert(nd);
+        assert(nd->event);
+
+        /* only interested in Managed/Other flag */
+        len = read(fd, &ra, sizeof(ra));
+        if ((size_t)len < sizeof(ra))
+                return 0;
+
+        if (ra.nd_ra_type != ND_ROUTER_ADVERT)
+                return 0;
+
+        if (ra.nd_ra_code != 0)
+                return 0;
+
+        nd->timeout = sd_event_source_unref(nd->timeout);
+
+        nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
+
+        if (ra.nd_ra_flags_reserved & ND_RA_FLAG_OTHER )
+                event = ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER;
+
+        if (ra.nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
+                event = ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED;
+
+        log_icmp6_nd(nd, "Received Router Advertisment flags %s/%s",
+                     (ra.nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)? "MANAGED":
+                     "none",
+                     (ra.nd_ra_flags_reserved & ND_RA_FLAG_OTHER)? "OTHER":
+                     "none");
+
+        icmp6_nd_notify(nd, event);
+
+        return 0;
+}
+
+static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
+                                             void *userdata)
+{
+        sd_icmp6_nd *nd = userdata;
+        uint64_t time_now, next_timeout;
+        struct ether_addr unset = { };
+        struct ether_addr *addr = NULL;
+        int r;
+
+        assert(s);
+        assert(nd);
+        assert(nd->event);
+
+        nd->timeout = sd_event_source_unref(nd->timeout);
+
+        if (nd->nd_sent >= ICMP6_MAX_ROUTER_SOLICITATIONS) {
+                icmp6_nd_notify(nd, ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT);
+                nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
+        } else {
+                if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
+                        addr = &nd->mac_addr;
+
+                r = dhcp_network_icmp6_send_router_solicitation(nd->fd, addr);
+                if (r < 0)
+                        log_icmp6_nd(nd, "Error sending Router Solicitation");
+                else {
+                        nd->state = ICMP6_ROUTER_SOLICITATION_SENT;
+                        log_icmp6_nd(nd, "Sent Router Solicitation");
+                }
+
+                nd->nd_sent++;
+
+                r = sd_event_now(nd->event, CLOCK_MONOTONIC, &time_now);
+                if (r < 0) {
+                        icmp6_nd_notify(nd, r);
+                        return 0;
+                }
+
+                next_timeout = time_now + ICMP6_ROUTER_SOLICITATION_INTERVAL;
+
+                r = sd_event_add_time(nd->event, &nd->timeout, CLOCK_MONOTONIC,
+                                      next_timeout, 0,
+                                      icmp6_router_solicitation_timeout, nd);
+                if (r < 0) {
+                        icmp6_nd_notify(nd, r);
+                        return 0;
+                }
+
+                r = sd_event_source_set_priority(nd->timeout,
+                                                 nd->event_priority);
+                if (r < 0) {
+                        icmp6_nd_notify(nd, r);
+                        return 0;
+                }
+        }
+
+        return 0;
+}
+
+int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) {
+        int r;
+
+        assert(nd);
+        assert(nd->event);
+
+        if (nd->state != ICMP6_NEIGHBOR_DISCOVERY_IDLE)
+                return -EINVAL;
+
+        if (nd->index < 1)
+                return -EINVAL;
+
+        r = dhcp_network_icmp6_bind_router_solicitation(nd->index);
+        if (r < 0)
+                return r;
+
+        nd->fd = r;
+
+        r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
+                            icmp6_router_advertisment_recv, nd);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(nd->recv, nd->event_priority);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_add_time(nd->event, &nd->timeout, CLOCK_MONOTONIC,
+                              0, 0, icmp6_router_solicitation_timeout, nd);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
+
+error:
+        if (r < 0)
+                icmp6_nd_init(nd);
+        else
+                log_icmp6_nd(client, "Start Router Solicitation");
+
+        return r;
+}
diff --git a/src/systemd/sd-icmp6-nd.h b/src/systemd/sd-icmp6-nd.h
new file mode 100644
index 0000000..5e9dc0a
--- /dev/null
+++ b/src/systemd/sd-icmp6-nd.h
@@ -0,0 +1,56 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdicmp6ndfoo
+#define foosdicmp6ndfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+enum {
+        ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE    = 0,
+        ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1,
+        ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER   = 2,
+        ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3,
+};
+
+typedef struct sd_icmp6_nd sd_icmp6_nd;
+
+typedef void(*sd_icmp6_nd_callback_t)(sd_icmp6_nd *nd, int event,
+                                      void *userdata);
+
+int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t cb,
+                             void *userdata);
+int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index);
+int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr);
+
+int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority);
+int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd);
+sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd);
+
+sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd);
+sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd);
+int sd_icmp6_nd_new(sd_icmp6_nd **ret);
+
+int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd);
+
+#endif

commit 139b011ab81ccea1d51f09e0261a1c390115c6ff
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Thu Jun 19 15:38:50 2014 +0300

    sd-dhcp6-client: Add initial DHCPv6 client files
    
    Add initial structure definition and functions for setting index, MAC
    address, callback and event loop. Define protocol values and states.

diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
new file mode 100644
index 0000000..0f408bc
--- /dev/null
+++ b/src/libsystemd-network/dhcp6-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) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+#include "sparse-endian.h"
+
+struct DHCP6Message {
+        union {
+                struct {
+                        uint8_t type;
+                        uint8_t _pad[3];
+                } _packed_;
+                be32_t transaction_id;
+        };
+} _packed_;
+
+typedef struct DHCP6Message DHCP6Message;
+
+enum {
+        DHCP6_PORT_SERVER                       = 547,
+        DHCP6_PORT_CLIENT                       = 546,
+};
+
+enum {
+        DHCP6_DUID_LLT                          = 1,
+        DHCP6_DUID_EN                           = 2,
+        DHCP6_DUID_LL                           = 3,
+        DHCP6_DUID_UUID                         = 4,
+};
+
+enum DHCP6State {
+        DHCP6_STATE_STOPPED                     = 0,
+        DHCP6_STATE_RS                          = 1,
+};
+
+enum {
+        DHCP6_SOLICIT                           = 1,
+        DHCP6_ADVERTISE                         = 2,
+        DHCP6_REQUEST                           = 3,
+        DHCP6_CONFIRM                           = 4,
+        DHCP6_RENEW                             = 5,
+        DHCP6_REBIND                            = 6,
+        DHCP6_REPLY                             = 7,
+        DHCP6_RELEASE                           = 8,
+        DHCP6_DECLINE                           = 9,
+        DHCP6_RECONFIGURE                       = 10,
+        DHCP6_INFORMATION_REQUEST               = 11,
+        DHCP6_RELAY_FORW                        = 12,
+        DHCP6_RELAY_REPL                        = 13,
+        _DHCP6_MESSAGE_MAX                      = 14,
+};
+
+enum {
+        DHCP6_OPTION_CLIENTID                   = 1,
+        DHCP6_OPTION_SERVERID                   = 2,
+        DHCP6_OPTION_IA_NA                      = 3,
+        DHCP6_OPTION_IA_TA                      = 4,
+        DHCP6_OPTION_IAADDR                     = 5,
+        DHCP6_OPTION_ORO                        = 6,
+        DHCP6_OPTION_PREFERENCE                 = 7,
+        DHCP6_OPTION_ELAPSED_TIME               = 8,
+        DHCP6_OPTION_RELAY_MSG                  = 9,
+        /* option code 10 is unassigned */
+        DHCP6_OPTION_AUTH                       = 11,
+        DHCP6_OPTION_UNICAST                    = 12,
+        DHCP6_OPTION_STATUS_CODE                = 13,
+        DHCP6_OPTION_RAPID_COMMIT               = 14,
+        DHCP6_OPTION_USER_CLASS                 = 15,
+        DHCP6_OPTION_VENDOR_CLASS               = 16,
+        DHCP6_OPTION_VENDOR_OPTS                = 17,
+        DHCP6_OPTION_INTERFACE_ID               = 18,
+        DHCP6_OPTION_RECONF_MSG                 = 19,
+        DHCP6_OPTION_RECONF_ACCEPT              = 20,
+};
+
+enum {
+        DHCP6_STATUS_SUCCESS                    = 0,
+        DHCP6_STATUS_UNSPEC_FAIL                = 1,
+        DHCP6_STATUS_NO_ADDRS_AVAIL             = 2,
+        DHCP6_STATUS_NO_BINDING                 = 3,
+        DHCP6_STATUS_NOT_ON_LINK                = 4,
+        DHCP6_STATUS_USE_MULTICAST              = 5,
+        _DHCP6_STATUS_MAX                       = 6,
+};
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
new file mode 100644
index 0000000..5063d4a
--- /dev/null
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -0,0 +1,203 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+
+#include "util.h"
+#include "refcnt.h"
+
+#include "sd-dhcp6-client.h"
+#include "dhcp6-protocol.h"
+
+struct sd_dhcp6_client {
+        RefCount n_ref;
+
+        enum DHCP6State state;
+        sd_event *event;
+        int event_priority;
+        int index;
+        struct ether_addr mac_addr;
+        sd_dhcp6_client_cb_t cb;
+        void *userdata;
+};
+
+int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
+                                 sd_dhcp6_client_cb_t cb, void *userdata)
+{
+        assert_return(client, -EINVAL);
+
+        client->cb = cb;
+        client->userdata = userdata;
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
+{
+        assert_return(client, -EINVAL);
+        assert_return(interface_index >= -1, -EINVAL);
+
+        client->index = interface_index;
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
+                            const struct ether_addr *mac_addr)
+{
+        assert_return(client, -EINVAL);
+
+        if (mac_addr)
+                memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
+        else
+                memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
+
+        return 0;
+}
+
+static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
+        if (client->cb) {
+                client = sd_dhcp6_client_ref(client);
+                client->cb(client, event, client->userdata);
+                client = sd_dhcp6_client_unref(client);
+        }
+
+        return client;
+}
+
+static int client_initialize(sd_dhcp6_client *client)
+{
+        assert_return(client, -EINVAL);
+
+        client->state = DHCP6_STATE_STOPPED;
+
+        return 0;
+}
+
+static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
+        assert_return(client, NULL);
+
+        client = client_notify(client, error);
+        if (client)
+                client_initialize(client);
+
+        return client;
+}
+
+int sd_dhcp6_client_stop(sd_dhcp6_client *client)
+{
+        client_stop(client, DHCP6_EVENT_STOP);
+
+        return 0;
+}
+
+int sd_dhcp6_client_start(sd_dhcp6_client *client)
+{
+        int r = 0;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->event, -EINVAL);
+        assert_return(client->index > 0, -EINVAL);
+
+        return r;
+}
+
+int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
+                                 int priority)
+{
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!client->event, -EBUSY);
+
+        if (event)
+                client->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&client->event);
+                if (r < 0)
+                        return 0;
+        }
+
+        client->event_priority = priority;
+
+        return 0;
+}
+
+int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
+        assert_return(client, -EINVAL);
+
+        client->event = sd_event_unref(client->event);
+
+        return 0;
+}
+
+sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
+        if (!client)
+                return NULL;
+
+        return client->event;
+}
+
+sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
+        if (client)
+                assert_se(REFCNT_INC(client->n_ref) >= 2);
+
+        return client;
+}
+
+sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
+        if (client && REFCNT_DEC(client->n_ref) <= 0) {
+
+                client_initialize(client);
+
+                sd_dhcp6_client_detach_event(client);
+
+                free(client);
+
+                return NULL;
+        }
+
+        return client;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
+#define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
+
+int sd_dhcp6_client_new(sd_dhcp6_client **ret)
+{
+        _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        client = new0(sd_dhcp6_client, 1);
+        if (!client)
+                return -ENOMEM;
+
+        client->n_ref = REFCNT_INIT;
+
+        client->index = -1;
+
+        *ret = client;
+        client = NULL;
+
+        return 0;
+}
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
new file mode 100644
index 0000000..4965011
--- /dev/null
+++ b/src/systemd/sd-dhcp6-client.h
@@ -0,0 +1,54 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddhcp6clienthfoo
+#define foosddhcp6clienthfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+enum {
+        DHCP6_EVENT_STOP                        = 0,
+};
+
+typedef struct sd_dhcp6_client sd_dhcp6_client;
+
+typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event,
+                                     void *userdata);
+int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
+                                 sd_dhcp6_client_cb_t cb, void *userdata);
+
+int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
+int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
+                            const struct ether_addr *mac_addr);
+
+int sd_dhcp6_client_stop(sd_dhcp6_client *client);
+int sd_dhcp6_client_start(sd_dhcp6_client *client);
+int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
+                                 int priority);
+int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);
+sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client);
+sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client);
+sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client);
+int sd_dhcp6_client_new(sd_dhcp6_client **ret);
+
+#endif



More information about the systemd-commits mailing list