[systemd-devel] [PATCH] network: Implement fallback DHCPv6 prefix handling for older kernels

Patrik Flykt patrik.flykt at linux.intel.com
Thu Apr 9 03:37:37 PDT 2015


When setting IPv6 addresses acquired by DHCPv6, systemd-networkd sets
the IFA_F_NOPREFIXROUTE flag in the IFA_FLAGS netlink attribute. As the
flag and the attribute are present starting with Linux 3.14, older
kernels will need systemd-network to manage prefix route handling.

Support for IFA_FLAGS is done during compile time and implemented as
part of netlink type checking. When IFA_FLAGS is detected in an systemd
external kernel header, HAVE_DECL_IFA_FLAGS is defined and the type
check will be successful. If not, setting the attribute will fail and
systemd-network will record this fact to its Link structure. Later if
a prefix expires, the same Link structure will be queried for the
successful use of IFA_FLAGS, with the DHCPv6 addresses handled either
by the kernel or the systemd-networkd fallback code.

The prefix expiration and IPv6 address updating fallback code is
resurrected from the parts deleted with commit
47d45d3cde45d6545367570264e4e3636bc9e345.

This patch can be removed once the minimum kernel requirements are
greater than or equal to 3.14.
---

	Hi,

This patch implements a compile time fix for IFA_FLAGS support. Doing the
fix run time has its difficulties as the kernel discarding DHCPv6 address
assignment in the error path of dhcp6_address_handler() can have been
caused by other error situations.

I got this one working on my machine by commenting IFA_FLAGS out/leaving
IFA_FLAGS defined in the kernel headers, please test against real running
3.8/3.14 Linux versions.


Cheers,

	Patrik



 src/libsystemd/sd-rtnl/rtnl-types.c |  2 ++
 src/network/networkd-address.c      |  5 +++-
 src/network/networkd-dhcp6.c        | 55 +++++++++++++++++++++++++++++++++++--
 src/network/networkd-link.c         |  4 +++
 src/network/networkd-link.h         |  2 ++
 5 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/src/libsystemd/sd-rtnl/rtnl-types.c b/src/libsystemd/sd-rtnl/rtnl-types.c
index 49784bf..407b749 100644
--- a/src/libsystemd/sd-rtnl/rtnl-types.c
+++ b/src/libsystemd/sd-rtnl/rtnl-types.c
@@ -360,7 +360,9 @@ static const NLType rtnl_address_types[CONST_MAX(IFA_MAX, IFA_FLAGS) + 1] = {
         [IFA_ANYCAST],
         [IFA_MULTICAST],
 */
+#if HAVE_DECL_IFA_FLAGS
         [IFA_FLAGS]             = { .type = NLA_U32 },
+#endif
 };
 
 static const NLTypeSystem rtnl_address_type_system = {
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 85acc49..2057d05 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -218,7 +218,10 @@ int address_update(Address *address, Link *link,
         if (address->flags & ~0xff) {
                 r = sd_rtnl_message_append_u32(req, IFA_FLAGS, address->flags);
                 if (r < 0)
-                        return log_error_errno(r, "Could not set extended flags: %m");
+                        log_link_warning(link, "Could not set extended flags: %s",
+                                         strerror(-r));
+                else
+                        link->rtnl_extended_flags = true;
         }
 
         r = sd_rtnl_message_addr_set_scope(req, address->scope);
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index e863f4b..6b040bd 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -67,7 +67,7 @@ static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
         memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
 
         addr->flags = IFA_F_NOPREFIXROUTE;
-        addr->prefixlen = 64;
+        addr->prefixlen = prefixlen;
 
         addr->cinfo.ifa_prefered = lifetime_preferred;
         addr->cinfo.ifa_valid = lifetime_valid;
@@ -262,6 +262,52 @@ static int dhcp6_configure(Link *link, int event) {
         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;
+
+        log_link_struct(link, LOG_INFO,
+                        "MESSAGE=%-*s: IPv6 prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d expired",
+                        IFNAMSIZ, link->ifname,
+                        SD_ICMP6_ADDRESS_FORMAT_VAL(*expired_prefix),
+                        expired_prefixlen, NULL);
+
+        sd_dhcp6_lease_reset_address_iter(lease);
+
+        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)
+                        continue;
+
+                log_link_struct(link, LOG_INFO,
+                                "MESSAGE=%-*s: IPv6 prefix length updated "SD_ICMP6_ADDRESS_FORMAT_STR"/%d",
+                                IFNAMSIZ, link->ifname,
+                                SD_ICMP6_ADDRESS_FORMAT_VAL(ip6_addr), 128,
+                                NULL);
+
+                dhcp6_address_update(link, &ip6_addr, 128, lifetime_preferred,
+                                     lifetime_valid);
+        }
+
+        return 0;
+}
+
 static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
         Link *link = userdata;
 
@@ -274,7 +320,6 @@ 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_PREFIX_EXPIRED:
                 return;
 
         case ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
@@ -284,6 +329,12 @@ static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
 
                 break;
 
+        case ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED:
+                if (!link_rtnl_extended_flags(link))
+                        dhcp6_prefix_expired(link);
+
+                break;
+
         default:
                 if (event < 0)
                         log_link_warning(link, "ICMPv6 error: %s",
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 3bd37d8..a7df6c7 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -32,6 +32,10 @@
 #include "networkd-link.h"
 #include "networkd-netdev.h"
 
+bool link_rtnl_extended_flags(Link *link) {
+        return link->rtnl_extended_flags;
+}
+
 bool link_dhcp6_enabled(Link *link) {
         if (link->flags & IFF_LOOPBACK)
                 return false;
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 479098c..5528049 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -82,6 +82,7 @@ struct Link {
 
         sd_icmp6_nd *icmp6_router_discovery;
         sd_dhcp6_client *dhcp6_client;
+        bool rtnl_extended_flags;
 
         sd_lldp *lldp;
         char *lldp_file;
@@ -125,6 +126,7 @@ bool link_ipv6ll_enabled(Link *link);
 bool link_dhcp4_server_enabled(Link *link);
 bool link_dhcp4_enabled(Link *link);
 bool link_dhcp6_enabled(Link *link);
+bool link_rtnl_extended_flags(Link *link);
 
 const char* link_state_to_string(LinkState s) _const_;
 LinkState link_state_from_string(const char *s) _pure_;
-- 
2.1.4



More information about the systemd-devel mailing list