]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: dhcp6pd: introduce a simplified and unified method to calculate subnet prefix
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 20 Oct 2021 20:16:52 +0000 (05:16 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 25 Oct 2021 15:35:22 +0000 (00:35 +0900)
src/network/networkd-dhcp6.c

index a92394554af1b6f41d6ddfafc316817d3ab12e7c..837368b829d8142f5323b15207bf25f620d0232e 100644 (file)
@@ -457,74 +457,83 @@ static bool link_has_preferred_subnet_id(Link *link) {
         return link->network->dhcp6_pd_subnet_id >= 0;
 }
 
-static int dhcp6_get_preferred_delegated_prefix(
+static int dhcp6_pd_calculate_prefix(
+                const struct in6_addr *pd_prefix,
+                uint8_t pd_prefix_len,
+                uint64_t subnet_id,
+                struct in6_addr *ret) {
+
+        struct in6_addr prefix;
+
+        assert(pd_prefix);
+        assert(pd_prefix_len <= 64);
+        assert(ret);
+
+        if (subnet_id >= UINT64_C(1) << (64 - pd_prefix_len))
+                return -ERANGE;
+
+        prefix = *pd_prefix;
+
+        if (pd_prefix_len < 32)
+                prefix.s6_addr32[0] |= htobe32(subnet_id >> 32);
+
+        prefix.s6_addr32[1] |= htobe32(subnet_id & 0xffffffff);
+
+        *ret = prefix;
+        return 0;
+}
+
+static int dhcp6_pd_get_preferred_prefix(
                 Link *link,
                 const struct in6_addr *pd_prefix,
                 uint8_t pd_prefix_len,
                 struct in6_addr *ret) {
 
-        /* We start off with the original PD prefix we have been assigned and iterate from there */
-        union in_addr_union prefix;
-        uint64_t n_prefixes;
+        struct in6_addr prefix;
         Link *assigned_link;
         int r;
 
         assert(link);
         assert(link->manager);
         assert(pd_prefix);
-        assert(pd_prefix_len <= 64);
-
-        n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
-        prefix.in6 = *pd_prefix;
 
         if (link_has_preferred_subnet_id(link)) {
-                uint64_t subnet_id = link->network->dhcp6_pd_subnet_id;
-
                 /* If the link has a preference for a particular subnet id try to allocate that */
-                if (subnet_id >= n_prefixes)
-                        return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE),
-                                                      "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
-                                                      subnet_id, n_prefixes);
 
-                r = in_addr_prefix_nth(AF_INET6, &prefix, 64, subnet_id);
+                r = dhcp6_pd_calculate_prefix(pd_prefix, pd_prefix_len, link->network->dhcp6_pd_subnet_id, &prefix);
                 if (r < 0)
                         return log_link_warning_errno(link, r,
                                                       "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
-                                                      subnet_id, n_prefixes);
-
-                /* Verify that the prefix we did calculate fits in the pd prefix.
-                 * This should not fail as we checked the prefix size beforehand */
-                assert_se(in6_addr_prefix_covers(pd_prefix, pd_prefix_len, &prefix.in6) > 0);
+                                                      link->network->dhcp6_pd_subnet_id, UINT64_C(1) << (64 - pd_prefix_len));
 
-                if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix.in6, &assigned_link) >= 0 &&
+                if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix, &assigned_link) >= 0 &&
                     assigned_link != link) {
                         _cleanup_free_ char *assigned_buf = NULL;
 
-                        (void) in6_addr_to_string(&prefix.in6, &assigned_buf);
+                        (void) in6_addr_to_string(&prefix, &assigned_buf);
                         return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN),
                                                       "The requested prefix %s is already assigned to another link.",
                                                       strna(assigned_buf));
                 }
 
-                *ret = prefix.in6;
+                *ret = prefix;
                 return 0;
         }
 
-        for (uint64_t n = 0; n < n_prefixes; n++) {
+        for (uint64_t n = 0; ; n++) {
+                r = dhcp6_pd_calculate_prefix(pd_prefix, pd_prefix_len, n, &prefix);
+                if (r < 0)
+                        return log_link_warning_errno(link, r,
+                                                      "Couldn't find a suitable prefix. Ran out of address space.");
+
                 /* If we do not have an allocation preference just iterate
                  * through the address space and return the first free prefix. */
-                if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix.in6, &assigned_link) < 0 ||
+                if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix, &assigned_link) < 0 ||
                     assigned_link == link) {
-                        *ret = prefix.in6;
+                        *ret = prefix;
                         return 0;
                 }
-
-                r = in_addr_prefix_next(AF_INET6, &prefix, 64);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Can't allocate another prefix. Out of address space?: %m");
         }
-
-        return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), "Couldn't find a suitable prefix. Ran out of address space.");
 }
 
 static int dhcp6_pd_prefix_distribute(
@@ -560,7 +569,7 @@ static int dhcp6_pd_prefix_distribute(
                         continue;
 
                 if (dhcp6_pd_get_assigned_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix) < 0 &&
-                    dhcp6_get_preferred_delegated_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix) < 0)
+                    dhcp6_pd_get_preferred_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix) < 0)
                         continue;
 
                 (void) in6_addr_prefix_to_string(&assigned_prefix, 64, &buf);