[systemd-devel] [PATCH 10/11] networkd-dhcp6: Assign DHCPv6 addresses and prefix lengths

Patrik Flykt patrik.flykt at linux.intel.com
Tue Jan 13 04:02:20 PST 2015


Once IPv6 addresses have been acquired, assign these to the interface
with the prefix lengths taken from the ICMPv6 Router Advertisement
handling code. The preferred and valid IPv6 address lifetimes are
handed to the kernel which will clean up them if not renewed in time.

When a prefix announced via Router Advertisements expires, find all
addresses that match that prefix and update the address to have a
prefix lenght of 128 causing the prefix to be off-link.
---
 src/network/networkd-dhcp6.c | 179 +++++++++++++++++++++++++++++++++++++++++--
 src/network/networkd-link.h  |  10 +++
 src/network/networkd.c       |   1 +
 3 files changed, 184 insertions(+), 6 deletions(-)

diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index c31bd4e..5f78f93 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -28,7 +28,151 @@
 #include "sd-icmp6-nd.h"
 #include "sd-dhcp6-client.h"
 
+static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
+                                        Link *link) {
+        int r;
+        uint32_t mtu;
+
+        r = sd_icmp6_ra_get_mtu(link->icmp6_router_discovery, &mtu);
+        if (r >= 0) {
+                r = link_set_mtu(link, mtu);
+                if (r < 0)
+                        log_link_error(link, "Failed to set IPv6 MTU to %" PRIu16, mtu);
+        }
+
+        return 0;
+}
+
+static int dhcp6_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
+                                 void *userdata) {
+        _cleanup_link_unref_ Link *link = userdata;
+        int r;
+
+        assert(link);
+
+        r = sd_rtnl_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_error(link, "Could not set DHCPv6 address: %s",
+                               strerror(-r));
+
+                link_enter_failed(link);
+
+        } else if (r >= 0)
+                link_rtnl_process_address(rtnl, m, link->manager);
+
+        return 1;
+}
+
+static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
+                                uint8_t prefixlen, uint32_t lifetime_preferred,
+                                uint32_t lifetime_valid) {
+        int r;
+        _cleanup_address_free_ Address *addr = NULL;
+
+        r = address_new_dynamic(&addr);
+        if (r < 0)
+                return r;
+
+        addr->family = AF_INET6;
+        memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
+        addr->prefixlen = prefixlen;
+
+        addr->cinfo.ifa_prefered = lifetime_preferred;
+        addr->cinfo.ifa_valid = lifetime_valid;
+
+        log_link_struct(link, LOG_INFO, "MESSAGE=%-*s: DHCPv6 address %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d timeout preferred %d valid %d",
+                        IFNAMSIZ,
+                        link->ifname, ADDRESS6_FMT_VAL(addr->in_addr.in6),
+                        addr->prefixlen, lifetime_preferred, lifetime_valid,
+                        NULL);
+
+        r = address_update(addr, link, dhcp6_address_handler);
+        if (r < 0)
+                log_link_warning(link, "Could not assign DHCPv6 address: %s",
+                                strerror(-r));
+
+        return r;
+}
+
+static int dhcp6_prefix_expired(Link *link) {
+        int r;
+        sd_dhcp6_lease *lease;
+        struct in6_addr *expired_prefix, ip6_addr;
+        uint8_t expired_prefixlen;
+        uint32_t lifetime_preferred, lifetime_valid;
+
+        r = sd_icmp6_ra_get_expired_prefix(link->icmp6_router_discovery,
+                                        &expired_prefix, &expired_prefixlen);
+        if (r < 0)
+                return r;
+
+        r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease);
+        if (r < 0)
+                return r;
+
+        r = sd_dhcp6_lease_reset_address_iter(lease);
+        if (r < 0 && r != -ENOMSG)
+                return r;
+
+        while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
+                                                &lifetime_preferred,
+                                                &lifetime_valid) >= 0) {
+
+                r = sd_icmp6_prefix_match(expired_prefix, expired_prefixlen,
+                                        &ip6_addr);
+                if (r >= 0) {
+                        r = dhcp6_address_update(link, &ip6_addr, 128,
+                                                lifetime_preferred,
+                                                lifetime_valid);
+
+                        return r;
+                }
+        }
+
+        return 0;
+}
+
+static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
+        int r;
+        sd_dhcp6_lease *lease;
+        struct in6_addr ip6_addr;
+        uint32_t lifetime_preferred, lifetime_valid;
+        uint8_t prefixlen;
+
+        r = sd_dhcp6_client_get_lease(client, &lease);
+        if (r < 0)
+                return r;
+
+        r = sd_dhcp6_lease_reset_address_iter(lease);
+        if (r < 0 && r != -ENOMSG)
+                return r;
+
+        while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
+                                                &lifetime_preferred,
+                                                &lifetime_valid) >= 0) {
+
+                r = sd_icmp6_ra_get_prefixlen(link->icmp6_router_discovery,
+                                        &ip6_addr, &prefixlen);
+                if (r < 0 && r != -EADDRNOTAVAIL) {
+                        log_link_warning(link, "Could not get prefix information: %s",
+                                        strerror(-r));
+                        return r;
+                }
+
+                if (r == -EADDRNOTAVAIL)
+                        prefixlen = 128;
+
+                r = dhcp6_address_update(link, &ip6_addr, prefixlen,
+                                        lifetime_preferred, lifetime_valid);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
+        int r;
         Link *link = userdata;
 
         assert(link);
@@ -42,9 +186,23 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
         case DHCP6_EVENT_STOP:
         case DHCP6_EVENT_RESEND_EXPIRE:
         case DHCP6_EVENT_RETRANS_MAX:
+                log_link_debug(link, "DHCPv6 event %d", event);
+                break;
+
         case DHCP6_EVENT_IP_ACQUIRE:
+                r = dhcp6_lease_address_acquired(client, link);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return;
+                }
+
+                /* fall through */
         case DHCP6_EVENT_INFORMATION_REQUEST:
-                log_link_debug(link, "DHCPv6 event %d", event);
+                r = dhcp6_lease_information_acquired(client, link);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return;
+                }
 
                 break;
 
@@ -72,7 +230,8 @@ static int dhcp6_configure(Link *link, int event) {
                 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");
+                        log_link_warning(link, "Could not get DHCPv6 Information request setting: %s",
+                                        strerror(-r));
                         link->dhcp6_client =
                                 sd_dhcp6_client_unref(link->dhcp6_client);
                         return r;
@@ -84,7 +243,8 @@ static int dhcp6_configure(Link *link, int event) {
                 r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
                                                         false);
                 if (r < 0) {
-                        log_link_warning(link, "Could not unset DHCPv6 Information request");
+                        log_link_warning(link, "Could not unset DHCPv6 Information request: %s",
+                                        strerror(-r));
                         link->dhcp6_client =
                                 sd_dhcp6_client_unref(link->dhcp6_client);
                         return r;
@@ -92,7 +252,8 @@ static int dhcp6_configure(Link *link, int event) {
 
                 r = sd_dhcp6_client_start(link->dhcp6_client);
                 if (r < 0) {
-                        log_link_warning(link, "Could not restart DHCPv6 after enabling Information request");
+                        log_link_warning(link, "Could not restart DHCPv6 after enabling Information request: %s",
+                                        strerror(-r));
                         link->dhcp6_client =
                                 sd_dhcp6_client_unref(link->dhcp6_client);
                         return r;
@@ -166,6 +327,13 @@ static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
         case ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
         case ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER:
         case ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED:
+                dhcp6_configure(link, event);
+
+                break;
+
+        case ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED:
+                dhcp6_prefix_expired(link);
+
                 break;
 
         default:
@@ -176,10 +344,9 @@ static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
                         log_link_warning(link, "ICMPv6 unknown event: %d",
                                          event);
 
-                return;
+                break;
         }
 
-        dhcp6_configure(link, event);
 }
 
 int icmp6_configure(Link *link) {
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 0e2f558..be5d0be 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -157,3 +157,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
         ((address).s_addr >> 8) & 0xFF,     \
         ((address).s_addr >> 16) & 0xFF,    \
         (address).s_addr >> 24
+
+#define ADDRESS6_FMT_VAL(address)               \
+        be16toh((address).s6_addr16[0]),        \
+        be16toh((address).s6_addr16[1]),        \
+        be16toh((address).s6_addr16[2]),        \
+        be16toh((address).s6_addr16[3]),        \
+        be16toh((address).s6_addr16[4]),        \
+        be16toh((address).s6_addr16[5]),        \
+        be16toh((address).s6_addr16[6]),        \
+        be16toh((address).s6_addr16[7])
diff --git a/src/network/networkd.c b/src/network/networkd.c
index ced319d..ce96286 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -33,6 +33,7 @@ int main(int argc, char *argv[]) {
         int r;
 
         log_set_target(LOG_TARGET_AUTO);
+        log_set_max_level(LOG_DEBUG);
         log_parse_environment();
         log_open();
 
-- 
2.1.4



More information about the systemd-devel mailing list