diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 3c3794ab87..76f4e4109c 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -531,13 +531,20 @@ static int dhcp6_pd_get_preferred_prefix( } for (uint64_t n = 0; ; n++) { + /* If we do not have an allocation preference just iterate + * through the address space and return the first free prefix. */ + 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. */ + /* Do not use explicitly requested subnet IDs. Note that the corresponding link may not + * appear yet. So, we need to check the ID is not used in any .network files. */ + if (set_contains(link->manager->dhcp6_pd_subnet_ids, &n)) + continue; + + /* Check that the prefix is not assigned to another link. */ if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix, &assigned_link) < 0 || assigned_link == link) { *ret = prefix; @@ -602,8 +609,7 @@ static int dhcp6_pd_distribute_prefix( const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, usec_t lifetime_preferred_usec, - usec_t lifetime_valid_usec, - bool assign_preferred_subnet_id) { + usec_t lifetime_valid_usec) { Link *link; int r; @@ -626,9 +632,6 @@ static int dhcp6_pd_distribute_prefix( if (link == dhcp6_link && !link->network->dhcp6_pd_assign) continue; - if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link)) - continue; - r = dhcp6_pd_assign_prefix(link, pd_prefix, pd_prefix_len, lifetime_preferred_usec, lifetime_valid_usec); if (r < 0) { if (link == dhcp6_link) @@ -978,17 +981,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { &pd_prefix, pd_prefix_len, lifetime_preferred_usec, - lifetime_valid_usec, - true); - if (r < 0) - return r; - - r = dhcp6_pd_distribute_prefix(dhcp6_link, - &pd_prefix, - pd_prefix_len, - lifetime_preferred_usec, - lifetime_valid_usec, - false); + lifetime_valid_usec); if (r < 0) return r; } diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 2141e20622..a122e08800 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -502,6 +502,7 @@ Manager* manager_free(Manager *m) { 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->dhcp6_pd_subnet_ids = set_free(m->dhcp6_pd_subnet_ids); m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref); m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref); @@ -585,7 +586,7 @@ int manager_load_config(Manager *m) { if (r < 0) return r; - return 0; + return manager_build_dhcp6_pd_subnet_ids(m); } bool manager_should_reload(Manager *m) { diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 42b77ee0f5..ae9d5adf1b 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -52,6 +52,7 @@ struct Manager { Hashmap *netdevs; OrderedHashmap *networks; OrderedSet *address_pools; + Set *dhcp6_pd_subnet_ids; usec_t network_dirs_ts_usec; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index c97998848c..443222f610 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -643,7 +643,7 @@ int network_reload(Manager *manager) { ordered_hashmap_free_with_destructor(manager->networks, network_unref); manager->networks = new_networks; - return 0; + return manager_build_dhcp6_pd_subnet_ids(manager); failure: ordered_hashmap_free_with_destructor(new_networks, network_unref); @@ -651,6 +651,32 @@ failure: return r; } +int manager_build_dhcp6_pd_subnet_ids(Manager *manager) { + Network *n; + int r; + + assert(manager); + + set_clear(manager->dhcp6_pd_subnet_ids); + + ORDERED_HASHMAP_FOREACH(n, manager->networks) { + if (n->unmanaged) + continue; + + if (!n->dhcp6_pd) + continue; + + if (n->dhcp6_pd_subnet_id < 0) + continue; + + r = set_ensure_put(&manager->dhcp6_pd_subnet_ids, &uint64_hash_ops, &n->dhcp6_pd_subnet_id); + if (r < 0) + return r; + } + + return 0; +} + static Network *network_free(Network *network) { if (!network) return NULL; diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 942210470c..2fdd4994c4 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -365,6 +365,8 @@ int network_reload(Manager *manager); int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename); int network_verify(Network *network); +int manager_build_dhcp6_pd_subnet_ids(Manager *manager); + int network_get_by_name(Manager *manager, const char *name, Network **ret); void network_apply_anonymize_if_set(Network *network);