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

Tom Gundersen tomegun at kemper.freedesktop.org
Wed Dec 10 09:35:32 PST 2014


 Makefile.am                                |    1 
 src/libsystemd-network/dhcp6-protocol.h    |    3 
 src/libsystemd-network/sd-dhcp6-client.c   |  150 +++++++++++++++-----
 src/libsystemd-network/sd-dhcp6-lease.c    |    4 
 src/libsystemd-network/test-dhcp6-client.c |  126 ++++++++++++++---
 src/network/networkd-dhcp6.c               |  210 +++++++++++++++++++++++++++++
 src/network/networkd-link.c                |  122 ----------------
 src/network/networkd-link.h                |    1 
 src/systemd/sd-dhcp6-client.h              |    5 
 9 files changed, 445 insertions(+), 177 deletions(-)

New commits:
commit 85bd849f09aceb7f972a0697494ea22b2247a5d7
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Dec 10 16:17:35 2014 +0200

    networkd-dhcp6: Support ICMPv6 Other information
    
    When ICMPv6 Other information is received, enable Information request
    in DHCPv6. If the DHCPv6 client already exists, only update the client
    if there is a transition from Other to Managed state.

diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index bf8d78b..c31bd4e 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -43,6 +43,7 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
         case DHCP6_EVENT_RESEND_EXPIRE:
         case DHCP6_EVENT_RETRANS_MAX:
         case DHCP6_EVENT_IP_ACQUIRE:
+        case DHCP6_EVENT_INFORMATION_REQUEST:
                 log_link_debug(link, "DHCPv6 event %d", event);
 
                 break;
@@ -58,13 +59,47 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
         }
 }
 
-static int dhcp6_configure(Link *link) {
+static int dhcp6_configure(Link *link, int event) {
         int r;
+        bool information_request;
 
         assert_return(link, -EINVAL);
 
-        if (link->dhcp6_client)
-                return 0;
+        if (link->dhcp6_client) {
+                if (event != ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED)
+                        return 0;
+
+                r = sd_dhcp6_client_get_information_request(link->dhcp6_client,
+                                                        &information_request);
+                if (r < 0) {
+                        log_link_warning(link, "Could not get DHCPv6 Information request setting");
+                        link->dhcp6_client =
+                                sd_dhcp6_client_unref(link->dhcp6_client);
+                        return r;
+                }
+
+                if (!information_request)
+                        return r;
+
+                r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
+                                                        false);
+                if (r < 0) {
+                        log_link_warning(link, "Could not unset DHCPv6 Information request");
+                        link->dhcp6_client =
+                                sd_dhcp6_client_unref(link->dhcp6_client);
+                        return r;
+                }
+
+                r = sd_dhcp6_client_start(link->dhcp6_client);
+                if (r < 0) {
+                        log_link_warning(link, "Could not restart DHCPv6 after enabling Information request");
+                        link->dhcp6_client =
+                                sd_dhcp6_client_unref(link->dhcp6_client);
+                        return r;
+                }
+
+                return r;
+        }
 
         r = sd_dhcp6_client_new(&link->dhcp6_client);
         if (r < 0)
@@ -97,6 +132,16 @@ static int dhcp6_configure(Link *link) {
                 return r;
         }
 
+        if (event == ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER) {
+                r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
+                                                        true);
+                if (r < 0) {
+                        link->dhcp6_client =
+                                sd_dhcp6_client_unref(link->dhcp6_client);
+                        return r;
+                }
+        }
+
         r = sd_dhcp6_client_start(link->dhcp6_client);
         if (r < 0)
                 link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
@@ -116,10 +161,10 @@ static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
 
         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_OTHER:
         case ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED:
                 break;
 
@@ -134,7 +179,7 @@ static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
                 return;
         }
 
-        dhcp6_configure(link);
+        dhcp6_configure(link, event);
 }
 
 int icmp6_configure(Link *link) {

commit 5c79bd79839f1e50bd3c34a0670037f7965ca5a4
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Dec 10 16:17:34 2014 +0200

    networkd-dhcp6: Move ICMPv6 and DHCPv6 configuration to new file
    
    Handle all aspects of ICMPv6 and DHCPv6 in a file of its own as is done
    with DHCPv4 and IPv4LL.

diff --git a/Makefile.am b/Makefile.am
index 23210ff..6f02c74 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5247,6 +5247,7 @@ libsystemd_networkd_core_la_SOURCES = \
 	src/network/networkd-link.c \
 	src/network/networkd-ipv4ll.c \
 	src/network/networkd-dhcp4.c \
+	src/network/networkd-dhcp6.c \
 	src/network/networkd-network.c \
 	src/network/networkd-address.c \
 	src/network/networkd-route.c \
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
new file mode 100644
index 0000000..bf8d78b
--- /dev/null
+++ b/src/network/networkd-dhcp6.c
@@ -0,0 +1,165 @@
+/*-*- 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/ether.h>
+#include <linux/if.h>
+
+#include "networkd-link.h"
+#include "network-internal.h"
+
+#include "sd-icmp6-nd.h"
+#include "sd-dhcp6-client.h"
+
+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_link_debug(link, "DHCPv6 event %d", event);
+
+                break;
+
+        default:
+                if (event < 0)
+                        log_link_warning(link, "DHCPv6 error: %s",
+                                         strerror(-event));
+                else
+                        log_link_warning(link, "DHCPv6 unknown event: %d",
+                                         event);
+                return;
+        }
+}
+
+static int dhcp6_configure(Link *link) {
+        int r;
+
+        assert_return(link, -EINVAL);
+
+        if (link->dhcp6_client)
+                return 0;
+
+        r = sd_dhcp6_client_new(&link->dhcp6_client);
+        if (r < 0)
+                return r;
+
+        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;
+        }
+
+        r = sd_dhcp6_client_set_mac(link->dhcp6_client,
+                                    (const uint8_t *) &link->mac,
+                                    sizeof (link->mac), ARPHRD_ETHER);
+        if (r < 0) {
+                link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
+                return r;
+        }
+
+        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;
+        }
+
+        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;
+        }
+
+        r = sd_dhcp6_client_start(link->dhcp6_client);
+        if (r < 0)
+                link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
+
+        return r;
+}
+
+static void icmp6_router_handler(sd_icmp6_nd *nd, 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 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_link_warning(link, "ICMPv6 error: %s",
+                                         strerror(-event));
+                else
+                        log_link_warning(link, "ICMPv6 unknown event: %d",
+                                         event);
+
+                return;
+        }
+
+        dhcp6_configure(link);
+}
+
+int icmp6_configure(Link *link) {
+        int r;
+
+        assert_return(link, -EINVAL);
+
+        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);
+
+        return r;
+}
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 06e093a..08f724e 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -857,106 +857,6 @@ static int link_set_bridge(Link *link) {
         return r;
 }
 
-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_link_debug(link, "DHCPv6 event %d", event);
-
-                break;
-
-        default:
-                if (event < 0)
-                        log_link_warning(link, "DHCPv6 error: %s",
-                                         strerror(-event));
-                else
-                        log_link_warning(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_link_warning(link, "ICMPv6 error: %s",
-                                         strerror(-event));
-                else
-                        log_link_warning(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,
-                                    (const uint8_t *) &link->mac,
-                                    sizeof (link->mac), ARPHRD_ETHER);
-        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;
 
@@ -1270,27 +1170,7 @@ static int link_configure(Link *link) {
         }
 
         if (link_dhcp6_enabled(link)) {
-                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);
+                r = icmp6_configure(link);
                 if (r < 0)
                         return r;
         }
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 31688c3..05c34ee 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -119,6 +119,7 @@ int link_set_hostname(Link *link, const char *hostname);
 
 int ipv4ll_configure(Link *link);
 int dhcp4_configure(Link *link);
+int icmp6_configure(Link *link);
 
 const char* link_state_to_string(LinkState s) _const_;
 LinkState link_state_from_string(const char *s) _pure_;

commit c4e8ceddccfbb14e475e74eb5c57ba32c3c6cf86
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Dec 10 16:17:33 2014 +0200

    test-dhcp6-client: Add a simple Information Request test case
    
    Start the DHCPv6 test case by sending an Information Request, verifying
    the reply and continuing at once with the normal address acquisition
    procedure. Reuse the DHCPv6 Solicit Reply so that the client code is
    verified to ignore any erroneously added IPv6 address information.

diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 940a606..017371e 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -1025,6 +1025,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
         assert_return(client->index > 0, -EINVAL);
         assert_return(client->state != state, -EINVAL);
 
+        log_dhcp6_client(client, "client state %d new state %d", client->state, state);
         client->timeout_resend_expire =
                 sd_event_source_unref(client->timeout_resend_expire);
         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 26b28a2..7590839 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -41,7 +41,7 @@ static struct ether_addr mac_addr = {
         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
 };
 
-static bool verbose = false;
+static bool verbose = true;
 
 static sd_event_source *hangcheck;
 static int test_dhcp_fd[2];
@@ -335,13 +335,19 @@ 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);
+static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
+                                   void *userdata) {
+        sd_event *e = userdata;
 
-        if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
-                return -errno;
+        assert_se(e);
+        assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
 
-        return test_dhcp_fd[0];
+        assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
+
+        if (verbose)
+                printf("  got DHCPv6 event %d\n", event);
+
+        sd_event_exit(e, 0);
 }
 
 static int test_client_send_reply(DHCP6Message *request) {
@@ -513,6 +519,83 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
         return 0;
 }
 
+static void test_client_information_cb(sd_dhcp6_client *client, int event,
+                                       void *userdata) {
+        sd_event *e = userdata;
+
+        assert_se(e);
+        assert_se(event == DHCP6_EVENT_INFORMATION_REQUEST);
+
+        if (verbose)
+                printf("  got DHCPv6 event %d\n", event);
+
+        assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0);
+        assert_se(sd_dhcp6_client_set_callback(client,
+                                               test_client_solicit_cb, e) >= 0);
+
+        assert_se(sd_dhcp6_client_start(client) >= 0);
+}
+
+static int test_client_verify_information_request(DHCP6Message *information_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_elapsed_time = false;
+        int r;
+        struct in6_addr addr;
+        uint32_t lt_pref, lt_valid;
+
+        assert_se(information_request->type == DHCP6_INFORMATION_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(optlen == sizeof(test_duid));
+                        memcpy(&test_duid, optval, sizeof(test_duid));
+
+                        break;
+
+                case DHCP6_OPTION_IA_NA:
+                        assert_not_reached("IA TA option must not be present");
+
+                        break;
+
+                case DHCP6_OPTION_SERVERID:
+                        assert_not_reached("Server ID option must not be present");
+
+                        break;
+
+                case DHCP6_OPTION_ELAPSED_TIME:
+                        assert_se(!found_elapsed_time);
+                        found_elapsed_time = true;
+
+                        assert_se(optlen == 2);
+
+                        break;
+                }
+        }
+
+        assert_se(r == -ENOMSG);
+        assert_se(found_clientid && found_elapsed_time);
+
+        assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, &lt_pref,
+                                                   &lt_valid) == -ENOMSG);
+
+        assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, &lt_pref,
+                                                  &lt_valid) == -ENOMSG);
+
+        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 =
@@ -534,10 +617,14 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
         assert_se(message->transaction_id & 0x00ffffff);
 
         if (test_client_message_num == 0) {
+                test_client_verify_information_request(message, option, len);
+                test_client_send_reply(message);
+                test_client_message_num++;
+        } else if (test_client_message_num == 1) {
                 test_client_verify_solicit(message, option, len);
                 test_client_send_advertise(message);
                 test_client_message_num++;
-        } else if (test_client_message_num == 1) {
+        } else if (test_client_message_num == 2) {
                 test_client_verify_request(message, option, len);
                 test_client_send_reply(message);
                 test_client_message_num++;
@@ -546,24 +633,19 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
         return len;
 }
 
-static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
-                                   void *userdata) {
-        sd_event *e = userdata;
-
-        assert_se(e);
-        assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
-
-        assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+        assert_se(index == test_index);
 
-        if (verbose)
-                printf("  got DHCPv6 event %d\n", event);
+        if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
+                return -errno;
 
-        sd_event_exit(e, 0);
+        return test_dhcp_fd[0];
 }
 
 static int test_client_solicit(sd_event *e) {
         sd_dhcp6_client *client;
         usec_t time_now = now(clock_boottime_or_monotonic());
+        bool val = true;
 
         if (verbose)
                 printf("* %s\n", __FUNCTION__);
@@ -578,8 +660,14 @@ static int test_client_solicit(sd_event *e) {
                                           sizeof (mac_addr),
                                           ARPHRD_ETHER) >= 0);
 
+        assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
+        assert_se(val == false);
+        assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0);
+        assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
+        assert_se(val == true);
+
         assert_se(sd_dhcp6_client_set_callback(client,
-                                               test_client_solicit_cb, e) >= 0);
+                                               test_client_information_cb, e) >= 0);
 
         assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(),
                                     time_now + 2 * USEC_PER_SEC, 0,

commit bbfa43ca37df0718287c25a8e39ee7477ebf33f6
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Dec 10 16:17:32 2014 +0200

    sd-dhcp6-client: Implement Information Request message
    
    Implement Information Request message according to RFC 3315, section
    18.1.5. with the excepion that the first message is not delayed by a
    random amount. Instead systemd-networkd is supposed to take care of
    desynchronizing between other clients.
    
    Initialize the DHCPv6 client structure in sd_dhcp6_client_start()
    as this allows toggling between information request and normal
    DHCPv6 address aquisition modes.

diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index eaa6717..3e0f339 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -51,6 +51,8 @@ enum {
         DHCP6_PORT_CLIENT                       = 546,
 };
 
+#define DHCP6_INF_TIMEOUT                       1 * USEC_PER_SEC
+#define DHCP6_INF_MAX_RT                        120 * USEC_PER_SEC
 #define DHCP6_SOL_MAX_DELAY                     1 * USEC_PER_SEC
 #define DHCP6_SOL_TIMEOUT                       1 * USEC_PER_SEC
 #define DHCP6_SOL_MAX_RT                        120 * USEC_PER_SEC
@@ -71,6 +73,7 @@ enum {
 
 enum DHCP6State {
         DHCP6_STATE_STOPPED                     = 0,
+        DHCP6_STATE_INFORMATION_REQUEST         = 1,
         DHCP6_STATE_SOLICITATION                = 2,
         DHCP6_STATE_REQUEST                     = 3,
         DHCP6_STATE_BOUND                       = 4,
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index cc5b744..940a606 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -62,6 +62,7 @@ struct sd_dhcp6_client {
         usec_t transaction_start;
         struct sd_dhcp6_lease *lease;
         int fd;
+        bool information_request;
         be16_t *req_opts;
         size_t req_opts_allocated;
         size_t req_opts_len;
@@ -227,6 +228,25 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du
         return 0;
 }
 
+int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
+                                            bool enabled) {
+        assert_return(client, -EINVAL);
+
+        client->information_request = enabled;
+
+        return 0;
+}
+
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
+                                            bool *enabled) {
+        assert_return(client, -EINVAL);
+        assert_return(enabled, -EINVAL);
+
+        *enabled = client->information_request;
+
+        return 0;
+}
+
 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
                                        uint16_t option) {
         size_t t;
@@ -333,6 +353,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         message->transaction_id = client->transaction_id;
 
         switch(client->state) {
+        case DHCP6_STATE_INFORMATION_REQUEST:
+                message->type = DHCP6_INFORMATION_REQUEST;
+
+                break;
+
         case DHCP6_STATE_SOLICITATION:
                 message->type = DHCP6_SOLICIT;
 
@@ -494,6 +519,12 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
 
         switch (client->state) {
+        case DHCP6_STATE_INFORMATION_REQUEST:
+                init_retransmit_time = DHCP6_INF_TIMEOUT;
+                max_retransmit_time = DHCP6_INF_MAX_RT;
+
+                break;
+
         case DHCP6_STATE_SOLICITATION:
 
                 if (client->retransmit_count && client->lease) {
@@ -744,6 +775,12 @@ static int client_parse_message(sd_dhcp6_client *client,
                         break;
 
                 case DHCP6_OPTION_IA_NA:
+                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+                                log_dhcp6_client(client, "Information request ignoring IA NA option");
+
+                                break;
+                        }
+
                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
                                                   &lease->ia);
                         if (r < 0 && r != -ENOMSG)
@@ -779,10 +816,12 @@ static int client_parse_message(sd_dhcp6_client *client,
                 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));
+        if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
+                r = dhcp6_lease_get_serverid(lease, &id, &id_len);
+                if (r < 0)
+                        log_dhcp6_client(client, "%s has no server id",
+                                         dhcp6_message_type_to_string(message->type));
+        }
 
         return r;
 }
@@ -814,12 +853,15 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
                         return 0;
         }
 
-        if (client->lease)
+        if (client->lease) {
                 dhcp6_lease_clear_timers(&client->lease->ia);
+                client->lease = sd_dhcp6_lease_unref(client->lease);
+        }
 
-        client->lease = sd_dhcp6_lease_unref(client->lease);
-        client->lease = lease;
-        lease = NULL;
+        if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
+                client->lease = lease;
+                lease = NULL;
+        }
 
         return DHCP6_STATE_BOUND;
 }
@@ -846,7 +888,8 @@ static int client_receive_advertise(sd_dhcp6_client *client,
                 return r;
 
         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
-        if (!client->lease || r < 0 || pref_advertise > pref_lease) {
+
+        if (r < 0 || pref_advertise > pref_lease) {
                 sd_dhcp6_lease_unref(client->lease);
                 client->lease = lease;
                 lease = NULL;
@@ -913,6 +956,17 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
                 return 0;
 
         switch (client->state) {
+        case DHCP6_STATE_INFORMATION_REQUEST:
+                r = client_receive_reply(client, message, len);
+                if (r < 0)
+                        return 0;
+
+                client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
+
+                client_start(client, DHCP6_STATE_STOPPED);
+
+                break;
+
         case DHCP6_STATE_SOLICITATION:
                 r = client_receive_advertise(client, message, len);
 
@@ -988,37 +1042,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
 
         switch (state) {
         case DHCP6_STATE_STOPPED:
-        case DHCP6_STATE_SOLICITATION:
+                if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+                        client->state = DHCP6_STATE_STOPPED;
 
-                r = client_ensure_iaid(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;
-
-                r = sd_event_source_set_description(client->receive_message, "dhcp6-receive-message");
-                if (r < 0)
-                        return r;
+                        return 0;
+                }
 
+                /* fall through */
+        case DHCP6_STATE_SOLICITATION:
                 client->state = DHCP6_STATE_SOLICITATION;
 
                 break;
 
+        case DHCP6_STATE_INFORMATION_REQUEST:
         case DHCP6_STATE_REQUEST:
         case DHCP6_STATE_RENEW:
         case DHCP6_STATE_REBIND:
@@ -1123,6 +1159,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client)
 int sd_dhcp6_client_start(sd_dhcp6_client *client)
 {
         int r = 0;
+        enum DHCP6State state = DHCP6_STATE_SOLICITATION;
 
         assert_return(client, -EINVAL);
         assert_return(client->event, -EINVAL);
@@ -1132,7 +1169,44 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
         if (r < 0)
                 return r;
 
-        return client_start(client, DHCP6_STATE_SOLICITATION);
+        r = client_ensure_iaid(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)
+                goto error;
+
+        r = sd_event_source_set_priority(client->receive_message,
+                                         client->event_priority);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_description(client->receive_message,
+                                        "dhcp6-receive-message");
+        if (r < 0)
+                goto error;
+
+        if (client->information_request)
+                state = DHCP6_STATE_INFORMATION_REQUEST;
+
+        log_dhcp6_client(client, "Started in %s mode",
+                        client->information_request? "Information request":
+                        "Managed");
+
+        return client_start(client, state);
+
+error:
+        client_reset(client);
+        return r;
 }
 
 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index c7f168f..e9663c0 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -33,6 +33,7 @@ enum {
         DHCP6_EVENT_RESEND_EXPIRE               = 10,
         DHCP6_EVENT_RETRANS_MAX                 = 11,
         DHCP6_EVENT_IP_ACQUIRE                  = 12,
+        DHCP6_EVENT_INFORMATION_REQUEST         = 13,
 };
 
 typedef struct sd_dhcp6_client sd_dhcp6_client;
@@ -47,6 +48,10 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
                             size_t addr_len, uint16_t arp_type);
 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
                              size_t duid_len);
+int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
+                                            bool enabled);
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
+                                            bool *enabled);
 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
                                        uint16_t option);
 

commit fab15fec2413bbc15dc7c24724ec82e371c966ba
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Dec 10 16:17:31 2014 +0200

    sd-dhcp6-lease: Return only -EINVAL when a NULL lease is supplied
    
    Suppyling a NULL lease is not a condition that needs to be asserted,
    returning -EINVAL is informative enough to the caller. This simplifies
    calling code and doesn't falsely indicate that something erroneous was
    attempted.

diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index e2715ea..8960fac 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -110,9 +110,11 @@ int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
 }
 
 int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) {
-        assert_return(lease, -EINVAL);
         assert_return(preference, -EINVAL);
 
+        if (!lease)
+                return -EINVAL;
+
         *preference = lease->preference;
 
         return 0;

commit c47e8936a43ce546e8a74fa569e9fbfae6c64be7
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Dec 10 16:17:30 2014 +0200

    sd-dhcp6-client: Make end of successfull option parsing explicit
    
    When all DHCPv6 options have been parsed, dhcp6_option_parse() returns
    -ENOMSG. Explicitely set the return value to indicate success so that
    later code does not need to take this special value into account.

diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 8537d7c..cc5b744 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -770,7 +770,10 @@ static int client_parse_message(sd_dhcp6_client *client,
                 }
         }
 
-        if ((r < 0 && r != -ENOMSG) || !clientid) {
+        if (r == -ENOMSG)
+                r = 0;
+
+        if (r < 0 || !clientid) {
                 log_dhcp6_client(client, "%s has incomplete options",
                                  dhcp6_message_type_to_string(message->type));
                 return -EINVAL;



More information about the systemd-commits mailing list