]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: Assign prefixes received via DHCPv6
authorPatrik Flykt <patrik.flykt@linux.intel.com>
Thu, 4 Jan 2018 13:11:59 +0000 (15:11 +0200)
committerPatrik Flykt <patrik.flykt@linux.intel.com>
Thu, 4 Jan 2018 13:22:44 +0000 (15:22 +0200)
When receiving one or more prefixes with variable length, assign a
64 bit long prefix for each link that has been configured for DHCPv6
prefix delegation and is not using DHCPv6 to fetch IPv6 adresses.

Keep assigning prefixes with length 64 from each prefix received via
DHCPv6 as long as there are prefixes left. If the number of prefixes
available from a prefix received via DHCPv6 is smaller than the
number of links, continue with the next delegated prefix, if any.

Remember the prefixes used for each link by storing them in a hash
and checking the hash each time a prefix is to be delegated. If an
error occurs when assigning a prefix to a link, try assigning the
prefix to another link. If the error occurs while updating the
prefix, log the situation and continue delegating the rest of the
prefixes.

src/network/networkd-dhcp6.c

index 59dcf28764b2e142a40e59fd23b37f1ad6fe773e..4bb42919dfcefd20239662dd1055164a4733e530 100644 (file)
 
 #include <netinet/ether.h>
 #include <linux/if.h>
+#include "sd-radv.h"
 
 #include "sd-dhcp6-client.h"
 
+#include "hashmap.h"
 #include "hostname-util.h"
 #include "network-internal.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
+#include "siphash24.h"
+#include "string-util.h"
+#include "radv-internal.h"
 
 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
 
@@ -74,6 +79,166 @@ static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
         return 0;
 }
 
+static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
+                                  uint8_t prefix_len,
+                                  uint32_t lifetime_preferred,
+                                  uint32_t lifetime_valid) {
+        sd_radv *radv = link->radv;
+        int r;
+        _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
+
+        r = sd_radv_prefix_new(&p);
+        if (r < 0)
+                return r;
+
+        r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
+        if (r < 0)
+                return r;
+
+        r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
+        if (r < 0)
+                return r;
+
+        r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
+        if (r < 0)
+                return r;
+
+        r = sd_radv_stop(radv);
+        if (r < 0)
+                return r;
+
+        r = sd_radv_add_prefix(radv, p, true);
+        if (r < 0 && r != -EEXIST)
+                return r;
+
+        r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
+        if (r < 0)
+                return r;
+
+        return sd_radv_start(radv);
+}
+
+static Network *dhcp6_reset_pd_prefix_network(Link *link) {
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->networks);
+
+        return link->manager->networks;
+}
+
+static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
+                                      struct in6_addr *pd_prefix,
+                                      uint8_t pd_prefix_len,
+                                      uint32_t lifetime_preferred,
+                                      uint32_t lifetime_valid) {
+        Link *link;
+        Manager *manager = dhcp6_link->manager;
+        union in_addr_union prefix;
+        uint8_t n_prefixes, n_used = 0;
+        _cleanup_free_ char *buf = NULL;
+        int r;
+
+        assert(manager);
+        assert(pd_prefix_len <= 64);
+
+        prefix.in6 = *pd_prefix;
+
+        r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
+        if (r < 0)
+                return r;
+
+        n_prefixes = 1 << (64 - pd_prefix_len);
+
+        (void) in_addr_to_string(AF_INET6, &prefix, &buf);
+        log_link_debug(dhcp6_link, "Assigning up to %u prefixes from %s/%u",
+                       n_prefixes, strnull(buf), pd_prefix_len);
+
+        while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
+                Link *assigned_link;
+
+                if (n_used == n_prefixes) {
+                        log_link_debug(dhcp6_link, "Assigned %u/%u prefixes from %s/%u",
+                                       n_used, n_prefixes, strnull(buf), pd_prefix_len);
+
+                        return -EAGAIN;
+                }
+
+                if (link == dhcp6_link)
+                        continue;
+
+                if (!dhcp6_verify_link(link))
+                        continue;
+
+                assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
+                if (assigned_link != NULL && assigned_link != link)
+                        continue;
+
+                r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
+                                           lifetime_preferred, lifetime_valid);
+                if (r < 0) {
+                        log_link_error_errno(link, r, "Unable to %s prefix %s/%u for link: %m",
+                                             assigned_link ? "update": "assign",
+                                             strnull(buf), pd_prefix_len);
+
+                        if (assigned_link == NULL)
+                                continue;
+
+                } else
+                        log_link_debug(link, "Assigned prefix %u/%u %s/64 to link",
+                                       n_used + 1, n_prefixes, strnull(buf));
+
+                n_used++;
+
+                r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
+                if (r < 0 && n_used < n_prefixes)
+                        return r;
+        }
+
+        return n_used;
+}
+
+static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
+        int r;
+        sd_dhcp6_lease *lease;
+        struct in6_addr pd_prefix;
+        uint8_t pd_prefix_len;
+        uint32_t lifetime_preferred, lifetime_valid;
+        _cleanup_free_ char *buf = NULL;
+        Iterator i = ITERATOR_FIRST;
+
+        r = sd_dhcp6_client_get_lease(client, &lease);
+        if (r < 0)
+                return r;
+
+        (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
+
+        dhcp6_reset_pd_prefix_network(link);
+        sd_dhcp6_lease_reset_pd_prefix_iter(lease);
+
+        while (sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len,
+                                     &lifetime_preferred,
+                                     &lifetime_valid) >= 0) {
+
+                if (pd_prefix_len > 64) {
+                        log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
+                                       strnull(buf), pd_prefix_len);
+                        continue;
+                }
+
+                r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix,
+                                               pd_prefix_len,
+                                               lifetime_preferred,
+                                               lifetime_valid);
+                if (r < 0 && r != -EAGAIN)
+                        return r;
+
+                if (r >= 0)
+                        i = ITERATOR_FIRST;
+        }
+
+        return 0;
+}
+
 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
                                  void *userdata) {
         _cleanup_link_unref_ Link *link = userdata;
@@ -178,6 +343,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
                 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
                         log_link_warning(link, "DHCPv6 lease lost");
 
+                (void) manager_dhcp6_prefix_remove_all(link->manager, link);
+
                 link->dhcp6_configured = false;
                 break;
 
@@ -188,6 +355,10 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
                         return;
                 }
 
+                r = dhcp6_lease_pd_prefix_acquired(client, link);
+                if (r < 0)
+                        log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
+
                 _fallthrough_;
         case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
                 r = dhcp6_lease_information_acquired(client, link);