diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-10-21 05:16:52 +0900 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-10-26 00:35:22 +0900 |
commit | 56bbe0ef02bc783875c12dd238740a846b775399 (patch) | |
tree | 602f5bf0413de4ce71c582f16f4ed92e8c0cd872 | |
parent | f595b2e9b438460e993c7eeae419c7c16133e19d (diff) | |
download | systemd-56bbe0ef02bc783875c12dd238740a846b775399.tar.gz |
network: dhcp6pd: introduce a simplified and unified method to calculate subnet prefix
-rw-r--r-- | src/network/networkd-dhcp6.c | 75 |
1 files changed, 42 insertions, 33 deletions
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index a92394554a..837368b829 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -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); |