]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: Implement fallback DHCPv6 prefix handling for older kernels
authorPatrik Flykt <patrik.flykt@linux.intel.com>
Fri, 10 Apr 2015 11:03:18 +0000 (14:03 +0300)
committerPatrik Flykt <patrik.flykt@linux.intel.com>
Mon, 4 May 2015 06:42:37 +0000 (09:42 +0300)
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 expiry.

By default, DHCPv6 addresses are first assigned setting the
IFA_F_NOPREFIXROUTE flag in the IFA_FLAGS netlink attribute. Should
the address assignment fail, the same assignment is tried without
the IFA_FLAGS attribute. Should also the second attempt fail, an error
is printed and address assignment ends with failure. As successful use
of the IFA_FLAGS netlink attribute is recorded in the Link structure,
the DHCPv6 code will know if the kernel or systemd-network fallback
code handles expiring prefixes.

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.

src/network/networkd-address.c
src/network/networkd-dhcp6.c
src/network/networkd-link.c
src/network/networkd-link.h

index a3aa1f7fdb7ef52a49c190470d8a7236477e0727..069ba3eccb1b2c744673211902d9a89e95bb3be0 100644 (file)
@@ -215,7 +215,7 @@ int address_update(Address *address, Link *link,
         if (r < 0)
                 return log_error_errno(r, "Could not set flags: %m");
 
-        if (address->flags & ~0xff) {
+        if (address->flags & ~0xff && link->rtnl_extended_attrs) {
                 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");
index e863f4b347625af57eab49bd3f3476c1de94307c..5668fdf16c9a8f2286d40c028a16ae3881edaf65 100644 (file)
@@ -28,6 +28,8 @@
 #include "sd-icmp6-nd.h"
 #include "sd-dhcp6-client.h"
 
+static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
+
 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
                                         Link *link) {
         return 0;
@@ -42,6 +44,15 @@ static int dhcp6_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
 
         r = sd_rtnl_message_get_errno(m);
         if (r < 0 && r != -EEXIST) {
+                if (link->rtnl_extended_attrs) {
+                        log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
+
+                        link->rtnl_extended_attrs = false;
+                        dhcp6_lease_address_acquired(link->dhcp6_client, link);
+
+                        return 1;
+                }
+
                 log_link_error(link, "Could not set DHCPv6 address: %s",
                                strerror(-r));
 
@@ -67,7 +78,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 +273,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 +331,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 +340,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_attrs)
+                        dhcp6_prefix_expired(link);
+
+                break;
+
         default:
                 if (event < 0)
                         log_link_warning(link, "ICMPv6 error: %s",
index 0f9a1cd6d17f9ab78cf0c9d9556dfb058198baf9..0c6bb658e69f265edb95e08c7e23df3069cf2ac9 100644 (file)
@@ -225,6 +225,7 @@ static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) {
         link->n_ref = 1;
         link->manager = manager;
         link->state = LINK_STATE_PENDING;
+        link->rtnl_extended_attrs = true;
         link->ifindex = ifindex;
         link->ifname = strdup(ifname);
         if (!link->ifname)
index 479098cb2b183636425a58d7b421d0fe4f61e8aa..c3bc1b907b8c465c52caafaf9c346d1ee6ccc3e0 100644 (file)
@@ -82,6 +82,7 @@ struct Link {
 
         sd_icmp6_nd *icmp6_router_discovery;
         sd_dhcp6_client *dhcp6_client;
+        bool rtnl_extended_attrs;
 
         sd_lldp *lldp;
         char *lldp_file;