mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-09-08 13:44:50 +03:00
network: dhcp6: manage assigned downstream prefixes by using Hashmap
When a system has thousands of downstream interfaces, previously the total cost of finding free subnet ID was O(n^2), where n is the number of downstream interfaces. This makes assigned prefixes are managed by Manager with Hashmap. So, the cost becomes O(n log n).
This commit is contained in:
@@ -56,52 +56,54 @@ static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
|
|||||||
return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0;
|
return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dhcp6_pd_get_link_by_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) {
|
static void link_remove_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) {
|
||||||
union in_addr_union u;
|
void *key;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
assert(link->manager);
|
||||||
|
assert(prefix);
|
||||||
|
|
||||||
|
if (hashmap_get(link->manager->links_by_dhcp6_pd_prefix, prefix) != link)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hashmap_remove2(link->manager->links_by_dhcp6_pd_prefix, prefix, &key);
|
||||||
|
free(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int link_add_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) {
|
||||||
|
_cleanup_free_ struct in6_addr *copy = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
assert(prefix);
|
||||||
|
|
||||||
|
copy = newdup(struct in6_addr, prefix, 1);
|
||||||
|
if (!copy)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = hashmap_ensure_put(&link->manager->links_by_dhcp6_pd_prefix, &in6_addr_hash_ops_free, copy, link);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r > 0)
|
||||||
|
TAKE_PTR(copy);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int link_get_by_dhcp6_pd_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) {
|
||||||
Link *link;
|
Link *link;
|
||||||
|
|
||||||
assert(manager);
|
assert(manager);
|
||||||
assert(prefix);
|
assert(prefix);
|
||||||
|
|
||||||
u.in6 = *prefix;
|
link = hashmap_get(manager->links_by_dhcp6_pd_prefix, prefix);
|
||||||
|
if (!link)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
HASHMAP_FOREACH(link, manager->links_by_index) {
|
|
||||||
if (!link_dhcp6_pd_is_enabled(link))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (link->network->dhcp6_pd_assign) {
|
|
||||||
Address *address;
|
|
||||||
|
|
||||||
SET_FOREACH(address, link->addresses) {
|
|
||||||
if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
|
|
||||||
continue;
|
|
||||||
assert(address->family == AF_INET6);
|
|
||||||
|
|
||||||
if (in_addr_prefix_covers(AF_INET6, &u, 64, &address->in_addr) > 0) {
|
|
||||||
if (ret)
|
if (ret)
|
||||||
*ret = link;
|
*ret = link;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Route *route;
|
|
||||||
|
|
||||||
SET_FOREACH(route, link->routes) {
|
|
||||||
if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
|
|
||||||
continue;
|
|
||||||
assert(route->family == AF_INET6);
|
|
||||||
|
|
||||||
if (in6_addr_equal(&route->dst.in6, prefix)) {
|
|
||||||
if (ret)
|
|
||||||
*ret = link;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) {
|
static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) {
|
||||||
union in_addr_union u;
|
union in_addr_union u;
|
||||||
@@ -175,6 +177,8 @@ int dhcp6_pd_remove(Link *link, bool only_marked) {
|
|||||||
if (link->radv)
|
if (link->radv)
|
||||||
(void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
|
(void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
|
||||||
|
|
||||||
|
link_remove_dhcp6_pd_prefix(link, &route->dst.in6);
|
||||||
|
|
||||||
k = route_remove(route);
|
k = route_remove(route);
|
||||||
if (k < 0)
|
if (k < 0)
|
||||||
r = k;
|
r = k;
|
||||||
@@ -183,17 +187,20 @@ int dhcp6_pd_remove(Link *link, bool only_marked) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SET_FOREACH(address, link->addresses) {
|
SET_FOREACH(address, link->addresses) {
|
||||||
|
struct in6_addr prefix;
|
||||||
|
|
||||||
if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
|
if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
|
||||||
continue;
|
continue;
|
||||||
if (only_marked && !address_is_marked(address))
|
if (only_marked && !address_is_marked(address))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (link->radv) {
|
prefix = address->in_addr.in6;
|
||||||
union in_addr_union prefix = address->in_addr;
|
in6_addr_mask(&prefix, 64);
|
||||||
|
|
||||||
in_addr_mask(AF_INET6, &prefix, 64);
|
if (link->radv)
|
||||||
(void) sd_radv_remove_prefix(link->radv, &prefix.in6, 64);
|
(void) sd_radv_remove_prefix(link->radv, &prefix, 64);
|
||||||
}
|
|
||||||
|
link_remove_dhcp6_pd_prefix(link, &prefix);
|
||||||
|
|
||||||
k = address_remove(address);
|
k = address_remove(address);
|
||||||
if (k < 0)
|
if (k < 0)
|
||||||
@@ -436,7 +443,7 @@ static int dhcp6_pd_assign_prefix(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
return 0;
|
return link_add_dhcp6_pd_prefix(link, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool link_has_preferred_subnet_id(Link *link) {
|
static bool link_has_preferred_subnet_id(Link *link) {
|
||||||
@@ -485,7 +492,7 @@ static int dhcp6_get_preferred_delegated_prefix(
|
|||||||
* This should not fail as we checked the prefix size beforehand */
|
* This should not fail as we checked the prefix size beforehand */
|
||||||
assert_se(in_addr_prefix_covers(AF_INET6, (const union in_addr_union*) pd_prefix, pd_prefix_len, &prefix) > 0);
|
assert_se(in_addr_prefix_covers(AF_INET6, (const union in_addr_union*) pd_prefix, pd_prefix_len, &prefix) > 0);
|
||||||
|
|
||||||
if (dhcp6_pd_get_link_by_prefix(link->manager, &prefix.in6, &assigned_link) >= 0 &&
|
if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix.in6, &assigned_link) >= 0 &&
|
||||||
assigned_link != link) {
|
assigned_link != link) {
|
||||||
_cleanup_free_ char *assigned_buf = NULL;
|
_cleanup_free_ char *assigned_buf = NULL;
|
||||||
|
|
||||||
@@ -502,7 +509,7 @@ static int dhcp6_get_preferred_delegated_prefix(
|
|||||||
for (uint64_t n = 0; n < n_prefixes; n++) {
|
for (uint64_t n = 0; n < n_prefixes; n++) {
|
||||||
/* If we do not have an allocation preference just iterate
|
/* If we do not have an allocation preference just iterate
|
||||||
* through the address space and return the first free prefix. */
|
* through the address space and return the first free prefix. */
|
||||||
if (dhcp6_pd_get_link_by_prefix(link->manager, &prefix.in6, &assigned_link) < 0 ||
|
if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix.in6, &assigned_link) < 0 ||
|
||||||
assigned_link == link) {
|
assigned_link == link) {
|
||||||
*ret = prefix.in6;
|
*ret = prefix.in6;
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -485,6 +485,7 @@ Manager* manager_free(Manager *m) {
|
|||||||
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
|
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
|
||||||
m->links_by_name = hashmap_free(m->links_by_name);
|
m->links_by_name = hashmap_free(m->links_by_name);
|
||||||
m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
|
m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
|
||||||
|
m->links_by_dhcp6_pd_prefix = hashmap_free(m->links_by_dhcp6_pd_prefix);
|
||||||
m->links_by_index = hashmap_free_with_destructor(m->links_by_index, link_unref);
|
m->links_by_index = hashmap_free_with_destructor(m->links_by_index, link_unref);
|
||||||
|
|
||||||
m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref);
|
m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref);
|
||||||
|
@@ -48,6 +48,7 @@ struct Manager {
|
|||||||
Hashmap *links_by_index;
|
Hashmap *links_by_index;
|
||||||
Hashmap *links_by_name;
|
Hashmap *links_by_name;
|
||||||
Hashmap *links_by_hw_addr;
|
Hashmap *links_by_hw_addr;
|
||||||
|
Hashmap *links_by_dhcp6_pd_prefix;
|
||||||
Hashmap *netdevs;
|
Hashmap *netdevs;
|
||||||
OrderedHashmap *networks;
|
OrderedHashmap *networks;
|
||||||
OrderedSet *address_pools;
|
OrderedSet *address_pools;
|
||||||
|
Reference in New Issue
Block a user