]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: use NetworkConfigSource/State to manage addresses and routes
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 6 Sep 2021 07:09:38 +0000 (16:09 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 24 Sep 2021 12:46:32 +0000 (21:46 +0900)
This also fixes #20146.

27 files changed:
src/network/netdev/l2tp-tunnel.c
src/network/networkd-address-pool.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp-server.h
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-dhcp6.h
src/network/networkd-ipv4acd.c
src/network/networkd-ipv4acd.h
src/network/networkd-ipv4ll.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-ndisc.h
src/network/networkd-network.c
src/network/networkd-nexthop.c
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd-setlink.c
src/network/test-network.c
src/network/test-networkd-conf.c

index a04fa6ac46ad5df0edebcf9f1eb4c253a5324024..cfe54f600cd436c1653c64b34f0dd98c26ca36de 100644 (file)
@@ -285,10 +285,6 @@ static int l2tp_acquire_local_address(L2tpTunnel *t, Link *link, union in_addr_u
                 if (l2tp_acquire_local_address_one(t, a, ret) >= 0)
                         return 1;
 
-        SET_FOREACH(a, link->addresses_foreign)
-                if (l2tp_acquire_local_address_one(t, a, ret) >= 0)
-                        return 1;
-
         return -ENODATA;
 }
 
index e1cefbefbddf161e7a01ea761a208ae100625b97..47dedfe6f20126eeb68a86d1c9f3dc2395bd3432 100644 (file)
@@ -106,24 +106,6 @@ static bool address_pool_prefix_is_taken(
                         if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
                                 return true;
                 }
-
-                /* Don't clash with assigned foreign addresses */
-                SET_FOREACH(a, l->addresses_foreign) {
-                        if (a->family != p->family)
-                                continue;
-
-                        if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
-                                return true;
-                }
-
-                /* Don't clash with addresses already pulled from the pool, but not assigned yet */
-                SET_FOREACH(a, l->pool_addresses) {
-                        if (a->family != p->family)
-                                continue;
-
-                        if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
-                                return true;
-                }
         }
 
         /* And don't clash with configured but un-assigned addresses either */
index a885697969210d41873fa0889dde16f856aa8d4b..4dd4f3691ded6b8b0adbdbc121a0a64f3bb3ebdc 100644 (file)
@@ -9,10 +9,12 @@
 #include "netlink-util.h"
 #include "networkd-address-pool.h"
 #include "networkd-address.h"
+#include "networkd-dhcp-server.h"
 #include "networkd-ipv4acd.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
 #include "networkd-queue.h"
+#include "networkd-route.h"
 #include "parse-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -131,7 +133,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s
 
         address->network = network;
         address->section = TAKE_PTR(n);
-        address->is_static = true;
+        address->source = NETWORK_CONFIG_SOURCE_STATIC;
 
         r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address);
         if (r < 0)
@@ -151,23 +153,7 @@ Address *address_free(Address *address) {
         }
 
         if (address->link) {
-                NDiscAddress *n;
-
                 set_remove(address->link->addresses, address);
-                set_remove(address->link->addresses_foreign, address);
-                set_remove(address->link->addresses_ipv4acd, address);
-                set_remove(address->link->static_addresses, address);
-                if (address->link->dhcp_address == address)
-                        address->link->dhcp_address = NULL;
-                if (address->link->dhcp_address_old == address)
-                        address->link->dhcp_address_old = NULL;
-                set_remove(address->link->dhcp6_addresses, address);
-                set_remove(address->link->dhcp6_addresses_old, address);
-                set_remove(address->link->dhcp6_pd_addresses, address);
-                set_remove(address->link->dhcp6_pd_addresses_old, address);
-                SET_FOREACH(n, address->link->ndisc_addresses)
-                        if (address_equal(n->address, address))
-                                free(set_remove(address->link->ndisc_addresses, n));
 
                 if (address->family == AF_INET6 &&
                     in6_addr_equal(&address->in_addr.in6, &address->link->ipv6ll_address))
@@ -181,6 +167,41 @@ Address *address_free(Address *address) {
         return mfree(address);
 }
 
+bool address_is_ready(const Address *a) {
+        assert(a);
+
+        if (FLAGS_SET(a->flags, IFA_F_TENTATIVE))
+                return false;
+
+        if (FLAGS_SET(a->state, NETWORK_CONFIG_STATE_REMOVING))
+                return false;
+
+        if (FLAGS_SET(a->state, NETWORK_CONFIG_STATE_PROBING))
+                return false;
+
+        if (!FLAGS_SET(a->state, NETWORK_CONFIG_STATE_CONFIGURED))
+                return false;
+
+        return true;
+}
+
+void link_mark_addresses(Link *link, NetworkConfigSource source, const struct in6_addr *router) {
+        Address *a;
+
+        assert(link);
+
+        SET_FOREACH(a, link->addresses) {
+                if (a->source != source)
+                        continue;
+
+                if (source == NETWORK_CONFIG_SOURCE_NDISC &&
+                    router && !in6_addr_equal(router, &a->provider.in6))
+                        continue;
+
+                address_mark(a);
+        }
+}
+
 static bool address_may_have_broadcast(const Address *a) {
         assert(a);
 
@@ -371,67 +392,19 @@ static int address_set_masquerade(Address *address, bool add) {
         return 0;
 }
 
-static int address_add_internal(Link *link, Set **addresses, const Address *in, Address **ret) {
-        _cleanup_(address_freep) Address *address = NULL;
+static int address_add(Link *link, Address *address) {
         int r;
 
         assert(link);
-        assert(addresses);
-        assert(in);
-
-        r = address_dup(in, &address);
-        if (r < 0)
-                return r;
-
-        /* Consider address tentative until we get the real flags from the kernel */
-        address->flags |= IFA_F_TENTATIVE;
+        assert(address);
 
-        r = set_ensure_put(addresses, &address_hash_ops, address);
+        r = set_ensure_put(&link->addresses, &address_hash_ops, address);
         if (r < 0)
                 return r;
         if (r == 0)
                 return -EEXIST;
 
         address->link = link;
-
-        if (ret)
-                *ret = address;
-        TAKE_PTR(address);
-        return 0;
-}
-
-static int address_add_foreign(Link *link, const Address *in, Address **ret) {
-        return address_add_internal(link, &link->addresses_foreign, in, ret);
-}
-
-static int address_add(Link *link, const Address *in, Address **ret) {
-        Address *address;
-        int r;
-
-        assert(link);
-        assert(in);
-
-        r = address_get(link, in, &address);
-        if (r == -ENOENT) {
-                /* Address does not exist, create a new one */
-                r = address_add_internal(link, &link->addresses, in, &address);
-                if (r < 0)
-                        return r;
-        } else if (r == 0) {
-                /* Take over a foreign address */
-                r = set_ensure_put(&link->addresses, &address_hash_ops, address);
-                if (r < 0)
-                        return r;
-
-                set_remove(link->addresses_foreign, address);
-        } else if (r == 1) {
-                /* Already exists, do nothing */
-                ;
-        } else
-                return r;
-
-        if (ret)
-                *ret = address;
         return 0;
 }
 
@@ -441,13 +414,13 @@ static int address_update(Address *address, const Address *src) {
 
         assert(address);
         assert(address->link);
-        assert(src);
 
         link = address->link;
-
-        address->flags = src->flags;
-        address->scope = src->scope;
-        address->cinfo = src->cinfo;
+        if (src) {
+                address->flags = src->flags;
+                address->scope = src->scope;
+                address->cinfo = src->cinfo;
+        }
 
         if (address_is_ready(address) &&
             address->family == AF_INET6 &&
@@ -484,8 +457,8 @@ static int address_drop(Address *address) {
         bool ready;
         int r;
 
-        if (!address)
-                return 0;
+        assert(address);
+        assert(address->link);
 
         ready = address_is_ready(address);
         link = address->link;
@@ -494,7 +467,8 @@ static int address_drop(Address *address) {
         if (r < 0)
                 log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
 
-        address_free(address);
+        if (address->state == 0)
+                address_free(address);
 
         link_update_operstate(link, true);
 
@@ -511,20 +485,12 @@ int address_get(Link *link, const Address *in, Address **ret) {
         assert(in);
 
         existing = set_get(link->addresses, in);
-        if (existing) {
-                if (ret)
-                        *ret = existing;
-                return 1;
-        }
-
-        existing = set_get(link->addresses_foreign, in);
-        if (existing) {
-                if (ret)
-                        *ret = existing;
-                return 0;
-        }
+        if (!existing)
+                return -ENOENT;
 
-        return -ENOENT;
+        if (ret)
+                *ret = existing;
+        return 0;
 }
 
 int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret) {
@@ -546,27 +512,6 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **
         return address_get(link, a, ret);
 }
 
-static int addresses_get_ipv4_address(Set *addresses, const struct in_addr *address, Address **ret) {
-        Address *a;
-
-        assert(address);
-
-        SET_FOREACH(a, addresses) {
-                if (a->family != AF_INET)
-                        continue;
-
-                if (!in4_addr_equal(&a->in_addr.in, address))
-                        continue;
-
-                if (ret)
-                        *ret = a;
-
-                return 0;
-        }
-
-        return -ENOENT;
-}
-
 int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) {
         int r;
 
@@ -587,11 +532,24 @@ int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned ch
                 a->prefixlen = prefixlen;
 
                 return address_get(link, a, ret);
-        }
+        } else {
+                Address *a;
 
-        if (addresses_get_ipv4_address(link->addresses, address, ret) >= 0)
-                return 0;
-        return addresses_get_ipv4_address(link->addresses_foreign, address, ret);
+                SET_FOREACH(a, link->addresses) {
+                        if (a->family != AF_INET)
+                                continue;
+
+                        if (!in4_addr_equal(&a->in_addr.in, address))
+                                continue;
+
+                        if (ret)
+                                *ret = a;
+
+                        return 0;
+                }
+
+                return -ENOENT;
+        }
 }
 
 int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) {
@@ -606,7 +564,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union
         if (family == AF_INET) {
                 HASHMAP_FOREACH(link, manager->links_by_index)
                         if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0)
-                                return !check_ready || address_is_ready(a);
+                                return check_ready ? address_is_ready(a) : address_exists(a);
         } else {
                 _cleanup_(address_freep) Address *tmp = NULL;
 
@@ -619,7 +577,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union
 
                 HASHMAP_FOREACH(link, manager->links_by_index)
                         if (address_get(link, tmp, &a) >= 0)
-                                return !check_ready || address_is_ready(a);
+                                return check_ready ? address_is_ready(a) : address_exists(a);
         }
 
         return false;
@@ -639,7 +597,7 @@ const char* format_lifetime(char *buf, size_t l, uint32_t lifetime) {
 }
 
 static void log_address_debug(const Address *address, const char *str, const Link *link) {
-        _cleanup_free_ char *addr = NULL, *peer = NULL, *flags_str = NULL;
+        _cleanup_free_ char *state = NULL, *addr = NULL, *peer = NULL, *flags_str = NULL;
 
         assert(address);
         assert(str);
@@ -648,14 +606,16 @@ static void log_address_debug(const Address *address, const char *str, const Lin
         if (!DEBUG_LOGGING)
                 return;
 
+        (void) network_config_state_to_string_alloc(address->state, &state);
         (void) in_addr_to_string(address->family, &address->in_addr, &addr);
         if (in_addr_is_set(address->family, &address->in_addr_peer))
                 (void) in_addr_to_string(address->family, &address->in_addr_peer, &peer);
 
         (void) address_flags_to_string_alloc(address->flags, address->family, &flags_str);
 
-        log_link_debug(link, "%s address: %s%s%s/%u (valid %s, preferred %s), flags: %s",
-                       str, strnull(addr), peer ? " peer " : "", strempty(peer), address->prefixlen,
+        log_link_debug(link, "%s %s address (%s): %s%s%s/%u (valid %s, preferred %s), flags: %s",
+                       str, strna(network_config_source_to_string(address->source)), strna(state),
+                       strnull(addr), peer ? " peer " : "", strempty(peer), address->prefixlen,
                        FORMAT_LIFETIME(address->cinfo.ifa_valid),
                        FORMAT_LIFETIME(address->cinfo.ifa_prefered),
                        strna(flags_str));
@@ -698,12 +658,8 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag
 static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
-        assert(rtnl);
         assert(m);
         assert(link);
-        assert(link->address_remove_messages > 0);
-
-        link->address_remove_messages--;
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 0;
@@ -715,16 +671,19 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
         return 1;
 }
 
-int address_remove(const Address *address, Link *link) {
+int address_remove(Address *address) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        Link *link;
         int r;
 
         assert(address);
         assert(IN_SET(address->family, AF_INET, AF_INET6));
-        assert(link);
-        assert(link->ifindex > 0);
-        assert(link->manager);
-        assert(link->manager->rtnl);
+        assert(address->link);
+        assert(address->link->ifindex > 0);
+        assert(address->link->manager);
+        assert(address->link->manager->rtnl);
+
+        link = address->link;
 
         log_address_debug(address, "Removing", link);
 
@@ -744,27 +703,11 @@ int address_remove(const Address *address, Link *link) {
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
 
         link_ref(link);
-        link->address_remove_messages++;
 
+        address_enter_removing(address);
         return 0;
 }
 
-static bool link_is_static_address_configured(const Link *link, const Address *address) {
-        Address *net_address;
-
-        assert(link);
-        assert(address);
-
-        if (!link->network)
-                return false;
-
-        ORDERED_HASHMAP_FOREACH(net_address, link->network->addresses_by_section)
-                if (address_equal(net_address, address))
-                        return true;
-
-        return false;
-}
-
 bool link_address_is_dynamic(const Link *link, const Address *address) {
         Route *route;
 
@@ -777,7 +720,10 @@ bool link_address_is_dynamic(const Link *link, const Address *address) {
         /* Even when the address is leased from a DHCP server, networkd assign the address
          * without lifetime when KeepConfiguration=dhcp. So, let's check that we have
          * corresponding routes with RTPROT_DHCP. */
-        SET_FOREACH(route, link->routes_foreign) {
+        SET_FOREACH(route, link->routes) {
+                if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
+                        continue;
+
                 if (route->protocol != RTPROT_DHCP)
                         continue;
 
@@ -821,6 +767,7 @@ int link_drop_ipv6ll_addresses(Link *link) {
                 _cleanup_(address_freep) Address *a = NULL;
                 unsigned char flags, prefixlen;
                 struct in6_addr address;
+                Address *existing;
                 int ifindex;
 
                 /* NETLINK_GET_STRICT_CHK socket option is supported since kernel 4.20. To support
@@ -866,7 +813,15 @@ int link_drop_ipv6ll_addresses(Link *link) {
                 a->prefixlen = prefixlen;
                 a->flags = flags;
 
-                r = address_remove(a, link);
+                if (address_get(link, a, &existing) < 0) {
+                        r = address_add(link, a);
+                        if (r < 0)
+                                return r;
+
+                        existing = TAKE_PTR(a);
+                }
+
+                r = address_remove(existing);
                 if (r < 0)
                         return r;
         }
@@ -879,63 +834,80 @@ int link_drop_foreign_addresses(Link *link) {
         int k, r = 0;
 
         assert(link);
+        assert(link->network);
 
-        SET_FOREACH(address, link->addresses_foreign) {
+        /* First, mark all addresses. */
+        SET_FOREACH(address, link->addresses) {
                 /* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */
                 if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
                         continue;
 
+                /* Ignore addresses we configured. */
+                if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN)
+                        continue;
+
+                /* Ignore addresses not assigned yet or already removing. */
+                if (!address_exists(address))
+                        continue;
+
                 if (link_address_is_dynamic(link, address)) {
                         if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
                                 continue;
                 } else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
                         continue;
 
-                if (link_is_static_address_configured(link, address)) {
-                        k = address_add(link, address, NULL);
-                        if (k < 0) {
-                                log_link_error_errno(link, k, "Failed to add address: %m");
-                                if (r >= 0)
-                                        r = k;
-                        }
-                } else {
-                        k = address_remove(address, link);
-                        if (k < 0 && r >= 0)
-                                r = k;
-                }
+                address_mark(address);
+        }
+
+        /* Then, unmark requested addresses. */
+        ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section) {
+                Address *existing;
+
+                if (address_get(link, address, &existing) >= 0)
+                        address_unmark(existing);
+        }
+
+        /* Finally, remove all marked addresses. */
+        SET_FOREACH(address, link->addresses) {
+                if (!address_is_marked(address))
+                        continue;
+
+                k = address_remove(address);
+                if (k < 0 && r >= 0)
+                        r = k;
         }
 
         return r;
 }
 
 int link_drop_addresses(Link *link) {
-        Address *address, *pool_address;
+        Address *address;
         int k, r = 0;
 
         assert(link);
 
         SET_FOREACH(address, link->addresses) {
+                /* Ignore addresses not assigned yet or already removing. */
+                if (!address_exists(address))
+                        continue;
+
                 /* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */
                 if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
                         continue;
 
-                k = address_remove(address, link);
+                k = address_remove(address);
                 if (k < 0 && r >= 0) {
                         r = k;
                         continue;
                 }
-
-                SET_FOREACH(pool_address, link->pool_addresses)
-                        if (address_equal(address, pool_address))
-                                address_free(set_remove(link->pool_addresses, pool_address));
         }
 
         return r;
 }
 
 static int address_acquire(Link *link, const Address *original, Address **ret) {
-        union in_addr_union in_addr = IN_ADDR_NULL;
         _cleanup_(address_freep) Address *na = NULL;
+        union in_addr_union in_addr;
         int r;
 
         assert(link);
@@ -969,12 +941,6 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
         na->in_addr = in_addr;
         address_set_broadcast(na);
 
-        r = set_ensure_put(&link->pool_addresses, &address_hash_ops, na);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -EEXIST;
-
         *ret = TAKE_PTR(na);
         return 1;
 }
@@ -1063,6 +1029,25 @@ static int address_configure(
         return 0;
 }
 
+void address_cancel_request(Address *address) {
+        Request req;
+
+        assert(address);
+        assert(address->link);
+
+        if (!address_is_requesting(address))
+                return;
+
+        req = (Request) {
+                .link = address->link,
+                .type = REQUEST_TYPE_ADDRESS,
+                .address = address,
+        };
+
+        request_drop(ordered_set_get(address->link->manager->request_queue, &req));
+        address_cancel_requesting(address);
+}
+
 static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -1084,25 +1069,6 @@ static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
         return 1;
 }
 
-static int static_address_after_configure(Request *req, void *object) {
-        Address *address = object;
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ADDRESS);
-        assert(address);
-
-        link = req->link;
-
-        r = set_ensure_put(&link->static_addresses, &address_hash_ops, address);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to store static address: %m");
-
-        return 0;
-}
-
 int link_request_address(
                 Link *link,
                 Address *address,
@@ -1111,50 +1077,72 @@ int link_request_address(
                 link_netlink_message_handler_t netlink_handler,
                 Request **ret) {
 
-        Address *acquired;
+        Address *acquired, *existing;
         int r;
 
         assert(link);
         assert(address);
+        assert(address->source != NETWORK_CONFIG_SOURCE_FOREIGN);
 
         r = address_acquire(link, address, &acquired);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m");
         if (r > 0) {
-                if (consume_object) {
+                if (consume_object)
                         address_free(address);
-                        consume_object = false; /* address from pool is already managed by Link. */
-                }
+
                 address = acquired;
+                consume_object = true;
+        }
+
+        if (address_get(link, address, &existing) < 0) {
+                _cleanup_(address_freep) Address *tmp = NULL;
+
+                r = address_dup(address, &tmp);
+                if (r < 0)
+                        return r;
+
+                /* Consider address tentative until we get the real flags from the kernel */
+                tmp->flags |= IFA_F_TENTATIVE;
+
+                r = address_add(link, tmp);
+                if (r < 0)
+                        return r;
+
+                existing = TAKE_PTR(tmp);
+        } else {
+                existing->source = address->source;
+                existing->provider = address->provider;
         }
 
+        r = ipv4acd_configure(existing);
+        if (r < 0)
+                return r;
+
         log_address_debug(address, "Requesting", link);
         r = link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object,
                                message_counter, netlink_handler, ret);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to request address: %m");
-        return r;
+        if (r == 0)
+                return 0;
+
+        address_enter_requesting(existing);
+
+        return 1;
 }
 
 int link_request_static_address(Link *link, Address *address, bool consume) {
-        Request *req;
-        int r;
-
         assert(link);
         assert(address);
+        assert(address->source == NETWORK_CONFIG_SOURCE_STATIC);
 
-        r = link_request_address(link, address, consume, &link->static_address_messages,
-                                 static_address_handler, &req);
-        if (r <= 0)
-                return r;
-
-        req->after_configure = static_address_after_configure;
-        return 0;
+        return link_request_address(link, address, consume, &link->static_address_messages,
+                                    static_address_handler, NULL);
 }
 
 int link_request_static_addresses(Link *link) {
         Address *a;
-        Prefix *p;
         int r;
 
         assert(link);
@@ -1168,52 +1156,13 @@ int link_request_static_addresses(Link *link) {
                         return r;
         }
 
-        HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
-                _cleanup_(address_freep) Address *address = NULL;
-
-                if (!p->assign)
-                        continue;
-
-                r = address_new(&address);
-                if (r < 0)
-                        return log_oom();
-
-                r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Could not get RA prefix: %m");
-
-                r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m");
-
-                address->family = AF_INET6;
-                address->route_metric = p->route_metric;
-
-                r = link_request_static_address(link, TAKE_PTR(address), true);
-                if (r < 0)
-                        return r;
-        }
-
-        if (in4_addr_is_set(&link->network->dhcp_server_address)) {
-                _cleanup_(address_freep) Address *address = NULL;
-
-                r = address_new(&address);
-                if (r < 0)
-                        return log_oom();
-
-                address->family = AF_INET;
-                address->in_addr.in = link->network->dhcp_server_address;
-                address->prefixlen = link->network->dhcp_server_address_prefixlen;
-                address_set_broadcast(address);
+        r = link_request_radv_addresses(link);
+        if (r < 0)
+                return r;
 
-                /* The same address may be explicitly configured in [Address] or [Network] section.
-                 * Configure the DHCP server address only when it is not. */
-                if (!link_is_static_address_configured(link, address)) {
-                        r = link_request_static_address(link, TAKE_PTR(address), true);
-                        if (r < 0)
-                                return r;
-                }
-        }
+        r = link_request_dhcp_server_address(link);
+        if (r < 0)
+                return r;
 
         if (link->static_address_messages == 0) {
                 link->static_addresses_configured = true;
@@ -1226,41 +1175,25 @@ int link_request_static_addresses(Link *link) {
         return 0;
 }
 
-static int address_is_ready_to_configure(Link *link, const Address *address) {
-        int r;
-
+static bool address_is_ready_to_configure(Link *link, const Address *address) {
         assert(link);
         assert(address);
 
         if (!link_is_ready_to_configure(link, false))
                 return false;
 
-        if (link->address_remove_messages > 0)
+        if (FLAGS_SET(address->state, NETWORK_CONFIG_STATE_PROBING))
                 return false;
 
-        if (address_get(link, address, NULL) >= 0)
-                return true;
-
-        /* If this is a new address, then refuse adding more than the limit */
+        /* Refuse adding more than the limit */
         if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
-                return log_link_warning_errno(link, SYNTHETIC_ERRNO(E2BIG),
-                                              "Too many addresses are configured, refusing: %m");
-
-        if (address->family == AF_INET &&
-            address->duplicate_address_detection & ADDRESS_FAMILY_IPV4 &&
-            link->hw_addr.length == ETH_ALEN &&
-            !ether_addr_is_null(&link->hw_addr.ether))
-                return ipv4acd_address_is_ready_to_configure(link, address);
-
-        r = address_add(link, address, NULL);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not add address: %m");;
+                return false;
 
         return true;
 }
 
 int request_process_address(Request *req) {
-        Address *a;
+        Address *existing;
         Link *link;
         int r;
 
@@ -1271,26 +1204,18 @@ int request_process_address(Request *req) {
 
         link = req->link;
 
-        r = address_is_ready_to_configure(link, req->address);
-        if (r <= 0)
-                return r;
-
-        r = address_configure(req->address, link, req->netlink_handler);
+        r = address_get(link, req->address, &existing);
         if (r < 0)
-                return r;
+                return log_link_warning_errno(link, r, "Failed to get address: %m");
 
-        /* To prevent a double decrement on failure in after_configure(). */
-        req->message_counter = NULL;
+        if (!address_is_ready_to_configure(link, existing))
+                return 0;
 
-        r = address_get(link, req->address, &a);
+        r = address_configure(req->address, link, req->netlink_handler);
         if (r < 0)
-                return r;
+                return log_link_warning_errno(link, r, "Failed to configure address: %m");
 
-        if (req->after_configure) {
-                r = req->after_configure(req, a);
-                if (r < 0)
-                        return r;
-        }
+        address_enter_configuring(existing);
 
         return 1;
 }
@@ -1441,10 +1366,14 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
 
         switch (type) {
         case RTM_NEWADDR:
-                log_address_debug(tmp, address ? "Remembering updated" : "Remembering foreign", link);
-                if (!address) {
-                        /* An address appeared that we did not request */
-                        r = address_add_foreign(link, tmp, &address);
+                if (address) {
+                        address_enter_configured(address);
+                        log_address_debug(address, "Remembering updated", link);
+                } else {
+                        address_enter_configured(tmp);
+                        log_address_debug(tmp, "Received new", link);
+
+                        r = address_add(link, tmp);
                         if (r < 0) {
                                 _cleanup_free_ char *buf = NULL;
 
@@ -1453,6 +1382,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                                                        strnull(buf));
                                 return 0;
                         }
+
+                        address = TAKE_PTR(tmp);
                 }
 
                 /* address_update() logs internally, so we don't need to here. */
@@ -1463,8 +1394,12 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 break;
 
         case RTM_DELADDR:
-                log_address_debug(tmp, address ? "Forgetting" : "Kernel removed unknown", link);
-                (void) address_drop(address);
+                if (address) {
+                        address_enter_removed(address);
+                        log_address_debug(address, address->state == 0 ? "Forgetting" : "Removed", link);
+                        (void) address_drop(address);
+                } else
+                        log_address_debug(tmp, "Kernel removed unknown", link);
 
                 break;
 
@@ -1922,12 +1857,6 @@ int config_parse_duplicate_address_detection(
         return 0;
 }
 
-bool address_is_ready(const Address *a) {
-        assert(a);
-
-        return !(a->flags & IFA_F_TENTATIVE);
-}
-
 static int address_section_verify(Address *address) {
         if (section_is_invalid(address->section))
                 return -EINVAL;
index 6b5f7ba9c494ad63dbe18dfb6d36051d533e7da2..0f291ba7f6dd28e886bb55c488a066259cd8685e 100644 (file)
@@ -21,10 +21,12 @@ typedef struct Request Request;
 typedef int (*address_ready_callback_t)(Address *address);
 
 struct Address {
+        Link *link;
         Network *network;
         NetworkConfigSection *section;
-
-        Link *link;
+        NetworkConfigSource source;
+        NetworkConfigState state;
+        union in_addr_union provider; /* DHCP server or router address */
 
         int family;
         unsigned char prefixlen;
@@ -42,8 +44,6 @@ struct Address {
 
         bool scope_set:1;
         bool ip_masquerade_done:1;
-        bool is_static:1; /* currently only used by IPv4ACD */
-        bool acd_announced:1;
         AddressFamily duplicate_address_detection;
         sd_ipv4acd *acd;
 
@@ -62,7 +62,7 @@ int address_new(Address **ret);
 Address* address_free(Address *address);
 int address_get(Link *link, const Address *in, Address **ret);
 int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
-int address_remove(const Address *address, Link *link);
+int address_remove(Address *address);
 bool address_equal(const Address *a1, const Address *a2);
 int address_dup(const Address *src, Address **ret);
 bool address_is_ready(const Address *a);
@@ -80,6 +80,7 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **
 int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret);
 int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);
 
+void address_cancel_request(Address *address);
 int link_request_address(
                 Link *link,
                 Address *address,
@@ -99,6 +100,16 @@ void address_hash_func(const Address *a, struct siphash *state);
 int address_compare_func(const Address *a1, const Address *a2);
 extern const struct hash_ops address_hash_ops;
 
+DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Address, address);
+static inline void address_enter_probing(Address *address) {
+        address_update_state(address, NETWORK_CONFIG_STATE_PROBING, NETWORK_CONFIG_STATE_PROBING);
+}
+static inline void address_cancel_probing(Address *address) {
+        address_update_state(address, NETWORK_CONFIG_STATE_PROBING, 0);
+}
+
+void link_mark_addresses(Link *link, NetworkConfigSource source, const struct in6_addr *router);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_broadcast);
 CONFIG_PARSER_PROTOTYPE(config_parse_label);
index f190bcf428b029c0802476c000b90b0a49e9e360..45ef1b4903dd3854182bcf70ea387b471ced56dc 100644 (file)
@@ -16,6 +16,7 @@
 #include "networkd-manager.h"
 #include "networkd-network.h"
 #include "networkd-queue.h"
+#include "networkd-route.h"
 #include "parse-util.h"
 #include "socket-netlink.h"
 #include "string-table.h"
@@ -74,6 +75,40 @@ void network_adjust_dhcp_server(Network *network) {
         }
 }
 
+int link_request_dhcp_server_address(Link *link) {
+        _cleanup_(address_freep) Address *address = NULL;
+        Address *existing;
+        int r;
+
+        assert(link);
+        assert(link->network);
+
+        if (!link_dhcp4_server_enabled(link))
+                return 0;
+
+        if (!in4_addr_is_set(&link->network->dhcp_server_address))
+                return 0;
+
+        r = address_new(&address);
+        if (r < 0)
+                return r;
+
+        address->source = NETWORK_CONFIG_SOURCE_STATIC;
+        address->family = AF_INET;
+        address->in_addr.in = link->network->dhcp_server_address;
+        address->prefixlen = link->network->dhcp_server_address_prefixlen;
+        address_set_broadcast(address);
+
+        if (address_get(link, address, &existing) >= 0 &&
+            address_exists(existing) &&
+            existing->source == NETWORK_CONFIG_SOURCE_STATIC)
+                /* The same address seems explicitly configured in [Address] or [Network] section.
+                 * Configure the DHCP server address only when it is not. */
+                return 0;
+
+        return link_request_static_address(link, TAKE_PTR(address), true);
+}
+
 static int link_find_dhcp_server_address(Link *link, Address **ret) {
         Address *address;
 
@@ -86,13 +121,21 @@ static int link_find_dhcp_server_address(Link *link, Address **ret) {
                                              link->network->dhcp_server_address_prefixlen, ret);
 
         /* If not, then select one from static addresses. */
-        SET_FOREACH(address, link->static_addresses)
-                if (address->family == AF_INET &&
-                    !in4_addr_is_localhost(&address->in_addr.in) &&
-                    in4_addr_is_null(&address->in_addr_peer.in)) {
-                        *ret = address;
-                        return 0;
-                }
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_STATIC)
+                        continue;
+                if (!address_exists(address))
+                        continue;
+                if (address->family != AF_INET)
+                        continue;
+                if (in4_addr_is_localhost(&address->in_addr.in))
+                        continue;
+                if (in4_addr_is_set(&address->in_addr_peer.in))
+                        continue;
+
+                *ret = address;
+                return 0;
+        }
 
         return -ENOENT;
 }
@@ -505,9 +548,6 @@ static bool dhcp_server_is_ready_to_configure(Link *link) {
         if (!link_has_carrier(link))
                 return false;
 
-        if (link->address_remove_messages > 0)
-                return false;
-
         if (!link->static_addresses_configured)
                 return false;
 
index a02cd995ec049c3247a9417add3d072a7709cdc6..1af1c65c66f2cbbfc11904761eda79780199f29d 100644 (file)
@@ -9,6 +9,7 @@ typedef struct Request Request;
 
 void network_adjust_dhcp_server(Network *network);
 
+int link_request_dhcp_server_address(Link *link);
 int link_request_dhcp_server(Link *link);
 int request_process_dhcp_server(Request *req);
 
index 247416a750b47a81600df61a2954c64d6ad0338d..a8d649842307ea117d39c3e70e2008f8d30f5e13 100644 (file)
@@ -27,7 +27,7 @@
 #include "sysctl-util.h"
 
 static int dhcp4_request_address_and_routes(Link *link, bool announce);
-static int dhcp4_remove_all(Link *link);
+static int dhcp4_check_ready(Link *link);
 
 void network_adjust_dhcp4(Network *network) {
         assert(network);
@@ -54,84 +54,106 @@ void network_adjust_dhcp4(Network *network) {
                 network->dhcp_client_identifier = network->dhcp_anonymize ? DHCP_CLIENT_ID_MAC : DHCP_CLIENT_ID_DUID;
 }
 
-static int dhcp4_release_old_lease(Link *link) {
+static int dhcp4_remove_address_and_routes(Link *link, bool only_marked) {
+        Address *address;
         Route *route;
         int k, r = 0;
 
         assert(link);
 
-        if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old))
-                return 0;
-
-        log_link_debug(link, "Removing old DHCPv4 address and routes.");
+        SET_FOREACH(route, link->routes) {
+                if (route->source != NETWORK_CONFIG_SOURCE_DHCP4)
+                        continue;
+                if (only_marked && !route_is_marked(route))
+                        continue;
 
-        SET_FOREACH(route, link->dhcp_routes_old) {
-                k = route_remove(route, NULL, link);
+                k = route_remove(route);
                 if (k < 0)
                         r = k;
+
+                route_cancel_request(route);
         }
 
-        if (link->dhcp_address_old) {
-                k = address_remove(link->dhcp_address_old, link);
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_DHCP4)
+                        continue;
+                if (only_marked && !address_is_marked(address))
+                        continue;
+
+                k = address_remove(address);
                 if (k < 0)
                         r = k;
+
+                address_cancel_request(address);
         }
 
         return r;
 }
 
-static void dhcp4_check_ready(Link *link) {
+static int dhcp4_address_get(Link *link, Address **ret) {
+        Address *address;
+
+        assert(link);
+
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_DHCP4)
+                        continue;
+                if (address_is_marked(address))
+                        continue;
+
+                if (ret)
+                        *ret = address;
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
+static int dhcp4_address_ready_callback(Address *address) {
+        assert(address);
+        assert(address->link);
+
+        /* Do not call this again. */
+        address->callback = NULL;
+
+        return dhcp4_check_ready(address->link);
+}
+
+static int dhcp4_check_ready(Link *link) {
+        Address *address;
         int r;
 
+        assert(link);
+
         if (link->dhcp4_messages > 0) {
                 log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__);
-                return;
+                return 0;
         }
 
-        if (!link->dhcp_address) {
+        if (dhcp4_address_get(link, &address) < 0) {
                 log_link_debug(link, "%s(): DHCPv4 address is not set.", __func__);
-                return;
+                return 0;
         }
 
-        if (!address_is_ready(link->dhcp_address)) {
+        if (!address_is_ready(address)) {
                 log_link_debug(link, "%s(): DHCPv4 address is not ready.", __func__);
-                return;
+                address->callback = dhcp4_address_ready_callback;
+                return 0;
         }
 
         link->dhcp4_configured = true;
+        log_link_debug(link, "DHCPv4 address and routes set.");
 
         /* New address and routes are configured now. Let's release old lease. */
-        r = dhcp4_release_old_lease(link);
-        if (r < 0) {
-                link_enter_failed(link);
-                return;
-        }
+        r = dhcp4_remove_address_and_routes(link, /* only_marked = */ true);
+        if (r < 0)
+                return r;
 
         r = sd_ipv4ll_stop(link->ipv4ll);
         if (r < 0)
-                log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address, ignoring: %m");
+                return log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address: %m");
 
         link_check_ready(link);
-}
-
-static int dhcp4_after_route_configure(Request *req, void *object) {
-        Route *route = object;
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ROUTE);
-        assert(route);
-
-        link = req->link;
-
-        r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, route);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m");
-
-        set_remove(link->dhcp_routes_old, route);
-
         return 0;
 }
 
@@ -140,7 +162,7 @@ static int dhcp4_retry(Link *link) {
 
         assert(link);
 
-        r = dhcp4_remove_all(link);
+        r = dhcp4_remove_address_and_routes(link, /* only_marked = */ false);
         if (r < 0)
                 return r;
 
@@ -192,19 +214,29 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
                 return 1;
         }
 
-        dhcp4_check_ready(link);
+        r = dhcp4_check_ready(link);
+        if (r < 0)
+                link_enter_failed(link);
 
         return 1;
 }
 
 static int dhcp4_request_route(Route *in, Link *link) {
         _cleanup_(route_freep) Route *route = in;
-        Request *req;
+        struct in_addr server;
+        Route *existing;
         int r;
 
         assert(route);
         assert(link);
+        assert(link->dhcp_lease);
+
+        r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &server);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m");
 
+        route->source = NETWORK_CONFIG_SOURCE_DHCP4;
+        route->provider.in = server;
         route->family = AF_INET;
         if (!route->protocol_set)
                 route->protocol = RTPROT_DHCP;
@@ -215,20 +247,13 @@ static int dhcp4_request_route(Route *in, Link *link) {
         if (route->mtu == 0)
                 route->mtu = link->network->dhcp_route_mtu;
 
-        r = link_has_route(link, route);
-        if (r < 0)
-                return r;
-        if (r == 0)
+        if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
                 link->dhcp4_configured = false;
+        else
+                route_unmark(existing);
 
-        r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
-                               dhcp4_route_handler, &req);
-        if (r <= 0)
-                return r;
-
-        req->after_configure = dhcp4_after_route_configure;
-
-        return 0;
+        return link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
+                                  dhcp4_route_handler, NULL);
 }
 
 static bool link_prefixroute(Link *link) {
@@ -675,19 +700,10 @@ static int dhcp4_request_routes_to_ntp(Link *link, const struct in_addr *gw) {
 
 static int dhcp4_request_routes(Link *link) {
         struct in_addr gw = {};
-        Route *rt;
         int r;
 
         assert(link);
-
-        if (!link->dhcp_lease)
-                return 0;
-
-        while ((rt = set_steal_first(link->dhcp_routes))) {
-                r = set_ensure_put(&link->dhcp_routes_old, &route_hash_ops, rt);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m");
-        }
+        assert(link->dhcp_lease);
 
         r = dhcp4_request_prefix_route(link);
         if (r < 0)
@@ -768,27 +784,6 @@ static int dhcp_reset_hostname(Link *link) {
         return 0;
 }
 
-static int dhcp4_remove_all(Link *link) {
-        Route *route;
-        int k, r = 0;
-
-        assert(link);
-
-        SET_FOREACH(route, link->dhcp_routes) {
-                k = route_remove(route, NULL, link);
-                if (k < 0)
-                        r = k;
-        }
-
-        if (link->dhcp_address) {
-                k = address_remove(link->dhcp_address, link);
-                if (k < 0)
-                        r = k;
-        }
-
-        return r;
-}
-
 int dhcp4_lease_lost(Link *link) {
         int k, r = 0;
 
@@ -799,12 +794,7 @@ int dhcp4_lease_lost(Link *link) {
 
         link->dhcp4_configured = false;
 
-        /* dhcp4_lease_lost() may be called during renewing IP address. */
-        k = dhcp4_release_old_lease(link);
-        if (k < 0)
-                r = k;
-
-        k = dhcp4_remove_all(link);
+        k = dhcp4_remove_address_and_routes(link, /* only_marked = */ false);
         if (k < 0)
                 r = k;
 
@@ -819,24 +809,7 @@ int dhcp4_lease_lost(Link *link) {
         link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
         link_dirty(link);
 
-        if (link->network->dhcp_send_decline) {
-                Address *a;
-
-                /* The acquired address may be still ARP probing and not configured. */
-
-                SET_FOREACH(a, link->addresses_ipv4acd)
-                        if (!a->is_static && address_get(link, a, NULL) < 0) {
-                                Request req = {
-                                        .link = link,
-                                        .address = a,
-                                };
-
-                                log_link_debug(link, "Canceling the request to configure DHCPv4 address "IPV4_ADDRESS_FMT_STR,
-                                               IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
-                                request_drop(ordered_set_get(link->manager->request_queue, &req));
-                        }
-        }
-
+        /* If one of the above failed. Do not request nexthops and routes. */
         if (r < 0)
                 return r;
 
@@ -847,43 +820,6 @@ int dhcp4_lease_lost(Link *link) {
         return link_request_static_routes(link, true);
 }
 
-static int dhcp4_address_ready_callback(Address *address) {
-        assert(address);
-
-        /* Do not call this again. */
-        address->callback = NULL;
-
-        dhcp4_check_ready(address->link);
-        return 0;
-}
-
-static int dhcp4_after_address_configure(Request *req, void *object) {
-        Address *address = object;
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ADDRESS);
-        assert(address);
-
-        link = req->link;
-
-        if (!address_equal(link->dhcp_address, address)) {
-                if (link->dhcp_address_old &&
-                    !address_equal(link->dhcp_address_old, link->dhcp_address)) {
-                        /* Still too old address exists? Let's remove it immediately. */
-                        r = address_remove(link->dhcp_address_old, link);
-                        if (r < 0)
-                                return r;
-                }
-                link->dhcp_address_old = link->dhcp_address;
-        }
-
-        link->dhcp_address = address;
-        return 0;
-}
-
 static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -896,12 +832,9 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
         if (r <= 0)
                 return r;
 
-        if (address_is_ready(link->dhcp_address)) {
-                r = dhcp4_address_ready_callback(link->dhcp_address);
-                if (r < 0)
-                        link_enter_failed(link);
-        } else
-                link->dhcp_address->callback = dhcp4_address_ready_callback;
+        r = dhcp4_check_ready(link);
+        if (r < 0)
+                link_enter_failed(link);
 
         return 1;
 }
@@ -911,14 +844,12 @@ static int dhcp4_request_address(Link *link, bool announce) {
         uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
         struct in_addr address, netmask, server;
         unsigned prefixlen;
-        Request *req;
+        Address *existing;
         int r;
 
         assert(link);
         assert(link->network);
-
-        if (!link->dhcp_lease)
-                return 0;
+        assert(link->dhcp_lease);
 
         r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
         if (r < 0)
@@ -973,6 +904,8 @@ static int dhcp4_request_address(Link *link, bool announce) {
         if (r < 0)
                 return log_oom();
 
+        addr->source = NETWORK_CONFIG_SOURCE_DHCP4;
+        addr->provider.in = server;
         addr->family = AF_INET;
         addr->in_addr.in.s_addr = address.s_addr;
         addr->cinfo.ifa_prefered = lifetime;
@@ -988,17 +921,15 @@ static int dhcp4_request_address(Link *link, bool announce) {
         if (r < 0)
                 return r;
 
-        if (address_get(link, addr, NULL) < 0)
+        if (address_get(link, addr, &existing) < 0) /* The address is new. */
                 link->dhcp4_configured = false;
+        else
+                address_unmark(existing);
 
         r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp4_messages,
-                                 dhcp4_address_handler, &req);
+                                 dhcp4_address_handler, NULL);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request DHCPv4 address: %m");
-        if (r == 0)
-                return 0;
-
-        req->after_configure = dhcp4_after_address_configure;
 
         return 0;
 }
@@ -1008,6 +939,9 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) {
 
         assert(link);
 
+        link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP4, NULL);
+        link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP4, NULL);
+
         r = dhcp4_request_address(link, announce);
         if (r < 0)
                 return r;
@@ -1016,8 +950,10 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) {
         if (r < 0)
                 return r;
 
-        link_set_state(link, LINK_STATE_CONFIGURING);
-        link_check_ready(link);
+        if (!link->dhcp4_configured) {
+                link_set_state(link, LINK_STATE_CONFIGURING);
+                link_check_ready(link);
+        }
 
         return 0;
 }
@@ -1372,7 +1308,9 @@ static int dhcp4_set_request_address(Link *link) {
         if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
                 return 0;
 
-        SET_FOREACH(a, link->addresses_foreign) {
+        SET_FOREACH(a, link->addresses) {
+                if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN)
+                        continue;
                 if (a->family != AF_INET)
                         continue;
                 if (link_address_is_dynamic(link, a))
index 0a7471f0e8e83a3483a38720f53d86dd7c90314e..23a0f5fb53d8e9036fb68d66a71523b0cb8779b5 100644 (file)
@@ -20,6 +20,7 @@
 #include "networkd-manager.h"
 #include "networkd-queue.h"
 #include "networkd-radv.h"
+#include "networkd-route.h"
 #include "siphash24.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -54,89 +55,103 @@ 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;
 }
 
-DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p) {
-        if (!p)
-                return NULL;
+static int dhcp6_pd_get_link_by_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) {
+        union in_addr_union u;
+        Link *link;
 
-        if (p->link && p->link->manager) {
-                hashmap_remove(p->link->manager->dhcp6_prefixes, &p->prefix);
-                set_remove(p->link->manager->dhcp6_pd_prefixes, p);
-        }
+        assert(manager);
+        assert(prefix);
 
-        link_unref(p->link);
-        return mfree(p);
-}
+        u.in6 = *prefix;
 
-static void dhcp6_pd_hash_func(const DHCP6DelegatedPrefix *p, struct siphash *state) {
-        assert(p);
+        HASHMAP_FOREACH(link, manager->links_by_index) {
+                if (!link_dhcp6_pd_is_enabled(link))
+                        continue;
 
-        siphash24_compress(&p->pd_prefix, sizeof(p->pd_prefix), state);
-        siphash24_compress(&p->link, sizeof(p->link), state);
-}
+                if (link->network->dhcp6_pd_assign) {
+                        Address *address;
 
-static int dhcp6_pd_compare_func(const DHCP6DelegatedPrefix *a, const DHCP6DelegatedPrefix *b) {
-        int r;
+                        SET_FOREACH(address, link->addresses) {
+                                if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
+                                        continue;
+                                assert(address->family == AF_INET6);
 
-        r = memcmp(&a->pd_prefix, &b->pd_prefix, sizeof(a->pd_prefix));
-        if (r != 0)
-                return r;
+                                if (in_addr_prefix_covers(AF_INET6, &u, 64, &address->in_addr) > 0) {
+                                        if (ret)
+                                                *ret = link;
+                                        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 CMP(a->link, b->link);
+        return -ENOENT;
 }
 
-DEFINE_HASH_OPS(dhcp6_pd_hash_ops, DHCP6DelegatedPrefix, dhcp6_pd_hash_func, dhcp6_pd_compare_func);
-
-static Link *dhcp6_pd_get_link_by_prefix(Link *link, const struct in6_addr *prefix) {
-        DHCP6DelegatedPrefix *pd;
+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;
 
         assert(link);
-        assert(link->manager);
-        assert(prefix);
-
-        pd = hashmap_get(link->manager->dhcp6_prefixes, prefix);
-        if (!pd)
-                return NULL;
-
-        return pd->link;
-}
+        assert(pd_prefix);
 
-static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, struct in6_addr *ret_prefix) {
-        DHCP6DelegatedPrefix *pd, in;
+        if (!link_dhcp6_pd_is_enabled(link))
+                return -ENOENT;
 
-        assert(link);
-        assert(link->manager);
-        assert(pd_prefix);
-        assert(ret_prefix);
+        u.in6 = *pd_prefix;
 
-        in = (DHCP6DelegatedPrefix) {
-                .pd_prefix = *pd_prefix,
-                .link = link,
-        };
+        if (link->network->dhcp6_pd_assign) {
+                Address *address;
 
-        pd = set_get(link->manager->dhcp6_pd_prefixes, &in);
-        if (!pd)
-                return -ENOENT;
+                SET_FOREACH(address, link->addresses) {
+                        if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
+                                continue;
+                        assert(address->family == AF_INET6);
 
-        *ret_prefix = pd->prefix;
-        return 0;
-}
+                        if (in_addr_prefix_covers(AF_INET6, &u, pd_prefix_len, &address->in_addr) <= 0)
+                                continue;
 
-static int dhcp6_pd_remove_old(Link *link, bool force);
+                        if (ret) {
+                                union in_addr_union prefix = address->in_addr;
 
-static int dhcp6_pd_address_callback(Address *address) {
-        Address *a;
+                                in_addr_mask(AF_INET6, &prefix, 64);
+                                *ret = prefix.in6;
+                        }
+                        return 0;
+                }
+        } else {
+                Route *route;
 
-        assert(address);
-        assert(address->link);
+                SET_FOREACH(route, link->routes) {
+                        if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
+                                continue;
+                        assert(route->family == AF_INET6);
 
-        /* Make this called only once */
-        SET_FOREACH(a, address->link->dhcp6_pd_addresses)
-                a->callback = NULL;
+                        if (in_addr_prefix_covers(AF_INET6, &u, pd_prefix_len, &route->dst) > 0) {
+                                if (ret)
+                                        *ret = route->dst.in6;
+                                return 0;
+                        }
+                }
+        }
 
-        return dhcp6_pd_remove_old(address->link, true);
+        return -ENOENT;
 }
 
-static int dhcp6_pd_remove_old(Link *link, bool force) {
+int dhcp6_pd_remove(Link *link, bool only_marked) {
         Address *address;
         Route *route;
         int k, r = 0;
@@ -144,205 +159,160 @@ static int dhcp6_pd_remove_old(Link *link, bool force) {
         assert(link);
         assert(link->manager);
 
-        if (!force && (link->dhcp6_pd_address_messages > 0 || link->dhcp6_pd_route_messages > 0))
-                return 0;
-
-        if (set_isempty(link->dhcp6_pd_addresses_old) && set_isempty(link->dhcp6_pd_routes_old))
+        if (!link_dhcp6_pd_is_enabled(link))
                 return 0;
 
-        if (!force) {
-                bool set_callback = !set_isempty(link->dhcp6_pd_addresses);
+        if (!only_marked)
+                link->dhcp6_pd_configured = false;
 
-                SET_FOREACH(address, link->dhcp6_pd_addresses)
-                        if (address_is_ready(address)) {
-                                set_callback = false;
-                                break;
-                        }
-
-                if (set_callback) {
-                        SET_FOREACH(address, link->dhcp6_pd_addresses)
-                                address->callback = dhcp6_pd_address_callback;
-                        return 0;
-                }
-        }
+        SET_FOREACH(route, link->routes) {
+                if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
+                        continue;
+                if (only_marked && !route_is_marked(route))
+                        continue;
 
-        log_link_debug(link, "Removing old DHCPv6 Prefix Delegation addresses and routes.");
+                if (link->radv)
+                        (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
 
-        SET_FOREACH(route, link->dhcp6_pd_routes_old) {
-                k = route_remove(route, NULL, link);
+                k = route_remove(route);
                 if (k < 0)
                         r = k;
 
-                if (link->radv)
-                        (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
-                dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6));
+                route_cancel_request(route);
         }
 
-        SET_FOREACH(address, link->dhcp6_pd_addresses_old) {
-                k = address_remove(address, link);
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
+                        continue;
+                if (only_marked && !address_is_marked(address))
+                        continue;
+
+                if (link->radv) {
+                        union in_addr_union prefix = address->in_addr;
+
+                        in_addr_mask(AF_INET6, &prefix, 64);
+                        (void) sd_radv_remove_prefix(link->radv, &prefix.in6, 64);
+                }
+
+                k = address_remove(address);
                 if (k < 0)
                         r = k;
+
+                address_cancel_request(address);
         }
 
         return r;
 }
 
-int dhcp6_pd_remove(Link *link) {
-        Address *address;
-        Route *route;
-        int k, r = 0;
-
-        assert(link);
-        assert(link->manager);
+static int dhcp6_pd_check_ready(Link *link);
 
-        if (!link_dhcp6_pd_is_enabled(link))
-                return 0;
+static int dhcp6_pd_address_ready_callback(Address *address) {
+        Address *a;
 
-        link->dhcp6_pd_address_configured = false;
-        link->dhcp6_pd_route_configured = false;
+        assert(address);
+        assert(address->link);
 
-        k = dhcp6_pd_remove_old(link, true);
-        if (k < 0)
-                r = k;
+        SET_FOREACH(a, address->link->addresses)
+                if (a->source == NETWORK_CONFIG_SOURCE_DHCP6PD)
+                        a->callback = NULL;
 
-        if (set_isempty(link->dhcp6_pd_addresses) && set_isempty(link->dhcp6_pd_routes))
-                return r;
+        return dhcp6_pd_check_ready(address->link);
+}
 
-        log_link_debug(link, "Removing DHCPv6 Prefix Delegation addresses and routes.");
+static int dhcp6_pd_check_ready(Link *link) {
+        bool has_ready = false;
+        Address *address;
+        int r;
 
-        SET_FOREACH(route, link->dhcp6_pd_routes) {
-                k = route_remove(route, NULL, link);
-                if (k < 0)
-                        r = k;
+        assert(link);
 
-                if (link->radv)
-                        (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
-                dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6));
+        if (link->dhcp6_pd_messages > 0) {
+                log_link_debug(link, "%s(): DHCPv6PD addresses and routes are not set.", __func__);
+                return 0;
         }
 
-        SET_FOREACH(address, link->dhcp6_pd_addresses) {
-                k = address_remove(address, link);
-                if (k < 0)
-                        r = k;
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
+                        continue;
+                if (address_is_ready(address)) {
+                        has_ready = true;
+                        break;
+                }
         }
 
-        return r;
-}
+        if (!has_ready) {
+                SET_FOREACH(address, link->addresses)
+                        if (address->source == NETWORK_CONFIG_SOURCE_DHCP6PD)
+                                address->callback = dhcp6_pd_address_ready_callback;
 
-static int dhcp6_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        int r;
+                log_link_debug(link, "%s(): no DHCPv6PD address is ready.", __func__);
+                return 0;
+        }
 
-        assert(link);
-        assert(link->dhcp6_pd_route_messages > 0);
+        link->dhcp6_pd_configured = true;
 
-        link->dhcp6_pd_route_messages--;
+        log_link_debug(link, "DHCPv6 PD addresses and routes set.");
 
-        r = route_configure_handler_internal(rtnl, m, link, "Failed to add DHCPv6 Prefix Delegation route");
-        if (r <= 0)
+        r = dhcp6_pd_remove(link, /* only_marked = */ true);
+        if (r < 0)
                 return r;
 
-        if (link->dhcp6_pd_route_messages == 0) {
-                log_link_debug(link, "DHCPv6 prefix delegation routes set");
-                if (link->dhcp6_pd_prefixes_assigned)
-                        link->dhcp6_pd_route_configured = true;
-
-                r = dhcp6_pd_remove_old(link, false);
-                if (r < 0) {
-                        link_enter_failed(link);
-                        return 1;
-                }
-
-                link_check_ready(link);
-        }
-
+        link_check_ready(link);
         return 1;
 }
 
-static int dhcp6_pd_after_route_configure(Request *req, void *object) {
-        Route *route = object;
-        Link *link;
+static int dhcp6_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ROUTE);
-        assert(route);
+        assert(link);
+        assert(link->dhcp6_pd_messages > 0);
 
-        link = req->link;
+        link->dhcp6_pd_messages--;
 
-        r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, route);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m");
+        r = route_configure_handler_internal(rtnl, m, link, "Failed to add DHCPv6 Prefix Delegation route");
+        if (r <= 0)
+                return r;
 
-        set_remove(link->dhcp6_pd_routes_old, route);
+        r = dhcp6_pd_check_ready(link);
+        if (r < 0)
+                link_enter_failed(link);
 
-        return 0;
+        return 1;
 }
 
-static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, const struct in6_addr *pd_prefix) {
-        _cleanup_(dhcp6_pd_freep) DHCP6DelegatedPrefix *pd = NULL;
+static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix) {
         _cleanup_(route_freep) Route *route = NULL;
-        Link *assigned_link;
-        Request *req;
+        Route *existing;
         int r;
 
         assert(link);
-        assert(link->manager);
+        assert(link->network);
         assert(prefix);
-        assert(pd_prefix);
+
+        if (link->network->dhcp6_pd_assign)
+                return 0;
 
         r = route_new(&route);
         if (r < 0)
                 return r;
 
+        route->source = NETWORK_CONFIG_SOURCE_DHCP6PD;
         route->family = AF_INET6;
         route->dst.in6 = *prefix;
         route->dst_prefixlen = 64;
         route->protocol = RTPROT_DHCP;
         route->priority = link->network->dhcp6_pd_route_metric;
 
-        r = link_has_route(link, route);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                link->dhcp6_pd_route_configured = false;
+        if (route_get(NULL, link, route, &existing) < 0)
+                link->dhcp6_pd_configured = false;
+        else
+                route_unmark(existing);
 
-        r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_pd_route_messages,
-                               dhcp6_pd_route_handler, &req);
+        r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_pd_messages,
+                               dhcp6_pd_route_handler, NULL);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request DHCPv6 prefix route: %m");
-        if (r == 0)
-                return 0;
-
-        req->after_configure = dhcp6_pd_after_route_configure;
-
-        assigned_link = dhcp6_pd_get_link_by_prefix(link, prefix);
-        if (assigned_link) {
-                assert(assigned_link == link);
-                return 0;
-        }
-
-        pd = new(DHCP6DelegatedPrefix, 1);
-        if (!pd)
-                return log_oom();
-
-        *pd = (DHCP6DelegatedPrefix) {
-                .prefix = *prefix,
-                .pd_prefix = *pd_prefix,
-                .link = link_ref(link),
-        };
-
-        r = hashmap_ensure_put(&link->manager->dhcp6_prefixes, &in6_addr_hash_ops, &pd->prefix, pd);
-        if (r == -ENOMEM)
-                return log_oom();
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m");
 
-        r = set_ensure_put(&link->manager->dhcp6_pd_prefixes, &dhcp6_pd_hash_ops, pd);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m");
-
-        TAKE_PTR(pd);
         return 0;
 }
 
@@ -350,25 +320,17 @@ static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin
         int r;
 
         assert(link);
-        assert(link->dhcp6_pd_address_messages > 0);
+        assert(link->dhcp6_pd_messages > 0);
 
-        link->dhcp6_pd_address_messages--;
+        link->dhcp6_pd_messages--;
 
         r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 delegated prefix address");
         if (r <= 0)
                 return r;
 
-        if (link->dhcp6_pd_address_messages == 0) {
-                log_link_debug(link, "DHCPv6 delegated prefix addresses set");
-                if (link->dhcp6_pd_prefixes_assigned)
-                        link->dhcp6_pd_address_configured = true;
-
-                r = dhcp6_pd_remove_old(link, false);
-                if (r < 0) {
-                        link_enter_failed(link);
-                        return 1;
-                }
-        }
+        r = dhcp6_pd_check_ready(link);
+        if (r < 0)
+                link_enter_failed(link);
 
         return 1;
 }
@@ -393,27 +355,6 @@ static void log_dhcp6_pd_address(Link *link, const Address *address) {
                       FORMAT_LIFETIME(address->cinfo.ifa_prefered));
 }
 
-static int dhcp6_pd_after_address_configure(Request *req, void *object) {
-        Address *address = object;
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ADDRESS);
-        assert(address);
-
-        link = req->link;
-
-        r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, address);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m");
-
-        set_remove(link->dhcp6_pd_addresses_old, address);
-
-        return 0;
-}
-
 static int dhcp6_pd_request_address(
                 Link *link,
                 const struct in6_addr *prefix,
@@ -421,7 +362,7 @@ static int dhcp6_pd_request_address(
                 uint32_t lifetime_valid) {
 
         _cleanup_(address_freep) Address *address = NULL;
-        Request *req;
+        Address *existing;
         int r;
 
         assert(link);
@@ -445,6 +386,7 @@ static int dhcp6_pd_request_address(
                         return log_link_warning_errno(link, r, "Failed to generate EUI64 address for acquired DHCPv6 delegated prefix: %m");
         }
 
+        address->source = NETWORK_CONFIG_SOURCE_DHCP6PD;
         address->prefixlen = 64;
         address->family = AF_INET6;
         address->cinfo.ifa_prefered = lifetime_preferred;
@@ -454,17 +396,15 @@ static int dhcp6_pd_request_address(
 
         log_dhcp6_pd_address(link, address);
 
-        if (address_get(link, address, NULL) < 0)
-                link->dhcp6_pd_address_configured = false;
+        if (address_get(link, address, &existing) < 0)
+                link->dhcp6_pd_configured = false;
+        else
+                address_unmark(existing);
 
-        r = link_request_address(link, TAKE_PTR(address), true, &link->dhcp6_pd_address_messages,
-                                 dhcp6_pd_address_handler, &req);
+        r = link_request_address(link, TAKE_PTR(address), true, &link->dhcp6_pd_messages,
+                                 dhcp6_pd_address_handler, NULL);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request DHCPv6 delegated prefix address: %m");
-        if (r == 0)
-                return 0;
-
-        req->after_configure = dhcp6_pd_after_address_configure;
 
         return 0;
 }
@@ -472,7 +412,6 @@ static int dhcp6_pd_request_address(
 static int dhcp6_pd_assign_prefix(
                 Link *link,
                 const struct in6_addr *prefix,
-                const struct in6_addr *pd_prefix,
                 uint32_t lifetime_preferred,
                 uint32_t lifetime_valid) {
 
@@ -488,7 +427,7 @@ static int dhcp6_pd_assign_prefix(
                         return r;
         }
 
-        r = dhcp6_pd_request_route(link, prefix, pd_prefix);
+        r = dhcp6_pd_request_route(link, prefix);
         if (r < 0)
                 return r;
 
@@ -508,7 +447,7 @@ static bool link_has_preferred_subnet_id(Link *link) {
 
 static int dhcp6_get_preferred_delegated_prefix(
                 Link *link,
-                const struct in6_addr *masked_pd_prefix,
+                const struct in6_addr *pd_prefix,
                 uint8_t pd_prefix_len,
                 struct in6_addr *ret) {
 
@@ -520,11 +459,11 @@ static int dhcp6_get_preferred_delegated_prefix(
 
         assert(link);
         assert(link->manager);
-        assert(masked_pd_prefix);
+        assert(pd_prefix);
         assert(pd_prefix_len <= 64);
 
         n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
-        prefix.in6 = *masked_pd_prefix;
+        prefix.in6 = *pd_prefix;
 
         if (link_has_preferred_subnet_id(link)) {
                 uint64_t subnet_id = link->network->dhcp6_pd_subnet_id;
@@ -543,10 +482,10 @@ static int dhcp6_get_preferred_delegated_prefix(
 
                 /* 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(in_addr_prefix_covers(AF_INET6, (const union in_addr_union*) masked_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);
 
-                assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix.in6);
-                if (assigned_link && assigned_link != link) {
+                if (dhcp6_pd_get_link_by_prefix(link->manager, &prefix.in6, &assigned_link) >= 0 &&
+                    assigned_link != link) {
                         _cleanup_free_ char *assigned_buf = NULL;
 
                         (void) in6_addr_to_string(&prefix.in6, &assigned_buf);
@@ -562,8 +501,8 @@ static int dhcp6_get_preferred_delegated_prefix(
         for (uint64_t n = 0; n < n_prefixes; n++) {
                 /* If we do not have an allocation preference just iterate
                  * through the address space and return the first free prefix. */
-                assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix.in6);
-                if (!assigned_link || assigned_link == link) {
+                if (dhcp6_pd_get_link_by_prefix(link->manager, &prefix.in6, &assigned_link) < 0 ||
+                    assigned_link == link) {
                         *ret = prefix.in6;
                         return 0;
                 }
@@ -577,7 +516,7 @@ static int dhcp6_get_preferred_delegated_prefix(
 }
 
 static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
-                                      const struct in6_addr *masked_pd_prefix,
+                                      const struct in6_addr *pd_prefix,
                                       uint8_t pd_prefix_len,
                                       uint32_t lifetime_preferred,
                                       uint32_t lifetime_valid,
@@ -588,7 +527,7 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
 
         assert(dhcp6_link);
         assert(dhcp6_link->manager);
-        assert(masked_pd_prefix);
+        assert(pd_prefix);
         assert(pd_prefix_len <= 64);
 
         HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) {
@@ -607,18 +546,15 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
                 if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link))
                         continue;
 
-                r = dhcp6_pd_get_assigned_prefix(link, masked_pd_prefix, &assigned_prefix);
+                r = dhcp6_pd_get_assigned_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix);
                 if (r < 0) {
-                        r = dhcp6_get_preferred_delegated_prefix(link, masked_pd_prefix, pd_prefix_len, &assigned_prefix);
-                        if (r < 0) {
-                                link->dhcp6_pd_prefixes_assigned = false;
+                        r = dhcp6_get_preferred_delegated_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix);
+                        if (r < 0)
                                 continue;
-                        }
                 }
 
                 (void) in6_addr_to_string(&assigned_prefix, &assigned_buf);
-                r = dhcp6_pd_assign_prefix(link, &assigned_prefix, masked_pd_prefix,
-                                           lifetime_preferred, lifetime_valid);
+                r = dhcp6_pd_assign_prefix(link, &assigned_prefix, lifetime_preferred, lifetime_valid);
                 if (r < 0) {
                         log_link_error_errno(link, r, "Unable to assign/update prefix %s/64: %m",
                                              strna(assigned_buf));
@@ -629,29 +565,14 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
 }
 
 static int dhcp6_pd_prepare(Link *link) {
-        Address *address;
-        Route *route;
-        int r;
-
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 0;
 
         if (!link_dhcp6_pd_is_enabled(link))
                 return 0;
 
-        link->dhcp6_pd_prefixes_assigned = true;
-
-        while ((address = set_steal_first(link->dhcp6_pd_addresses))) {
-                r = set_ensure_put(&link->dhcp6_pd_addresses_old, &address_hash_ops, address);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation address: %m");
-        }
-
-        while ((route = set_steal_first(link->dhcp6_pd_routes))) {
-                r = set_ensure_put(&link->dhcp6_pd_routes_old, &route_hash_ops, route);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation route: %m");
-        }
+        link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6PD, NULL);
+        link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP6PD, NULL);
 
         return 0;
 }
@@ -665,23 +586,15 @@ static int dhcp6_pd_finalize(Link *link) {
         if (!link_dhcp6_pd_is_enabled(link))
                 return 0;
 
-        if (link->dhcp6_pd_address_messages == 0) {
-                if (link->dhcp6_pd_prefixes_assigned)
-                        link->dhcp6_pd_address_configured = true;
-        } else
-                log_link_debug(link, "Setting DHCPv6 PD addresses");
-
-        if (link->dhcp6_pd_route_messages == 0) {
-                if (link->dhcp6_pd_prefixes_assigned)
-                        link->dhcp6_pd_route_configured = true;
-        } else
-                log_link_debug(link, "Setting DHCPv6 PD routes");
+        if (link->dhcp6_pd_messages == 0) {
+                link->dhcp6_pd_configured = false;
 
-        r = dhcp6_pd_remove_old(link, false);
-        if (r < 0)
-                return r;
+                r = dhcp6_pd_remove(link, /* only_marked = */ true);
+                if (r < 0)
+                        return r;
+        }
 
-        if (!link->dhcp6_pd_address_configured || !link->dhcp6_pd_route_configured)
+        if (!link->dhcp6_pd_configured)
                 link_set_state(link, LINK_STATE_CONFIGURING);
 
         link_check_ready(link);
@@ -699,7 +612,7 @@ static void dhcp6_pd_prefix_lost(Link *dhcp6_link) {
                 if (link == dhcp6_link)
                         continue;
 
-                r = dhcp6_pd_remove(link);
+                r = dhcp6_pd_remove(link, /* only_marked = */ false);
                 if (r < 0)
                         link_enter_failed(link);
         }
@@ -707,154 +620,124 @@ static void dhcp6_pd_prefix_lost(Link *dhcp6_link) {
         set_clear(dhcp6_link->dhcp6_pd_prefixes);
 }
 
-static int dhcp6_remove_old(Link *link, bool force);
-
-static int dhcp6_address_callback(Address *address) {
-        Address *a;
-
-        assert(address);
-        assert(address->link);
-
-        /* Make this called only once */
-        SET_FOREACH(a, address->link->dhcp6_addresses)
-                a->callback = NULL;
-
-        return dhcp6_remove_old(address->link, true);
-}
-
-static int dhcp6_remove_old(Link *link, bool force) {
+static int dhcp6_remove(Link *link, bool only_marked) {
         Address *address;
         Route *route;
         int k, r = 0;
 
         assert(link);
 
-        if (!force && (link->dhcp6_address_messages > 0 || link->dhcp6_route_messages > 0))
-                return 0;
+        if (!only_marked)
+                link->dhcp6_configured = false;
 
-        if (set_isempty(link->dhcp6_addresses_old) && set_isempty(link->dhcp6_routes_old))
-                return 0;
-
-        if (!force) {
-                bool set_callback = !set_isempty(link->dhcp6_addresses);
+        SET_FOREACH(route, link->routes) {
+                if (route->source != NETWORK_CONFIG_SOURCE_DHCP6)
+                        continue;
+                if (only_marked && !route_is_marked(route))
+                        continue;
 
-                SET_FOREACH(address, link->dhcp6_addresses)
-                        if (address_is_ready(address)) {
-                                set_callback = false;
-                                break;
-                        }
+                k = route_remove(route);
+                if (k < 0)
+                        r = k;
 
-                if (set_callback) {
-                        SET_FOREACH(address, link->dhcp6_addresses)
-                                address->callback = dhcp6_address_callback;
-                        return 0;
-                }
+                route_cancel_request(route);
         }
 
-        log_link_debug(link, "Removing old DHCPv6 addresses and routes.");
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_DHCP6)
+                        continue;
+                if (only_marked && !address_is_marked(address))
+                        continue;
 
-        SET_FOREACH(route, link->dhcp6_routes_old) {
-                k = route_remove(route, NULL, link);
+                k = address_remove(address);
                 if (k < 0)
                         r = k;
-        }
 
-        SET_FOREACH(address, link->dhcp6_addresses_old) {
-                k = address_remove(address, link);
-                if (k < 0)
-                        r = k;
+                address_cancel_request(address);
         }
 
         return r;
 }
 
-static int dhcp6_remove(Link *link) {
-        Address *address;
-        Route *route;
-        int k, r = 0;
+static int dhcp6_check_ready(Link *link);
 
-        assert(link);
+static int dhcp6_address_ready_callback(Address *address) {
+        Address *a;
 
-        link->dhcp6_address_configured = false;
-        link->dhcp6_route_configured = false;
+        assert(address);
+        assert(address->link);
 
-        k = dhcp6_remove_old(link, true);
-        if (k < 0)
-                r = k;
+        SET_FOREACH(a, address->link->addresses)
+                if (a->source == NETWORK_CONFIG_SOURCE_DHCP6)
+                        a->callback = NULL;
 
-        if (set_isempty(link->dhcp6_addresses) && set_isempty(link->dhcp6_routes))
-                return r;
+        return dhcp6_check_ready(address->link);
+}
 
-        log_link_debug(link, "Removing DHCPv6 addresses and routes.");
+static int dhcp6_check_ready(Link *link) {
+        bool has_ready = false;
+        Address *address;
+        int r;
 
-        SET_FOREACH(route, link->dhcp6_routes) {
-                k = route_remove(route, NULL, link);
-                if (k < 0)
-                        r = k;
+        assert(link);
+
+        if (link->dhcp6_messages > 0) {
+                log_link_debug(link, "%s(): DHCPv6 addresses and routes are not set.", __func__);
+                return 0;
         }
 
-        SET_FOREACH(address, link->dhcp6_addresses) {
-                k = address_remove(address, link);
-                if (k < 0)
-                        r = k;
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_DHCP6)
+                        continue;
+                if (address_is_ready(address)) {
+                        has_ready = true;
+                        break;
+                }
         }
 
-        return r;
+        if (!has_ready) {
+                SET_FOREACH(address, link->addresses)
+                        if (address->source == NETWORK_CONFIG_SOURCE_DHCP6)
+                                address->callback = dhcp6_address_ready_callback;
+
+                log_link_debug(link, "%s(): no DHCPv6 address is ready.", __func__);
+                return 0;
+        }
+
+        link->dhcp6_configured = true;
+        log_link_debug(link, "DHCPv6 addresses and routes set.");
+
+        r = dhcp6_remove(link, /* only_marked = */ true);
+        if (r < 0)
+                return r;
+
+        link_check_ready(link);
+        return 0;
 }
 
 static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
-        assert(link->dhcp6_route_messages > 0);
+        assert(link->dhcp6_messages > 0);
 
-        link->dhcp6_route_messages--;
+        link->dhcp6_messages--;
 
         r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCPv6 delegated subnet");
         if (r <= 0)
                 return r;
 
-        if (link->dhcp6_route_messages == 0) {
-                log_link_debug(link, "Unreachable routes for DHCPv6 delegated subnets set");
-                link->dhcp6_route_configured = true;
-
-                r = dhcp6_remove_old(link, false);
-                if (r < 0) {
-                        link_enter_failed(link);
-                        return 1;
-                }
-
-                link_check_ready(link);
-        }
-
-        return 1;
-}
-
-static int dhcp6_after_route_configure(Request *req, void *object) {
-        Route *route = object;
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ROUTE);
-        assert(route);
-
-        link = req->link;
-
-        r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, route);
+        r = dhcp6_check_ready(link);
         if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet: %m");
-
-        set_remove(link->dhcp6_routes_old, route);
+                link_enter_failed(link);
 
-        return 0;
+        return 1;
 }
 
 static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *addr, uint8_t prefixlen) {
         _cleanup_(route_freep) Route *route = NULL;
         _cleanup_free_ char *buf = NULL;
-        Request *req;
+        Route *existing;
         int r;
 
         assert(link);
@@ -872,6 +755,7 @@ static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *ad
         if (r < 0)
                 return log_oom();
 
+        route->source = NETWORK_CONFIG_SOURCE_DHCP6;
         route->family = AF_INET6;
         route->dst.in6 = *addr;
         route->dst_prefixlen = prefixlen;
@@ -879,28 +763,23 @@ static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *ad
         route->type = RTN_UNREACHABLE;
         route->protocol = RTPROT_DHCP;
 
-        r = link_has_route(link, route);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                link->dhcp6_route_configured = false;
+        if (route_get(link->manager, NULL, route, &existing) < 0)
+                link->dhcp6_configured = false;
+        else
+                route_unmark(existing);
 
-        r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_route_messages,
-                               dhcp6_route_handler, &req);
+        r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_messages,
+                               dhcp6_route_handler, NULL);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to request unreachable route for DHCPv6 delegated subnet %s: %m",
                                             strna(buf));
-        if (r == 0)
-                return 0;
-
-        req->after_configure = dhcp6_after_route_configure;
 
         return 0;
 }
 
 static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_t prefixlen) {
-        _cleanup_free_ struct in_addr_prefix *p = NULL;
         _cleanup_free_ char *buf = NULL;
+        struct in_addr_prefix *p;
         int r;
 
         assert(link);
@@ -927,11 +806,9 @@ static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_
                       prefixlen < 48 ? " with prefix length < 48, looks unusual.": "");
 
         /* Store PD prefix even if prefixlen > 64, not to make logged at warning level so frequently. */
-        r = set_ensure_put(&link->dhcp6_pd_prefixes, &in_addr_prefix_hash_ops_free, p);
+        r = set_ensure_consume(&link->dhcp6_pd_prefixes, &in_addr_prefix_hash_ops_free, p);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to store DHCPv6 PD prefix %s: %m", strna(buf));
-        if (r > 0)
-                TAKE_PTR(p);
 
         return prefixlen <= 64;
 }
@@ -1029,102 +906,61 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
         int r;
 
         assert(link);
-        assert(link->dhcp6_address_messages > 0);
+        assert(link->dhcp6_messages > 0);
 
-        link->dhcp6_address_messages--;
+        link->dhcp6_messages--;
 
         r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 address");
         if (r <= 0)
                 return r;
 
-        if (link->dhcp6_address_messages == 0) {
-                log_link_debug(link, "DHCPv6 addresses set");
-                link->dhcp6_address_configured = true;
-
-                r = dhcp6_remove_old(link, false);
-                if (r < 0) {
-                        link_enter_failed(link);
-                        return 1;
-                }
-        }
+        r = dhcp6_check_ready(link);
+        if (r < 0)
+                link_enter_failed(link);
 
         return 1;
 }
 
-static void log_dhcp6_address(Link *link, const Address *address, char **ret) {
+static void log_dhcp6_address(Link *link, const Address *address) {
         _cleanup_free_ char *buffer = NULL;
         bool by_ndisc = false;
         Address *existing;
-        NDiscAddress *na;
-        int log_level, r;
+        int log_level;
 
         assert(link);
         assert(address);
         assert(address->family == AF_INET6);
 
-        (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buffer);
+        (void) in6_addr_to_string(&address->in_addr.in6, &buffer);
 
-        r = address_get(link, address, &existing);
-        if (r < 0) {
+        if (address_get(link, address, &existing) < 0) {
                 /* New address. */
                 log_level = LOG_INFO;
                 goto simple_log;
         } else
                 log_level = LOG_DEBUG;
 
-        if (set_contains(link->dhcp6_addresses, address))
-                /* Already warned. */
-                goto simple_log;
-
         if (address->prefixlen == existing->prefixlen)
                 /* Currently, only conflict in prefix length is reported. */
                 goto simple_log;
 
-        SET_FOREACH(na, link->ndisc_addresses)
-                if (address_compare_func(na->address, existing)) {
-                        by_ndisc = true;
-                        break;
-                }
+        if (existing->source == NETWORK_CONFIG_SOURCE_NDISC)
+                by_ndisc = true;
 
-        log_link_warning(link, "DHCPv6 address %s (valid %s, preferred %s) conflicts the existing address %s %s.",
-                         strna(buffer),
+        log_link_warning(link, "DHCPv6 address %s/%u (valid %s, preferred %s) conflicts the address %s/%u%s.",
+                         strna(buffer), address->prefixlen,
                          FORMAT_LIFETIME(address->cinfo.ifa_valid),
                          FORMAT_LIFETIME(address->cinfo.ifa_prefered),
-                         strna(buffer),
-                         by_ndisc ? "assigned by NDISC. Please try to use or update IPv6Token= setting "
+                         strna(buffer), existing->prefixlen,
+                         by_ndisc ? " assigned by NDisc. Please try to use or update IPv6Token= setting "
                          "to change the address generated by NDISC, or disable UseAutonomousPrefix=" : "");
-        goto finalize;
+        return;
 
 simple_log:
-        log_link_full(link, log_level, "DHCPv6 address %s (valid %s, preferred %s)",
-                      strna(buffer),
+        log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s, preferred %s)",
+                      strna(buffer), address->prefixlen,
                       FORMAT_LIFETIME(address->cinfo.ifa_valid),
                       FORMAT_LIFETIME(address->cinfo.ifa_prefered));
-
-finalize:
-        if (ret)
-                *ret = TAKE_PTR(buffer);
-}
-
-static int dhcp6_after_address_configure(Request *req, void *object) {
-        Address *address = object;
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ADDRESS);
-        assert(address);
-
-        link = req->link;
-
-        r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, address);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store DHCPv6 address: %m");
-
-        set_remove(link->dhcp6_addresses_old, address);
-
-        return 0;
 }
 
 static int dhcp6_request_address(
@@ -1134,14 +970,14 @@ static int dhcp6_request_address(
                 uint32_t lifetime_valid) {
 
         _cleanup_(address_freep) Address *addr = NULL;
-        _cleanup_free_ char *buffer = NULL;
-        Request *req;
+        Address *existing;
         int r;
 
         r = address_new(&addr);
         if (r < 0)
                 return log_oom();
 
+        addr->source = NETWORK_CONFIG_SOURCE_DHCP6;
         addr->family = AF_INET6;
         addr->in_addr.in6 = *ip6_addr;
         addr->flags = IFA_F_NOPREFIXROUTE;
@@ -1149,19 +985,21 @@ static int dhcp6_request_address(
         addr->cinfo.ifa_prefered = lifetime_preferred;
         addr->cinfo.ifa_valid = lifetime_valid;
 
-        log_dhcp6_address(link, addr, &buffer);
+        log_dhcp6_address(link, addr);
 
-        if (address_get(link, addr, NULL) < 0)
-                link->dhcp6_address_configured = false;
+        if (address_get(link, addr, &existing) < 0)
+                link->dhcp6_configured = false;
+        else
+                address_unmark(existing);
 
-        r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp6_address_messages,
-                                 dhcp6_address_handler, &req);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to request DHCPv6 address %s: %m", strna(buffer));
-        if (r == 0)
-                return 0;
+        r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp6_messages,
+                                 dhcp6_address_handler, NULL);
+        if (r < 0) {
+                _cleanup_free_ char *buffer = NULL;
 
-        req->after_configure = dhcp6_after_address_configure;
+                (void) in6_addr_to_string(ip6_addr, &buffer);
+                return log_link_error_errno(link, r, "Failed to request DHCPv6 address %s/128: %m", strna(buffer));
+        }
 
         return 0;
 }
@@ -1215,21 +1053,10 @@ static int dhcp6_address_acquired(Link *link) {
 static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL;
         sd_dhcp6_lease *lease;
-        Address *a;
-        Route *rt;
         int r;
 
-        while ((a = set_steal_first(link->dhcp6_addresses))) {
-                r = set_ensure_put(&link->dhcp6_addresses_old, &address_hash_ops, a);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to store old DHCPv6 address: %m");
-        }
-
-        while ((rt = set_steal_first(link->dhcp6_routes))) {
-                r = set_ensure_put(&link->dhcp6_routes_old, &route_hash_ops, rt);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to store old DHCPv6 route: %m");
-        }
+        link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6, NULL);
+        link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP6, NULL);
 
         r = sd_dhcp6_client_get_lease(client, &lease);
         if (r < 0)
@@ -1250,21 +1077,16 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
                 /* When we had PD prefixes but not now, we need to remove them. */
                 dhcp6_pd_prefix_lost(link);
 
-        if (link->dhcp6_address_messages == 0)
-                link->dhcp6_address_configured = true;
-        else
-                log_link_debug(link, "Setting DHCPv6 addresses");
-
-        if (link->dhcp6_route_messages == 0)
-                link->dhcp6_route_configured = true;
-        else
-                log_link_debug(link, "Setting unreachable routes for DHCPv6 delegated subnets");
+        if (link->dhcp6_messages == 0) {
+                link->dhcp6_configured = true;
 
-        r = dhcp6_remove_old(link, false);
-        if (r < 0)
-                return r;
+                r = dhcp6_remove(link, /* only_marked = */ true);
+                if (r < 0)
+                        return r;
+        } else
+                log_link_debug(link, "Setting DHCPv6 addresses and routes");
 
-        if (!link->dhcp6_address_configured || !link->dhcp6_route_configured)
+        if (!link->dhcp6_configured)
                 link_set_state(link, LINK_STATE_CONFIGURING);
 
         link_check_ready(link);
@@ -1288,7 +1110,7 @@ static int dhcp6_lease_lost(Link *link) {
 
         link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
 
-        r = dhcp6_remove(link);
+        r = dhcp6_remove(link, /* only_marked = */ false);
         if (r < 0)
                 return r;
 
index 23612454d94e32315a8f14a1d93905a416835a98..72f196ad514bc44ea236d6b41579bd3cebdd118f 100644 (file)
@@ -16,18 +16,9 @@ typedef enum DHCP6ClientStartMode {
 typedef struct Link Link;
 typedef struct Request Request;
 
-typedef struct DHCP6DelegatedPrefix {
-        struct in6_addr prefix;     /* Prefix assigned to the link */
-        struct in6_addr pd_prefix;  /* PD prefix provided by DHCP6 lease */
-        Link *link;
-} DHCP6DelegatedPrefix;
-
-DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p);
-DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free);
-
 bool link_dhcp6_with_address_enabled(Link *link);
 bool link_dhcp6_pd_is_enabled(Link *link);
-int dhcp6_pd_remove(Link *link);
+int dhcp6_pd_remove(Link *link, bool only_marked);
 int dhcp6_update_mac(Link *link);
 int dhcp6_start(Link *link);
 int dhcp6_request_information(Link *link, int ir);
index 74a669ead43d594bca393171b080f90d0854bbfb..703278f8b86c0431d394d957a68656481c0b74cb 100644 (file)
@@ -9,42 +9,26 @@
 #include "networkd-link.h"
 #include "networkd-manager.h"
 
-static int static_address_on_stop(Link *link, Address *address) {
+static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_conflict) {
         int r;
 
         assert(link);
         assert(address);
 
-        if (address_get(link, address, NULL) < 0)
-                return 0;
-
-        log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
-                       IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
-
-        r = address_remove(address, link);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
-                                              IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
-
-        return 0;
-}
-
-static int static_address_on_conflict(Link *link, Address *address) {
-        int r;
+        /* Prevent form the address being freed. */
+        address_enter_probing(address);
 
-        assert(link);
-        assert(address);
+        if (!address_exists(address))
+                return 0; /* Not assigned. */
 
-        if (address_get(link, address, NULL) < 0) {
-                log_link_warning(link, "Cannot configure requested address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
+        if (on_conflict)
+                log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
                                  IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
-                return 0;
-        }
-
-        log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
-                         IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+        else
+                log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
+                               IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
 
-        r = address_remove(address, link);
+        r = address_remove(address);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
                                               IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
@@ -52,7 +36,7 @@ static int static_address_on_conflict(Link *link, Address *address) {
         return 0;
 }
 
-static int dhcp4_address_on_conflict(Link *link) {
+static int dhcp4_address_on_conflict(Link *link, Address *address) {
         int r;
 
         assert(link);
@@ -71,11 +55,14 @@ static int dhcp4_address_on_conflict(Link *link) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m");
 
+        /* make the address will be freed. */
+        address_cancel_probing(address);
+
         /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
         return 0;
 }
 
-static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
+static void on_acd(sd_ipv4acd *acd, int event, void *userdata) {
         Address *address = userdata;
         Link *link;
         int r;
@@ -85,13 +72,14 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
         assert(address->acd == acd);
         assert(address->link);
         assert(address->family == AF_INET);
+        assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
 
         link = address->link;
 
         switch (event) {
         case SD_IPV4ACD_EVENT_STOP:
-                if (is_static) {
-                        r = static_address_on_stop(link, address);
+                if (address->source == NETWORK_CONFIG_SOURCE_STATIC) {
+                        r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ false);
                         if (r < 0)
                                 link_enter_failed(link);
                 }
@@ -104,14 +92,17 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
                 log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
                                IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
 
-                address->acd_announced = true;
+                address_cancel_probing(address);
                 break;
 
         case SD_IPV4ACD_EVENT_CONFLICT:
-                if (is_static)
-                        r = static_address_on_conflict(link, address);
+                log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
+                                 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+
+                if (address->source == NETWORK_CONFIG_SOURCE_STATIC)
+                        r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ true);
                 else
-                        r = dhcp4_address_on_conflict(link);
+                        r = dhcp4_address_on_conflict(link, address);
                 if (r < 0)
                         link_enter_failed(link);
                 break;
@@ -121,14 +112,6 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
         }
 }
 
-static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
-        on_acd(acd, event, userdata, true);
-}
-
-static void dhcp4_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
-        on_acd(acd, event, userdata, false);
-}
-
 static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
         Manager *m = userdata;
         struct hw_addr_data hw_addr;
@@ -144,27 +127,31 @@ static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void
         return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
 }
 
-static int ipv4acd_configure(Link *link, const Address *a) {
-        _cleanup_(address_freep) Address *address = NULL;
+int ipv4acd_configure(Address *address) {
+        Link *link;
         int r;
 
-        assert(link);
-        assert(a);
-        assert(a->family == AF_INET);
+        assert(address);
+        assert(address->link);
 
-        log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR,
-                       IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
+        if (address->family != AF_INET)
+                return 0;
 
-        r = address_dup(a, &address);
-        if (r < 0)
-                return r;
+        if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4))
+                return 0;
 
-        r = set_ensure_put(&link->addresses_ipv4acd, &address_hash_ops, address);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -EEXIST;
-        address->link = link;
+        /* Currently, only static and DHCP4 addresses are supported. */
+        assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
+
+        if (address->acd) {
+                address_enter_probing(address);
+                return 0;
+        }
+
+        link = address->link;
+
+        log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR,
+                       IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
 
         r = sd_ipv4acd_new(&address->acd);
         if (r < 0)
@@ -186,9 +173,7 @@ static int ipv4acd_configure(Link *link, const Address *a) {
         if (r < 0)
                 return r;
 
-        r = sd_ipv4acd_set_callback(address->acd,
-                                    address->is_static ? static_address_on_acd : dhcp4_address_on_acd,
-                                    address);
+        r = sd_ipv4acd_set_callback(address->acd, on_acd, address);
         if (r < 0)
                 return r;
 
@@ -202,36 +187,10 @@ static int ipv4acd_configure(Link *link, const Address *a) {
                         return r;
         }
 
-        TAKE_PTR(address);
+        address_enter_probing(address);
         return 0;
 }
 
-int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address) {
-        Address *acd_address;
-        int r;
-
-        acd_address = set_get(link->addresses_ipv4acd, address);
-        if (!acd_address) {
-                r = ipv4acd_configure(link, address);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to configure IPv4ACD client: %m");
-
-                return false;
-        }
-
-        if (!acd_address->acd_announced)
-                return false;
-
-        r = set_ensure_put(&link->addresses, &address_hash_ops, acd_address);
-        if (r < 0)
-                return log_oom();
-        if (r == 0)
-                return log_link_warning_errno(link, SYNTHETIC_ERRNO(EEXIST), "Address already exists.");
-
-        acd_address->flags |= IFA_F_TENTATIVE;
-        return true;
-}
-
 int ipv4acd_update_mac(Link *link) {
         Address *address;
         int k, r = 0;
@@ -243,8 +202,9 @@ int ipv4acd_update_mac(Link *link) {
         if (ether_addr_is_null(&link->hw_addr.ether))
                 return 0;
 
-        SET_FOREACH(address, link->addresses_ipv4acd) {
-                assert(address->acd);
+        SET_FOREACH(address, link->addresses) {
+                if (!address->acd)
+                        continue;
 
                 k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
                 if (k < 0)
@@ -262,7 +222,10 @@ int ipv4acd_start(Link *link) {
 
         assert(link);
 
-        SET_FOREACH(address, link->addresses_ipv4acd) {
+        SET_FOREACH(address, link->addresses) {
+                if (!address->acd)
+                        continue;
+
                 if (sd_ipv4acd_is_running(address->acd))
                         continue;
 
@@ -280,7 +243,10 @@ int ipv4acd_stop(Link *link) {
 
         assert(link);
 
-        SET_FOREACH(address, link->addresses_ipv4acd) {
+        SET_FOREACH(address, link->addresses) {
+                if (!address->acd)
+                        continue;
+
                 k = sd_ipv4acd_stop(address->acd);
                 if (k < 0)
                         r = k;
@@ -295,7 +261,10 @@ int ipv4acd_set_ifname(Link *link) {
 
         assert(link);
 
-        SET_FOREACH(address, link->addresses_ipv4acd) {
+        SET_FOREACH(address, link->addresses) {
+                if (!address->acd)
+                        continue;
+
                 r = sd_ipv4acd_set_ifname(address->acd, link->ifname);
                 if (r < 0)
                         return r;
index c3d8b0ec3874a7e887b021bc6e2fffb6e3f36313..6ebfa362896d18bdc2f00e010c9c1038862dc06f 100644 (file)
@@ -4,7 +4,7 @@
 typedef struct Address Address;
 typedef struct Link Link;
 
-int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address);
+int ipv4acd_configure(Address *address);
 int ipv4acd_update_mac(Link *link);
 int ipv4acd_start(Link *link);
 int ipv4acd_stop(Link *link);
index 83ed3726f7d2cf79476e97d13a7b37091ebd4bbf..a3e5b4ba74c2feeae2f26895e8039b84eb179476 100644 (file)
@@ -28,6 +28,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
         if (r < 0)
                 return -ENOMEM;
 
+        address->source = NETWORK_CONFIG_SOURCE_IPV4LL;
         address->family = AF_INET;
         address->in_addr.in = addr;
         address->prefixlen = 16;
@@ -41,6 +42,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
 
 static int ipv4ll_address_lost(Link *link) {
         _cleanup_(address_freep) Address *address = NULL;
+        Address *existing;
         int r;
 
         assert(link);
@@ -53,10 +55,19 @@ static int ipv4ll_address_lost(Link *link) {
         if (r < 0)
                 return r;
 
+        if (address_get(link, address, &existing) < 0)
+                return 0;
+
+        if (existing->source != NETWORK_CONFIG_SOURCE_IPV4LL)
+                return 0;
+
+        if (!address_exists(existing))
+                return 0;
+
         log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR,
                        IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
 
-        return address_remove(address, link);
+        return address_remove(existing);
 }
 
 static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
index ad39f690f6cc360e5881d067e51388d76a60ef59..dbc8a2347b581c7fbd3f89d18d150b5266421977 100644 (file)
@@ -48,6 +48,7 @@
 #include "networkd-nexthop.h"
 #include "networkd-queue.h"
 #include "networkd-radv.h"
+#include "networkd-route.h"
 #include "networkd-routing-policy-rule.h"
 #include "networkd-setlink.h"
 #include "networkd-sriov.h"
@@ -228,29 +229,12 @@ static Link *link_free(Link *link) {
         link_dns_settings_clear(link);
 
         link->routes = set_free(link->routes);
-        link->routes_foreign = set_free(link->routes_foreign);
-        link->dhcp_routes = set_free(link->dhcp_routes);
-        link->dhcp_routes_old = set_free(link->dhcp_routes_old);
-        link->dhcp6_routes = set_free(link->dhcp6_routes);
-        link->dhcp6_routes_old = set_free(link->dhcp6_routes_old);
-        link->dhcp6_pd_routes = set_free(link->dhcp6_pd_routes);
-        link->dhcp6_pd_routes_old = set_free(link->dhcp6_pd_routes_old);
-        link->ndisc_routes = set_free(link->ndisc_routes);
 
         link->nexthops = set_free(link->nexthops);
 
         link->neighbors = set_free(link->neighbors);
 
         link->addresses = set_free(link->addresses);
-        link->addresses_foreign = set_free(link->addresses_foreign);
-        link->addresses_ipv4acd = set_free(link->addresses_ipv4acd);
-        link->pool_addresses = set_free(link->pool_addresses);
-        link->static_addresses = set_free(link->static_addresses);
-        link->dhcp6_addresses = set_free(link->dhcp6_addresses);
-        link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old);
-        link->dhcp6_pd_addresses = set_free(link->dhcp6_pd_addresses);
-        link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old);
-        link->ndisc_addresses = set_free(link->ndisc_addresses);
 
         link->dhcp6_pd_prefixes = set_free(link->dhcp6_pd_prefixes);
 
@@ -391,7 +375,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
         if (k < 0)
                 r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
 
-        k = dhcp6_pd_remove(link);
+        k = dhcp6_pd_remove(link, /* only_marked = */ false);
         if (k < 0)
                 r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m");
 
@@ -457,7 +441,7 @@ void link_check_ready(Link *link) {
                         _cleanup_free_ char *str = NULL;
 
                         (void) in_addr_prefix_to_string(a->family, &a->in_addr, a->prefixlen, &str);
-                        return (void) log_link_debug(link, "%s(): an address %s is not ready.", __func__, strna(str));
+                        return (void) log_link_debug(link, "%s(): address %s is not ready.", __func__, strna(str));
                 }
 
         if (!link->static_address_labels_configured)
@@ -493,44 +477,42 @@ void link_check_ready(Link *link) {
             !in6_addr_is_set(&link->ipv6ll_address))
                 return (void) log_link_debug(link, "%s(): IPv6LL is not configured yet.", __func__);
 
-        bool has_ndisc_address = false;
-        NDiscAddress *n;
-        SET_FOREACH(n, link->ndisc_addresses)
-                if (!n->marked) {
-                        has_ndisc_address = true;
+        bool has_dynamic_address = false;
+        SET_FOREACH(a, link->addresses) {
+                if (address_is_marked(a))
+                        continue;
+                if (!address_exists(a))
+                        continue;
+                if (IN_SET(a->source,
+                           NETWORK_CONFIG_SOURCE_IPV4LL, NETWORK_CONFIG_SOURCE_DHCP4,
+                           NETWORK_CONFIG_SOURCE_DHCP6, NETWORK_CONFIG_SOURCE_NDISC)) {
+                        has_dynamic_address = true;
                         break;
                 }
+        }
 
-        if ((link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) || link_ipv4ll_enabled(link)) &&
-            !link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address &&
-            !link->ipv4ll_address_configured)
+        if ((link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) ||
+             (link_dhcp6_pd_is_enabled(link) && link->network->dhcp6_pd_assign)) && !has_dynamic_address)
                 /* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */
                 return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
 
         /* Ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */
-        if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) ||
-            (!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link)) ||
-            link_ipv4ll_enabled(link)) {
-
-                if (!link->dhcp4_configured &&
-                    !(link->dhcp6_address_configured && link->dhcp6_route_configured) &&
-                    !(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) &&
-                    !(link->ndisc_addresses_configured && link->ndisc_routes_configured) &&
-                    !link->ipv4ll_address_configured)
+        if (link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) ||
+            link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) ||
+            (!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link))) {
+
+                if (!link->ipv4ll_address_configured && !link->dhcp4_configured &&
+                    !link->dhcp6_configured && !link->dhcp6_pd_configured && !link->ndisc_configured)
                         /* When DHCP[46], NDisc, or IPv4LL is enabled, at least one protocol must be finished. */
                         return (void) log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__);
 
-                log_link_debug(link, "%s(): DHCPv4:%s IPv4LL:%s DHCPv6_addresses:%s DHCPv6_routes:%s "
-                               "DHCPv6PD_addresses:%s DHCPv6PD_routes:%s NDisc_addresses:%s NDisc_routes:%s",
+                log_link_debug(link, "%s(): IPv4LL:%s DHCPv4:%s DHCPv6:%s DHCPv6PD:%s NDisc:%s",
                                __func__,
-                               yes_no(link->dhcp4_configured),
                                yes_no(link->ipv4ll_address_configured),
-                               yes_no(link->dhcp6_address_configured),
-                               yes_no(link->dhcp6_route_configured),
-                               yes_no(link->dhcp6_pd_address_configured),
-                               yes_no(link->dhcp6_pd_route_configured),
-                               yes_no(link->ndisc_addresses_configured),
-                               yes_no(link->ndisc_routes_configured));
+                               yes_no(link->dhcp4_configured),
+                               yes_no(link->dhcp6_configured),
+                               yes_no(link->dhcp6_pd_configured),
+                               yes_no(link->ndisc_configured));
         }
 
         link_set_state(link, LINK_STATE_CONFIGURED);
@@ -1758,18 +1740,6 @@ void link_update_operstate(Link *link, bool also_update_master) {
                         ipv6_scope = MIN(ipv6_scope, address->scope);
         }
 
-        /* for operstate we also take foreign addresses into account */
-        SET_FOREACH(address, link->addresses_foreign) {
-                if (!address_is_ready(address))
-                        continue;
-
-                if (address->family == AF_INET)
-                        ipv4_scope = MIN(ipv4_scope, address->scope);
-
-                if (address->family == AF_INET6)
-                        ipv6_scope = MIN(ipv6_scope, address->scope);
-        }
-
         ipv4_address_state = address_state_from_scope(ipv4_scope);
         ipv6_address_state = address_state_from_scope(ipv6_scope);
         address_state = address_state_from_scope(MIN(ipv4_scope, ipv6_scope));
index 7190310a13210c14d268dd8f22be921367c93122..fb7793f98dac64323b87e2f5e86b8b9fff764f00 100644 (file)
@@ -38,7 +38,6 @@ typedef enum LinkState {
 
 typedef struct Manager Manager;
 typedef struct Network Network;
-typedef struct Address Address;
 typedef struct DUID DUID;
 
 typedef struct Link {
@@ -91,8 +90,6 @@ typedef struct Link {
         unsigned static_nexthop_messages;
         unsigned static_route_messages;
         unsigned static_routing_policy_rule_messages;
-        unsigned address_remove_messages;
-        unsigned route_remove_messages;
         unsigned tc_messages;
         unsigned sr_iov_messages;
         unsigned set_link_messages;
@@ -101,19 +98,12 @@ typedef struct Link {
         unsigned create_stacked_netdev_after_configured_messages;
 
         Set *addresses;
-        Set *addresses_foreign;
-        Set *addresses_ipv4acd;
-        Set *pool_addresses;
-        Set *static_addresses;
         Set *neighbors;
         Set *routes;
-        Set *routes_foreign;
         Set *nexthops;
 
         sd_dhcp_client *dhcp_client;
         sd_dhcp_lease *dhcp_lease;
-        Address *dhcp_address, *dhcp_address_old;
-        Set *dhcp_routes, *dhcp_routes_old;
         char *lease_file;
         unsigned dhcp4_messages;
         bool dhcp4_route_failed:1;
@@ -144,31 +134,18 @@ typedef struct Link {
         sd_ndisc *ndisc;
         Set *ndisc_rdnss;
         Set *ndisc_dnssl;
-        Set *ndisc_addresses;
-        Set *ndisc_routes;
-        unsigned ndisc_addresses_messages;
-        unsigned ndisc_routes_messages;
-        bool ndisc_addresses_configured:1;
-        bool ndisc_routes_configured:1;
+        unsigned ndisc_messages;
+        bool ndisc_configured:1;
 
         sd_radv *radv;
 
         sd_dhcp6_client *dhcp6_client;
         sd_dhcp6_lease *dhcp6_lease;
-        Set *dhcp6_addresses, *dhcp6_addresses_old;
-        Set *dhcp6_routes, *dhcp6_routes_old;
         Set *dhcp6_pd_prefixes;
-        Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old;
-        Set *dhcp6_pd_routes, *dhcp6_pd_routes_old;
-        unsigned dhcp6_address_messages;
-        unsigned dhcp6_route_messages;
-        unsigned dhcp6_pd_address_messages;
-        unsigned dhcp6_pd_route_messages;
-        bool dhcp6_address_configured:1;
-        bool dhcp6_route_configured:1;
-        bool dhcp6_pd_address_configured:1;
-        bool dhcp6_pd_route_configured:1;
-        bool dhcp6_pd_prefixes_assigned:1;
+        unsigned dhcp6_messages;
+        unsigned dhcp6_pd_messages;
+        bool dhcp6_configured:1;
+        bool dhcp6_pd_configured:1;
 
         /* This is about LLDP reception */
         sd_lldp *lldp;
index 9fb12a07f8c1643d34f6944d661404cecca6f81e..2bc2e5d9540db0e37659f623026ce766a6c7226e 100644 (file)
@@ -26,6 +26,7 @@
 #include "netlink-util.h"
 #include "network-internal.h"
 #include "networkd-address-pool.h"
+#include "networkd-address.h"
 #include "networkd-dhcp-server-bus.h"
 #include "networkd-dhcp6.h"
 #include "networkd-link-bus.h"
@@ -35,6 +36,7 @@
 #include "networkd-network-bus.h"
 #include "networkd-nexthop.h"
 #include "networkd-queue.h"
+#include "networkd-route.h"
 #include "networkd-routing-policy-rule.h"
 #include "networkd-speed-meter.h"
 #include "networkd-state-file.h"
@@ -456,9 +458,6 @@ Manager* manager_free(Manager *m) {
 
         m->request_queue = ordered_set_free(m->request_queue);
 
-        m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free);
-        m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free);
-
         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_hw_addr = hashmap_free(m->links_by_hw_addr);
index c80095544ad1a6e55d4968caa4b4a2287742922d..6ba0ae1f0949301fae72209da40c8d1794aa3d0e 100644 (file)
@@ -49,8 +49,6 @@ struct Manager {
         Hashmap *links_by_hw_addr;
         Hashmap *netdevs;
         OrderedHashmap *networks;
-        Hashmap *dhcp6_prefixes;
-        Set *dhcp6_pd_prefixes;
         OrderedSet *address_pools;
 
         usec_t network_dirs_ts_usec;
index 866ea89007a5e202fbcde24690bd3f0d429be355..4979d83f6c35d5689f094198ec16e8027ba3ed1a 100644 (file)
@@ -16,6 +16,7 @@
 #include "networkd-manager.h"
 #include "networkd-ndisc.h"
 #include "networkd-queue.h"
+#include "networkd-route.h"
 #include "networkd-state-file.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -96,115 +97,65 @@ void network_adjust_ipv6_accept_ra(Network *network) {
                 network->ndisc_deny_listed_route_prefix = set_free_free(network->ndisc_deny_listed_route_prefix);
 }
 
-static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force);
-
-static int ndisc_address_callback(Address *address) {
-        struct in6_addr router = {};
-        NDiscAddress *n;
-
-        assert(address);
-        assert(address->link);
-        assert(address->family == AF_INET6);
-
-        SET_FOREACH(n, address->link->ndisc_addresses)
-                if (n->address == address) {
-                        router = n->router;
-                        break;
-                }
-
-        if (in6_addr_is_null(&router)) {
-                _cleanup_free_ char *buf = NULL;
-
-                (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buf);
-                log_link_debug(address->link, "%s is called for %s, but it is already removed, ignoring.",
-                               __func__, strna(buf));
-                return 0;
-        }
-
-        /* Make this called only once */
-        SET_FOREACH(n, address->link->ndisc_addresses)
-                if (in6_addr_equal(&n->router, &router))
-                        n->address->callback = NULL;
-
-        return ndisc_remove_old_one(address->link, &router, true);
-}
-
-static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force) {
-        NDiscAddress *na;
-        NDiscRoute *nr;
+static int ndisc_remove(Link *link, struct in6_addr *router) {
+        bool updated = false;
         NDiscDNSSL *dnssl;
         NDiscRDNSS *rdnss;
+        Address *address;
+        Route *route;
         int k, r = 0;
-        bool updated = false;
 
         assert(link);
-        assert(router);
 
-        if (!force) {
-                bool set_callback = false;
+        SET_FOREACH(route, link->routes) {
+                if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
+                        continue;
+                if (!route_is_marked(route))
+                        continue;
+                if (router && !in6_addr_equal(router, &route->provider.in6))
+                        continue;
 
-                SET_FOREACH(na, link->ndisc_addresses)
-                        if (!na->marked && in6_addr_equal(&na->router, router)) {
-                                set_callback = true;
-                                break;
-                        }
+                k = route_remove(route);
+                if (k < 0)
+                        r = k;
 
-                if (set_callback)
-                        SET_FOREACH(na, link->ndisc_addresses)
-                                if (!na->marked && address_is_ready(na->address)) {
-                                        set_callback = false;
-                                        break;
-                                }
+                route_cancel_request(route);
+        }
 
-                if (set_callback) {
-                        SET_FOREACH(na, link->ndisc_addresses)
-                                if (!na->marked && in6_addr_equal(&na->router, router))
-                                        na->address->callback = ndisc_address_callback;
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
+                        continue;
+                if (!address_is_marked(address))
+                        continue;
+                if (router && !in6_addr_equal(router, &address->provider.in6))
+                        continue;
 
-                        if (DEBUG_LOGGING) {
-                                _cleanup_free_ char *buf = NULL;
+                k = address_remove(address);
+                if (k < 0)
+                        r = k;
 
-                                (void) in6_addr_to_string(router, &buf);
-                                log_link_debug(link, "No SLAAC address obtained from %s is ready. "
-                                               "The old NDisc information will be removed later.",
-                                               strna(buf));
-                        }
-                        return 0;
-                }
+                address_cancel_request(address);
         }
 
-        if (DEBUG_LOGGING) {
-                _cleanup_free_ char *buf = NULL;
+        SET_FOREACH(rdnss, link->ndisc_rdnss) {
+                if (!rdnss->marked)
+                        continue;
+                if (router && !in6_addr_equal(router, &rdnss->router))
+                        continue;
 
-                (void) in6_addr_to_string(router, &buf);
-                log_link_debug(link, "Removing old NDisc information obtained from %s.", strna(buf));
+                free(set_remove(link->ndisc_rdnss, rdnss));
+                updated = true;
         }
 
-        SET_FOREACH(na, link->ndisc_addresses)
-                if (na->marked && in6_addr_equal(&na->router, router)) {
-                        k = address_remove(na->address, link);
-                        if (k < 0)
-                                r = k;
-                }
-
-        SET_FOREACH(nr, link->ndisc_routes)
-                if (nr->marked && in6_addr_equal(&nr->router, router)) {
-                        k = route_remove(nr->route, NULL, link);
-                        if (k < 0)
-                                r = k;
-                }
-
-        SET_FOREACH(rdnss, link->ndisc_rdnss)
-                if (rdnss->marked && in6_addr_equal(&rdnss->router, router)) {
-                        free(set_remove(link->ndisc_rdnss, rdnss));
-                        updated = true;
-                }
+        SET_FOREACH(dnssl, link->ndisc_dnssl) {
+                if (!dnssl->marked)
+                        continue;
+                if (router && !in6_addr_equal(router, &dnssl->router))
+                        continue;
 
-        SET_FOREACH(dnssl, link->ndisc_dnssl)
-                if (dnssl->marked && in6_addr_equal(&dnssl->router, router)) {
-                        free(set_remove(link->ndisc_dnssl, dnssl));
-                        updated = true;
-                }
+                free(set_remove(link->ndisc_dnssl, dnssl));
+                updated = true;
+        }
 
         if (updated)
                 link_dirty(link);
@@ -212,197 +163,100 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool
         return r;
 }
 
-static int ndisc_remove_old(Link *link) {
-        _cleanup_set_free_free_ Set *routers = NULL;
-        _cleanup_free_ struct in6_addr *router = NULL;
-        struct in6_addr *a;
-        NDiscAddress *na;
-        NDiscRoute *nr;
-        NDiscDNSSL *dnssl;
-        NDiscRDNSS *rdnss;
-        int k, r;
+static int ndisc_check_ready(Link *link);
 
-        assert(link);
+static int ndisc_address_ready_callback(Address *address) {
+        Address *a;
 
-        if (link->ndisc_addresses_messages > 0 ||
-            link->ndisc_routes_messages > 0)
-                return 0;
-
-        routers = set_new(&in6_addr_hash_ops);
-        if (!routers)
-                return -ENOMEM;
+        assert(address);
+        assert(address->link);
 
-        SET_FOREACH(na, link->ndisc_addresses)
-                if (!set_contains(routers, &na->router)) {
-                        router = newdup(struct in6_addr, &na->router, 1);
-                        if (!router)
-                                return -ENOMEM;
+        SET_FOREACH(a, address->link->addresses)
+                if (a->source == NETWORK_CONFIG_SOURCE_NDISC)
+                        a->callback = NULL;
 
-                        r = set_put(routers, router);
-                        if (r < 0)
-                                return r;
-
-                        assert(r > 0);
-                        TAKE_PTR(router);
-                }
+        return ndisc_check_ready(address->link);
+}
 
-        SET_FOREACH(nr, link->ndisc_routes)
-                if (!set_contains(routers, &nr->router)) {
-                        router = newdup(struct in6_addr, &nr->router, 1);
-                        if (!router)
-                                return -ENOMEM;
+static int ndisc_check_ready(Link *link) {
+        bool found = false, ready = false;
+        Address *address;
+        int r;
 
-                        r = set_put(routers, router);
-                        if (r < 0)
-                                return r;
+        assert(link);
 
-                        assert(r > 0);
-                        TAKE_PTR(router);
-                }
+        if (link->ndisc_messages > 0) {
+                log_link_debug(link, "%s(): SLAAC addresses and routes are not set.", __func__);
+                return 0;
+        }
 
-        SET_FOREACH(rdnss, link->ndisc_rdnss)
-                if (!set_contains(routers, &rdnss->router)) {
-                        router = newdup(struct in6_addr, &rdnss->router, 1);
-                        if (!router)
-                                return -ENOMEM;
+        SET_FOREACH(address, link->addresses) {
+                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
+                        continue;
 
-                        r = set_put(routers, router);
-                        if (r < 0)
-                                return r;
+                found = true;
 
-                        assert(r > 0);
-                        TAKE_PTR(router);
+                if (address_is_ready(address)) {
+                        ready = true;
+                        break;
                 }
+        }
 
-        SET_FOREACH(dnssl, link->ndisc_dnssl)
-                if (!set_contains(routers, &dnssl->router)) {
-                        router = newdup(struct in6_addr, &dnssl->router, 1);
-                        if (!router)
-                                return -ENOMEM;
-
-                        r = set_put(routers, router);
-                        if (r < 0)
-                                return r;
+        if (found && !ready) {
+                SET_FOREACH(address, link->addresses)
+                        if (address->source == NETWORK_CONFIG_SOURCE_NDISC)
+                                address->callback = ndisc_address_ready_callback;
 
-                        assert(r > 0);
-                        TAKE_PTR(router);
-                }
-
-        r = 0;
-        SET_FOREACH(a, routers) {
-                k = ndisc_remove_old_one(link, a, false);
-                if (k < 0)
-                        r = k;
+                log_link_debug(link, "%s(): no SLAAC address is ready.", __func__);
+                return 0;
         }
 
-        return r;
-}
+        link->ndisc_configured = true;
+        log_link_debug(link, "SLAAC addresses and routes set.");
 
-static void ndisc_route_hash_func(const NDiscRoute *x, struct siphash *state) {
-        route_hash_func(x->route, state);
-}
+        r = ndisc_remove(link, NULL);
+        if (r < 0)
+                return r;
 
-static int ndisc_route_compare_func(const NDiscRoute *a, const NDiscRoute *b) {
-        return route_compare_func(a->route, b->route);
+        link_check_ready(link);
+        return 0;
 }
 
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
-                ndisc_route_hash_ops,
-                NDiscRoute,
-                ndisc_route_hash_func,
-                ndisc_route_compare_func,
-                free);
-
 static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
-        assert(link->ndisc_routes_messages > 0);
+        assert(link->ndisc_messages > 0);
 
-        link->ndisc_routes_messages--;
+        link->ndisc_messages--;
 
         r = route_configure_handler_internal(rtnl, m, link, "Could not set NDisc route");
         if (r <= 0)
                 return r;
 
-        if (link->ndisc_routes_messages == 0) {
-                log_link_debug(link, "NDisc routes set.");
-                link->ndisc_routes_configured = true;
-
-                r = ndisc_remove_old(link);
-                if (r < 0) {
-                        link_enter_failed(link);
-                        return 1;
-                }
-
-                link_check_ready(link);
-        }
-
-        return 1;
-}
-
-static int ndisc_after_route_configure(Request *req, void *object) {
-        _cleanup_free_ NDiscRoute *nr = NULL;
-        NDiscRoute *nr_exist;
-        struct in6_addr router;
-        Route *route = object;
-        sd_ndisc_router *rt;
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ROUTE);
-        assert(req->userdata);
-        assert(route);
-
-        link = req->link;
-        rt = req->userdata;
-
-        r = sd_ndisc_router_get_address(rt, &router);
+        r = ndisc_check_ready(link);
         if (r < 0)
-                return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
-
-        nr = new(NDiscRoute, 1);
-        if (!nr)
-                return log_oom();
-
-        *nr = (NDiscRoute) {
-                .router = router,
-                .route = route,
-        };
-
-        nr_exist = set_get(link->ndisc_routes, nr);
-        if (nr_exist) {
-                nr_exist->marked = false;
-                nr_exist->router = router;
-                return 0;
-        }
-
-        r = set_ensure_put(&link->ndisc_routes, &ndisc_route_hash_ops, nr);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store NDisc SLAAC route: %m");
-        assert(r > 0);
-        TAKE_PTR(nr);
-
-        return 0;
-}
-
-static void ndisc_request_on_free(Request *req) {
-        assert(req);
+                link_enter_failed(link);
 
-        sd_ndisc_router_unref(req->userdata);
+        return 1;
 }
 
 static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
         _cleanup_(route_freep) Route *route = in;
-        Request *req;
+        struct in6_addr router;
+        Route *existing;
         int r;
 
         assert(route);
         assert(link);
         assert(rt);
 
+        r = sd_ndisc_router_get_address(rt, &router);
+        if (r < 0)
+                return r;
+
+        route->source = NETWORK_CONFIG_SOURCE_NDISC;
+        route->provider.in6 = router;
         if (!route->table_set)
                 route->table = link_get_ipv6_accept_ra_route_table(link);
         if (!route->priority_set)
@@ -410,134 +264,58 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
         if (!route->protocol_set)
                 route->protocol = RTPROT_RA;
 
-        r = link_has_route(link, route);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                link->ndisc_routes_configured = false;
-
-        r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_routes_messages,
-                               ndisc_route_handler, &req);
-        if (r <= 0)
-                return r;
-
-        req->userdata = sd_ndisc_router_ref(rt);
-        req->after_configure = ndisc_after_route_configure;
-        req->on_free = ndisc_request_on_free;
-
-        return 0;
-}
-
-static void ndisc_address_hash_func(const NDiscAddress *x, struct siphash *state) {
-        address_hash_func(x->address, state);
-}
+        if (route_get(NULL, link, route, &existing) < 0)
+                link->ndisc_configured = false;
+        else
+                route_unmark(existing);
 
-static int ndisc_address_compare_func(const NDiscAddress *a, const NDiscAddress *b) {
-        return address_compare_func(a->address, b->address);
+        return link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages,
+                                  ndisc_route_handler, NULL);
 }
 
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
-                ndisc_address_hash_ops,
-                NDiscAddress,
-                ndisc_address_hash_func,
-                ndisc_address_compare_func,
-                free);
-
 static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
-        assert(link->ndisc_addresses_messages > 0);
+        assert(link->ndisc_messages > 0);
 
-        link->ndisc_addresses_messages--;
+        link->ndisc_messages--;
 
         r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address");
         if (r <= 0)
                 return r;
 
-        if (link->ndisc_addresses_messages == 0) {
-                log_link_debug(link, "NDisc SLAAC addresses set.");
-                link->ndisc_addresses_configured = true;
-
-                r = ndisc_remove_old(link);
-                if (r < 0) {
-                        link_enter_failed(link);
-                        return 1;
-                }
-        }
-
-        return 1;
-}
-
-static int ndisc_after_address_configure(Request *req, void *object) {
-        _cleanup_free_ NDiscAddress *na = NULL;
-        NDiscAddress *na_exist;
-        struct in6_addr router;
-        sd_ndisc_router *rt;
-        Address *address = object;
-        Link *link;
-        int r;
-
-        assert(req);
-        assert(req->link);
-        assert(req->type == REQUEST_TYPE_ADDRESS);
-        assert(req->userdata);
-        assert(address);
-
-        link = req->link;
-        rt = req->userdata;
-
-        r = sd_ndisc_router_get_address(rt, &router);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
-
-        na = new(NDiscAddress, 1);
-        if (!na)
-                return log_oom();
-
-        *na = (NDiscAddress) {
-                .router = router,
-                .address = address,
-        };
-
-        na_exist = set_get(link->ndisc_addresses, na);
-        if (na_exist) {
-                na_exist->marked = false;
-                na_exist->router = router;
-                return 0;
-        }
-
-        r = set_ensure_put(&link->ndisc_addresses, &ndisc_address_hash_ops, na);
+        r = ndisc_check_ready(link);
         if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m");
-        assert(r > 0);
-        TAKE_PTR(na);
+                link_enter_failed(link);
 
-        return 0;
+        return 1;
 }
 
 static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
         _cleanup_(address_freep) Address *address = in;
-        Request *req;
+        struct in6_addr router;
+        Address *existing;
         int r;
 
         assert(address);
         assert(link);
         assert(rt);
 
-        if (address_get(link, address, NULL) < 0)
-                link->ndisc_addresses_configured = false;
-
-        r = link_request_address(link, TAKE_PTR(address), true, &link->ndisc_addresses_messages,
-                                 ndisc_address_handler, &req);
-        if (r <= 0)
+        r = sd_ndisc_router_get_address(rt, &router);
+        if (r < 0)
                 return r;
 
-        req->userdata = sd_ndisc_router_ref(rt);
-        req->after_configure = ndisc_after_address_configure;
-        req->on_free = ndisc_request_on_free;
+        address->source = NETWORK_CONFIG_SOURCE_NDISC;
+        address->provider.in6 = router;
 
-        return 0;
+        if (address_get(link, address, &existing) < 0)
+                link->ndisc_configured = false;
+        else
+                address_unmark(existing);
+
+        return link_request_address(link, TAKE_PTR(address), true, &link->ndisc_messages,
+                                 ndisc_address_handler, NULL);
 }
 
 static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
@@ -987,7 +765,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
         uint32_t lifetime;
         const struct in6_addr *a;
         struct in6_addr router;
-        NDiscRDNSS *rdnss;
         usec_t time_now;
         bool updated = false;
         int n, r;
@@ -1011,10 +788,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
         if (n < 0)
                 return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m");
 
-        SET_FOREACH(rdnss, link->ndisc_rdnss)
-                if (in6_addr_equal(&rdnss->router, &router))
-                        rdnss->marked = true;
-
         if (lifetime == 0)
                 return 0;
 
@@ -1025,7 +798,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
 
         for (int j = 0; j < n; j++) {
                 _cleanup_free_ NDiscRDNSS *x = NULL;
-                NDiscRDNSS d = {
+                NDiscRDNSS *rdnss, d = {
                         .address = a[j],
                 };
 
@@ -1081,7 +854,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
         struct in6_addr router;
         uint32_t lifetime;
         usec_t time_now;
-        NDiscDNSSL *dnssl;
         bool updated = false;
         char **j;
         int r;
@@ -1105,10 +877,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m");
 
-        SET_FOREACH(dnssl, link->ndisc_dnssl)
-                if (in6_addr_equal(&dnssl->router, &router))
-                        dnssl->marked = true;
-
         if (lifetime == 0)
                 return 0;
 
@@ -1120,6 +888,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
 
         STRV_FOREACH(j, l) {
                 _cleanup_free_ NDiscDNSSL *s = NULL;
+                NDiscDNSSL *dnssl;
 
                 s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
                 if (!s)
@@ -1242,11 +1011,28 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
         }
 }
 
+static void ndisc_mark(Link *link, const struct in6_addr *router) {
+        NDiscRDNSS *rdnss;
+        NDiscDNSSL *dnssl;
+
+        assert(link);
+        assert(router);
+
+        link_mark_addresses(link, NETWORK_CONFIG_SOURCE_NDISC, router);
+        link_mark_routes(link, NETWORK_CONFIG_SOURCE_NDISC, router);
+
+        SET_FOREACH(rdnss, link->ndisc_rdnss)
+                if (in6_addr_equal(&rdnss->router, router))
+                        rdnss->marked = true;
+
+        SET_FOREACH(dnssl, link->ndisc_dnssl)
+                if (in6_addr_equal(&dnssl->router, router))
+                        dnssl->marked = true;
+}
+
 static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         struct in6_addr router;
         uint64_t flags;
-        NDiscAddress *na;
-        NDiscRoute *nr;
         int r;
 
         assert(link);
@@ -1271,13 +1057,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
                 return 0;
         }
 
-        SET_FOREACH(na, link->ndisc_addresses)
-                if (in6_addr_equal(&na->router, &router))
-                        na->marked = true;
-
-        SET_FOREACH(nr, link->ndisc_routes)
-                if (in6_addr_equal(&nr->router, &router))
-                        nr->marked = true;
+        ndisc_mark(link, &router);
 
         r = sd_ndisc_router_get_flags(rt, &flags);
         if (r < 0)
@@ -1307,21 +1087,16 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return r;
 
-        if (link->ndisc_addresses_messages == 0)
-                link->ndisc_addresses_configured = true;
-        else
-                log_link_debug(link, "Setting SLAAC addresses.");
+        if (link->ndisc_messages == 0) {
+                link->ndisc_configured = true;
 
-        if (link->ndisc_routes_messages == 0)
-                link->ndisc_routes_configured = true;
-        else
-                log_link_debug(link, "Setting NDisc routes.");
-
-        r = ndisc_remove_old(link);
-        if (r < 0)
-                return r;
+                r = ndisc_remove(link, &router);
+                if (r < 0)
+                        return r;
+        } else
+                log_link_debug(link, "Setting SLAAC addresses and router.");
 
-        if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured)
+        if (!link->ndisc_configured)
                 link_set_state(link, LINK_STATE_CONFIGURING);
 
         link_check_ready(link);
@@ -1349,9 +1124,8 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router
 
         case SD_NDISC_EVENT_TIMEOUT:
                 log_link_debug(link, "NDisc handler get timeout event");
-                if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) {
-                        link->ndisc_addresses_configured = true;
-                        link->ndisc_routes_configured = true;
+                if (link->ndisc_messages == 0) {
+                        link->ndisc_configured = true;
                         link_check_ready(link);
                 }
                 break;
@@ -1403,6 +1177,9 @@ int ndisc_start(Link *link) {
         if (!link_has_carrier(link))
                 return 0;
 
+        if (in6_addr_is_null(&link->ipv6ll_address))
+                return 0;
+
         log_link_debug(link, "Discovering IPv6 routers");
 
         return sd_ndisc_start(link->ndisc);
@@ -1559,14 +1336,15 @@ int config_parse_address_generation_type(
         return 0;
 }
 
-DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_use_domains, dhcp_use_domains, DHCPUseDomains,
-                         "Failed to parse UseDomains= setting");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
-                         "Failed to parse DHCPv6Client= setting");
 static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
         [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO]     = "no",
         [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
         [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES]    = "yes",
 };
 
-DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_use_domains, dhcp_use_domains, DHCPUseDomains,
+                         "Failed to parse UseDomains= setting");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
+                         "Failed to parse DHCPv6Client= setting");
index bb32a68690c6492dabab59f5081adc39f27b9680..514089c2259a967726c1b48634fd503eee8e0866 100644 (file)
@@ -2,11 +2,11 @@
 #pragma once
 
 #include "conf-parser.h"
-#include "networkd-address.h"
-#include "networkd-link.h"
-#include "networkd-route.h"
 #include "time-util.h"
 
+typedef struct Link Link;
+typedef struct Network Network;
+
 typedef enum IPv6AcceptRAStartDHCP6Client {
         IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO,
         IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS,
@@ -15,20 +15,6 @@ typedef enum IPv6AcceptRAStartDHCP6Client {
         _IPV6_ACCEPT_RA_START_DHCP6_CLIENT_INVALID = -EINVAL,
 } IPv6AcceptRAStartDHCP6Client;
 
-typedef struct NDiscAddress {
-        /* Used when GC'ing old DNS servers when configuration changes. */
-        bool marked;
-        struct in6_addr router;
-        Address *address;
-} NDiscAddress;
-
-typedef struct NDiscRoute {
-        /* Used when GC'ing old DNS servers when configuration changes. */
-        bool marked;
-        struct in6_addr router;
-        Route *route;
-} NDiscRoute;
-
 typedef struct NDiscRDNSS {
         /* Used when GC'ing old DNS servers when configuration changes. */
         bool marked;
@@ -61,6 +47,3 @@ void ndisc_flush(Link *link);
 CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_use_domains);
-
-const char* ipv6_accept_ra_start_dhcp6_client_to_string(IPv6AcceptRAStartDHCP6Client i) _const_;
-IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client_from_string(const char *s) _pure_;
index 4aee6fa43cac19a668880fcc01cf5e9131dc8fc0..6ffa0efbb48bac384b60d025bc7b1227a845cc6b 100644 (file)
@@ -28,6 +28,7 @@
 #include "networkd-network.h"
 #include "networkd-nexthop.h"
 #include "networkd-radv.h"
+#include "networkd-route.h"
 #include "networkd-routing-policy-rule.h"
 #include "networkd-sriov.h"
 #include "parse-util.h"
index 6e878720313099359af56cabe976918925b17513..d1ac730e15fbe2cc146dcb904383b79b8b1a455e 100644 (file)
@@ -743,8 +743,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
                 return false;
 
         if (nexthop_owned_by_link(nexthop)) {
-                Link *l;
-
                 /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated
                  * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of
                  * kernel. */
@@ -752,13 +750,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
                         return false;
                 if (!FLAGS_SET(link->flags, IFF_UP))
                         return false;
-
-                HASHMAP_FOREACH(l, link->manager->links_by_index) {
-                        if (l->address_remove_messages > 0)
-                                return false;
-                        if (l->route_remove_messages > 0)
-                                return false;
-                }
         }
 
         /* All group members must be configured first. */
index afb3795f2fa8db5f48f71df50aeab474b55b5e59..c734c4c3b48a29273986993743184269c20a7384 100644 (file)
@@ -7,11 +7,13 @@
 #include <arpa/inet.h>
 
 #include "dns-domain.h"
+#include "networkd-address.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
 #include "networkd-queue.h"
 #include "networkd-radv.h"
+#include "networkd-route.h"
 #include "parse-util.h"
 #include "string-util.h"
 #include "string-table.h"
@@ -202,6 +204,54 @@ void network_adjust_radv(Network *network) {
         }
 }
 
+static bool link_radv_enabled(Link *link) {
+        assert(link);
+
+        if (!link_ipv6ll_enabled(link))
+                return false;
+
+        return link->network->router_prefix_delegation;
+}
+
+int link_request_radv_addresses(Link *link) {
+        Prefix *p;
+        int r;
+
+        assert(link);
+
+        if (!link_radv_enabled(link))
+                return 0;
+
+        HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
+                _cleanup_(address_freep) Address *address = NULL;
+
+                if (!p->assign)
+                        continue;
+
+                r = address_new(&address);
+                if (r < 0)
+                        return log_oom();
+
+                r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
+                if (r < 0)
+                        return r;
+
+                r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
+                if (r < 0)
+                        return r;
+
+                address->source = NETWORK_CONFIG_SOURCE_STATIC;
+                address->family = AF_INET6;
+                address->route_metric = p->route_metric;
+
+                r = link_request_static_address(link, TAKE_PTR(address), true);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 int config_parse_prefix(
                 const char *unit,
                 const char *filename,
@@ -660,15 +710,6 @@ static int radv_find_uplink(Link *link, Link **ret) {
         return 0;
 }
 
-static bool link_radv_enabled(Link *link) {
-        assert(link);
-
-        if (!link_ipv6ll_enabled(link))
-                return false;
-
-        return link->network->router_prefix_delegation;
-}
-
 static int radv_configure(Link *link) {
         uint16_t router_lifetime;
         Link *uplink = NULL;
index a8adb9bce1c538f091fd462947581bb070896a00..aec3d3386fb1482c0400eacd70f02d3a5e563127 100644 (file)
@@ -51,6 +51,8 @@ void network_drop_invalid_prefixes(Network *network);
 void network_drop_invalid_route_prefixes(Network *network);
 void network_adjust_radv(Network *network);
 
+int link_request_radv_addresses(Link *link);
+
 int radv_update_mac(Link *link);
 int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
                     uint32_t lifetime_preferred, uint32_t lifetime_valid);
index c94887ef8a1bfc3083a33711d16840c13e5f1f13..009afa1b29f61d7f84bbfd3e66ea74b851913f1a 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "netlink-util.h"
+#include "networkd-address.h"
 #include "networkd-ipv4ll.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
@@ -226,6 +227,7 @@ static int route_new_static(Network *network, const char *filename, unsigned sec
         route->protocol = RTPROT_STATIC;
         route->network = network;
         route->section = TAKE_PTR(n);
+        route->source = NETWORK_CONFIG_SOURCE_STATIC;
 
         r = hashmap_ensure_put(&network->routes_by_section, &network_config_hash_ops, route->section, route);
         if (r < 0)
@@ -246,26 +248,11 @@ Route *route_free(Route *route) {
 
         network_config_section_free(route->section);
 
-        if (route->link) {
-                NDiscRoute *n;
-
+        if (route->link)
                 set_remove(route->link->routes, route);
-                set_remove(route->link->routes_foreign, route);
-                set_remove(route->link->dhcp_routes, route);
-                set_remove(route->link->dhcp_routes_old, route);
-                set_remove(route->link->dhcp6_routes, route);
-                set_remove(route->link->dhcp6_routes_old, route);
-                set_remove(route->link->dhcp6_pd_routes, route);
-                set_remove(route->link->dhcp6_pd_routes_old, route);
-                SET_FOREACH(n, route->link->ndisc_routes)
-                        if (route_equal(n->route, route))
-                                free(set_remove(route->link->ndisc_routes, n));
-        }
-
-        if (route->manager) {
+
+        if (route->manager)
                 set_remove(route->manager->routes, route);
-                set_remove(route->manager->routes_foreign, route);
-        }
 
         ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
 
@@ -414,87 +401,77 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 route_compare_func,
                 route_free);
 
-bool route_equal(const Route *r1, const Route *r2) {
-        if (r1 == r2)
-                return true;
+static bool route_type_is_reject(const Route *route) {
+        assert(route);
 
-        if (!r1 || !r2)
-                return false;
+        return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
+}
+
+static bool route_needs_convert(const Route *route) {
+        assert(route);
 
-        return route_compare_func(r1, r2) == 0;
+        return route->nexthop_id > 0 || !ordered_set_isempty(route->multipath_routes);
 }
 
-static int route_get(const Manager *manager, const Link *link, const Route *in, Route **ret) {
-        Route *existing;
+static int route_add(Manager *manager, Link *link, Route *route) {
+        int r;
 
-        assert(manager || link);
-        assert(in);
+        assert(route);
 
-        existing = set_get(link ? link->routes : manager->routes, in);
-        if (existing) {
-                if (ret)
-                        *ret = existing;
-                return 1;
-        }
+        if (route_type_is_reject(route)) {
+                assert(manager);
 
-        existing = set_get(link ? link->routes_foreign : manager->routes_foreign, in);
-        if (existing) {
-                if (ret)
-                        *ret = existing;
-                return 0;
+                r = set_ensure_put(&manager->routes, &route_hash_ops, route);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -EEXIST;
+
+                route->manager = manager;
+        } else {
+                assert(link);
+
+                r = set_ensure_put(&link->routes, &route_hash_ops, route);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -EEXIST;
+
+                route->link = link;
         }
 
-        return -ENOENT;
+        return 0;
 }
 
-static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight) {
-        assert(dest);
-        assert(src);
+int route_get(Manager *manager, Link *link, const Route *in, Route **ret) {
+        Route *route;
 
-        /* This only copies entries used by the above hash and compare functions. */
-
-        dest->family = src->family;
-        dest->src = src->src;
-        dest->src_prefixlen = src->src_prefixlen;
-        dest->dst = src->dst;
-        dest->dst_prefixlen = src->dst_prefixlen;
-        dest->prefsrc = src->prefsrc;
-        dest->scope = src->scope;
-        dest->protocol = src->protocol;
-        if (nh && nh->blackhole)
-                dest->type = RTN_BLACKHOLE;
-        else
-                dest->type = src->type;
-        dest->tos = src->tos;
-        dest->priority = src->priority;
-        dest->table = src->table;
-        dest->initcwnd = src->initcwnd;
-        dest->initrwnd = src->initrwnd;
-        dest->lifetime = src->lifetime;
-        dest->advmss = src->advmss;
-        dest->nexthop_id = src->nexthop_id;
-
-        if (nh) {
-                assert(hashmap_isempty(nh->group));
-
-                dest->gw_family = nh->family;
-                dest->gw = nh->gw;
-                dest->gw_weight = nh_weight != UINT8_MAX ? nh_weight : src->gw_weight;
-        } else if (m) {
-                dest->gw_family = m->gateway.family;
-                dest->gw = m->gateway.address;
-                dest->gw_weight = m->weight;
+        assert(in);
+
+        if (route_type_is_reject(in)) {
+                if (!manager)
+                        return -ENOENT;
+
+                route = set_get(manager->routes, in);
         } else {
-                dest->gw_family = src->gw_family;
-                dest->gw = src->gw;
-                dest->gw_weight = src->gw_weight;
+                if (!link)
+                        return -ENOENT;
+
+                route = set_get(link->routes, in);
         }
+        if (!route)
+                return -ENOENT;
+
+        if (ret)
+                *ret = route;
+
+        return 0;
 }
 
 int route_dup(const Route *src, Route **ret) {
         _cleanup_(route_freep) Route *dest = NULL;
-        MultipathRoute *m;
-        int r;
+
+        /* This does not copy mulipath routes. */
 
         assert(src);
         assert(ret);
@@ -511,278 +488,261 @@ int route_dup(const Route *src, Route **ret) {
         dest->multipath_routes = NULL;
         dest->expire = NULL;
 
-        ORDERED_SET_FOREACH(m, src->multipath_routes) {
-                _cleanup_(multipath_route_freep) MultipathRoute *n = NULL;
+        *ret = TAKE_PTR(dest);
+        return 0;
+}
 
-                r = multipath_route_dup(m, &n);
-                if (r < 0)
-                        return r;
+static void route_apply_nexthop(Route *route, const NextHop *nh, uint8_t nh_weight) {
+        assert(route);
+        assert(nh);
+        assert(hashmap_isempty(nh->group));
 
-                r = ordered_set_ensure_put(&dest->multipath_routes, NULL, n);
-                if (r < 0)
-                        return r;
+        route->gw_family = nh->family;
+        route->gw = nh->gw;
 
-                TAKE_PTR(n);
-        }
+        if (nh_weight != UINT8_MAX)
+                route->gw_weight = nh_weight;
 
-        *ret = TAKE_PTR(dest);
-        return 0;
+        if (nh->blackhole)
+                route->type = RTN_BLACKHOLE;
 }
 
-static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, Route **ret) {
-        _cleanup_(route_freep) Route *route = NULL;
-        int r;
+static void route_apply_multipath_route(Route *route, const MultipathRoute *m) {
+        assert(route);
+        assert(m);
 
-        assert(manager || link);
-        assert(routes);
-        assert(in);
+        route->gw_family = m->gateway.family;
+        route->gw = m->gateway.address;
+        route->gw_weight = m->weight;
+}
 
-        r = route_new(&route);
-        if (r < 0)
-                return r;
+static int multipath_route_get_link(Manager *manager, const MultipathRoute *m, Link **ret) {
+        int r;
 
-        route_copy(route, in, NULL, NULL, UINT8_MAX);
+        assert(manager);
+        assert(m);
 
-        r = set_ensure_put(routes, &route_hash_ops, route);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -EEXIST;
+        if (m->ifname) {
+                r = link_get_by_name(manager, m->ifname, ret);
+                return r < 0 ? r : 1;
 
-        route->link = link;
-        route->manager = manager;
+        } else if (m->ifindex > 0) { /* Always ignore ifindex if ifname is set. */
+                r = link_get_by_index(manager, m->ifindex, ret);
+                return r < 0 ? r : 1;
+        }
 
         if (ret)
-                *ret = route;
-
-        route = NULL;
-
+                *ret = NULL;
         return 0;
 }
 
-static int route_add_foreign(Manager *manager, Link *link, const Route *in, Route **ret) {
-        assert(manager || link);
-        return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret);
-}
+typedef struct ConvertedRoutes {
+        size_t n;
+        Route **routes;
+        Link **links;
+} ConvertedRoutes;
 
-static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) {
-        _cleanup_(route_freep) Route *tmp = NULL;
-        Route *route;
-        int r;
+static ConvertedRoutes *converted_routes_free(ConvertedRoutes *c) {
+        if (!c)
+                return NULL;
 
-        assert(manager || link);
-        assert(in);
+        for (size_t i = 0; i < c->n; i++)
+                route_free(c->routes[i]);
 
-        if (nh) {
-                assert(hashmap_isempty(nh->group));
+        free(c->routes);
+        free(c->links);
 
-                r = route_new(&tmp);
-                if (r < 0)
-                        return r;
+        return mfree(c);
+}
 
-                route_copy(tmp, in, NULL, nh, nh_weight);
-                in = tmp;
-        } else if (m) {
-                assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex));
+DEFINE_TRIVIAL_CLEANUP_FUNC(ConvertedRoutes*, converted_routes_free);
 
-                r = route_new(&tmp);
-                if (r < 0)
-                        return r;
+static int converted_routes_new(size_t n, ConvertedRoutes **ret) {
+        _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
+        _cleanup_free_ Route **routes = NULL;
+        _cleanup_free_ Link **links = NULL;
 
-                route_copy(tmp, in, m, NULL, UINT8_MAX);
-                in = tmp;
-        }
+        assert(n > 0);
+        assert(ret);
 
-        r = route_get(manager, link, in, &route);
-        if (r == -ENOENT) {
-                /* Route does not exist, create a new one */
-                r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, &route);
-                if (r < 0)
-                        return r;
-        } else if (r == 0) {
-                /* Take over a foreign route */
-                r = set_ensure_put(link ? &link->routes : &manager->routes, &route_hash_ops, route);
-                if (r < 0)
-                        return r;
+        routes = new0(Route*, n);
+        if (!routes)
+                return -ENOMEM;
 
-                set_remove(link ? link->routes_foreign : manager->routes_foreign, route);
-        } else if (r == 1) {
-                /* Route exists, do nothing */
-                ;
-        } else
-                return r;
+        links = new0(Link*, n);
+        if (!links)
+                return -ENOMEM;
 
-        if (ret)
-                *ret = route;
-        return 0;
-}
+        c = new(ConvertedRoutes, 1);
+        if (!c)
+                return -ENOMEM;
 
-static bool route_type_is_reject(const Route *route) {
-        assert(route);
+        *c = (ConvertedRoutes) {
+                .n = n,
+                .routes = TAKE_PTR(routes),
+                .links = TAKE_PTR(links),
+        };
 
-        return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
+        *ret = TAKE_PTR(c);
+        return 0;
 }
 
-static int link_has_route_one(Link *link, const Route *route, const NextHop *nh, uint8_t nh_weight) {
-        _cleanup_(route_freep) Route *tmp = NULL;
+static int route_convert(Manager *manager, const Route *route, ConvertedRoutes **ret) {
+        _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
         int r;
 
-        assert(link);
+        assert(manager);
         assert(route);
-        assert(nh);
+        assert(ret);
 
-        r = route_new(&tmp);
-        if (r < 0)
-                return r;
+        if (!route_needs_convert(route)) {
+                *ret = NULL;
+                return 0;
+        }
 
-        route_copy(tmp, route, NULL, nh, nh_weight);
+        if (route->nexthop_id > 0) {
+                struct nexthop_grp *nhg;
+                NextHop *nh;
 
-        if (route_type_is_reject(route) || (nh && nh->blackhole))
-                return route_get(link->manager, NULL, tmp, NULL) >= 0;
-        else
-                return route_get(NULL, link, tmp, NULL) >= 0;
-}
+                r = manager_get_nexthop_by_id(manager, route->nexthop_id, &nh);
+                if (r < 0)
+                        return r;
 
-int link_has_route(Link *link, const Route *route) {
-        MultipathRoute *m;
-        int r;
+                if (hashmap_isempty(nh->group)) {
+                        r = converted_routes_new(1, &c);
+                        if (r < 0)
+                                return r;
 
-        assert(link);
-        assert(route);
+                        r = route_dup(route, &c->routes[0]);
+                        if (r < 0)
+                                return r;
 
-        if (route->nexthop_id > 0) {
-                struct nexthop_grp *nhg;
-                NextHop *nh;
+                        route_apply_nexthop(c->routes[0], nh, UINT8_MAX);
+                        c->links[0] = nh->link;
 
-                if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
-                        return false;
+                        *ret = TAKE_PTR(c);
+                        return 1;
+                }
 
-                if (hashmap_isempty(nh->group))
-                        return link_has_route_one(link, route, nh, UINT8_MAX);
+                r = converted_routes_new(hashmap_size(nh->group), &c);
+                if (r < 0)
+                        return r;
 
+                size_t i = 0;
                 HASHMAP_FOREACH(nhg, nh->group) {
                         NextHop *h;
 
-                        if (manager_get_nexthop_by_id(link->manager, nhg->id, &h) < 0)
-                                return false;
+                        r = manager_get_nexthop_by_id(manager, nhg->id, &h);
+                        if (r < 0)
+                                return r;
 
-                        r = link_has_route_one(link, route, h, nhg->weight);
-                        if (r <= 0)
+                        r = route_dup(route, &c->routes[i]);
+                        if (r < 0)
                                 return r;
+
+                        route_apply_nexthop(c->routes[i], h, nhg->weight);
+                        c->links[i] = h->link;
+
+                        i++;
                 }
 
-                return true;
-        }
+                *ret = TAKE_PTR(c);
+                return 1;
 
-        if (ordered_set_isempty(route->multipath_routes)) {
-                if (route_type_is_reject(route))
-                        return route_get(link->manager, NULL, route, NULL) >= 0;
-                else
-                        return route_get(NULL, link, route, NULL) >= 0;
         }
 
-        ORDERED_SET_FOREACH(m, route->multipath_routes) {
-                _cleanup_(route_freep) Route *tmp = NULL;
-                Link *l;
-
-                if (m->ifname) {
-                        if (link_get_by_name(link->manager, m->ifname, &l) < 0)
-                                return false;
+        assert(!ordered_set_isempty(route->multipath_routes));
 
-                        m->ifindex = l->ifindex;
-                } else
-                        l = link;
+        r = converted_routes_new(ordered_set_size(route->multipath_routes), &c);
+        if (r < 0)
+                return r;
 
-                r = route_new(&tmp);
+        size_t i = 0;
+        MultipathRoute *m;
+        ORDERED_SET_FOREACH(m, route->multipath_routes) {
+                r = route_dup(route, &c->routes[i]);
                 if (r < 0)
                         return r;
 
-                route_copy(tmp, route, m, NULL, UINT8_MAX);
+                route_apply_multipath_route(c->routes[i], m);
 
-                if (route_get(NULL, l, tmp, NULL) < 0)
-                        return false;
+                r = multipath_route_get_link(manager, m, &c->links[i]);
+                if (r < 0)
+                        return r;
+
+                i++;
         }
 
-        return true;
+        *ret = TAKE_PTR(c);
+        return 1;
 }
 
-static bool route_address_is_reachable(const Route *route, int family, const union in_addr_union *address) {
-        assert(route);
-        assert(IN_SET(family, AF_INET, AF_INET6));
-        assert(address);
-
-        if (route->family != family)
-                return false;
-
-        if (!in_addr_is_set(route->family, &route->dst))
-                return false;
+void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router) {
+        Route *route;
 
-        return in_addr_prefix_intersect(
-                        route->family,
-                        &route->dst,
-                        route->dst_prefixlen,
-                        address,
-                        FAMILY_ADDRESS_SIZE(family) * 8) > 0;
-}
+        assert(link);
 
-static bool prefix_route_address_is_reachable(const Address *a, int family, const union in_addr_union *address) {
-        assert(a);
-        assert(IN_SET(family, AF_INET, AF_INET6));
-        assert(address);
+        SET_FOREACH(route, link->routes) {
+                if (route->source != source)
+                        continue;
 
-        if (a->family != family)
-                return false;
-        if (!address_is_ready(a))
-                return false;
-        if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
-                return false;
-        if (in_addr_is_set(a->family, &a->in_addr_peer))
-                return false;
+                if (source == NETWORK_CONFIG_SOURCE_NDISC &&
+                    router && !in6_addr_equal(router, &route->provider.in6))
+                        continue;
 
-        return in_addr_prefix_intersect(
-                        family,
-                        &a->in_addr,
-                        a->prefixlen,
-                        address,
-                        FAMILY_ADDRESS_SIZE(family) * 8) > 0;
+                route_mark(route);
+        }
 }
 
 static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) {
         Route *route;
+        Address *a;
 
         assert(link);
         assert(link->manager);
         assert(IN_SET(family, AF_INET, AF_INET6));
         assert(address);
 
-
-        SET_FOREACH(route, link->routes)
-                if (route_address_is_reachable(route, family, address))
-                        return true;
-        SET_FOREACH(route, link->routes_foreign)
-                if (route_address_is_reachable(route, family, address))
+        SET_FOREACH(route, link->routes) {
+                if (!route_exists(route))
+                        continue;
+                if (route->family != family)
+                        continue;
+                if (!in_addr_is_set(route->family, &route->dst))
+                        continue;
+                if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) > 0)
                         return true;
+        }
+
+        if (link->manager->manage_foreign_routes)
+                return false;
 
         /* If we do not manage foreign routes, then there may exist a prefix route we do not know,
          * which was created on configuring an address. Hence, also check the addresses. */
-        if (!link->manager->manage_foreign_routes) {
-                Address *a;
-
-                SET_FOREACH(a, link->addresses)
-                        if (prefix_route_address_is_reachable(a, family, address))
-                                return true;
-                SET_FOREACH(a, link->addresses_foreign)
-                        if (prefix_route_address_is_reachable(a, family, address))
-                                return true;
+        SET_FOREACH(a, link->addresses) {
+                if (!address_is_ready(a))
+                        continue;
+                if (a->family != family)
+                        continue;
+                if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
+                        continue;
+                if (in_addr_is_set(a->family, &a->in_addr_peer))
+                        continue;
+                if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, address) > 0)
+                        return true;
         }
 
         return false;
 }
 
-static Route *routes_get_default_gateway(Set *routes, int family, Route *gw) {
+static Route *link_find_default_gateway(Link *link, int family, Route *gw) {
         Route *route;
 
-        SET_FOREACH(route, routes) {
+        assert(link);
+
+        SET_FOREACH(route, link->routes) {
+                if (!route_exists(route))
+                        continue;
                 if (family != AF_UNSPEC && route->family != family)
                         continue;
                 if (route->dst_prefixlen != 0)
@@ -826,20 +786,22 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) {
                 if (link->state != LINK_STATE_CONFIGURED)
                         continue;
 
-                gw = routes_get_default_gateway(link->routes, family, gw);
-                gw = routes_get_default_gateway(link->routes_foreign, family, gw);
+                gw = link_find_default_gateway(link, family, gw);
         }
 
         if (!gw)
                 return -ENOENT;
 
-        assert(gw->link);
-        *ret = gw->link;
+        if (ret) {
+                assert(gw->link);
+                *ret = gw->link;
+        }
+
         return 0;
 }
 
 static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) {
-        _cleanup_free_ char *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL,
+        _cleanup_free_ char *state = NULL, *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL,
                 *table = NULL, *scope = NULL, *proto = NULL;
         const char *gw = NULL;
 
@@ -852,6 +814,7 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
         if (!DEBUG_LOGGING)
                 return;
 
+        (void) network_config_state_to_string_alloc(route->state, &state);
         if (in_addr_is_set(route->family, &route->dst))
                 (void) in_addr_prefix_to_string(route->family, &route->dst, route->dst_prefixlen, &dst);
         if (in_addr_is_set(route->family, &route->src))
@@ -889,8 +852,10 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
         (void) route_protocol_full_to_string_alloc(route->protocol, &proto);
 
         log_link_debug(link,
-                       "%s route: dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32,
-                       str, strna(dst), strna(src), strna(gw), strna(prefsrc),
+                       "%s %s route (%s): dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, "
+                       "proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32,
+                       str, strna(network_config_source_to_string(route->source)), strna(state),
+                       strna(dst), strna(src), strna(gw), strna(prefsrc),
                        strna(scope), strna(table), strna(proto),
                        strna(route_type_to_string(route->type)),
                        route->nexthop_id, route->priority);
@@ -1002,16 +967,14 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req
         return 0;
 }
 
-static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(m);
-        assert(link);
-        assert(link->route_remove_messages > 0);
 
-        link->route_remove_messages--;
+        /* link may be NULL. */
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+        if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 0;
 
         r = sd_netlink_message_get_errno(m);
@@ -1021,32 +984,19 @@ static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Li
         return 1;
 }
 
-static int manager_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) {
-        int r;
-
-        assert(m);
-        assert(manager);
-        assert(manager->route_remove_messages > 0);
-
-        manager->route_remove_messages--;
-
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -ESRCH)
-                log_message_warning_errno(m, r, "Could not drop route, ignoring");
-
-        return 1;
-}
-
-int route_remove(const Route *route, Manager *manager, Link *link) {
+int route_remove(Route *route) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         unsigned char type;
+        Manager *manager;
+        Link *link;
         int r;
 
-        assert(link || manager);
+        assert(route);
+        assert(route->manager || (route->link && route->link->manager));
         assert(IN_SET(route->family, AF_INET, AF_INET6));
 
-        if (!manager)
-                manager = link->manager;
+        link = route->link;
+        manager = route->manager ?: link->manager;
 
         log_route_debug(route, "Removing", link, manager);
 
@@ -1075,100 +1025,85 @@ int route_remove(const Route *route, Manager *manager, Link *link) {
         if (r < 0)
                 return r;
 
-        if (link) {
-                r = netlink_call_async(manager->rtnl, NULL, req,
-                                       link_route_remove_handler,
-                                       link_netlink_destroy_callback, link);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
-                link_ref(link);
-                link->route_remove_messages++;
-        } else {
-                r = netlink_call_async(manager->rtnl, NULL, req,
-                                       manager_route_remove_handler,
-                                       NULL, manager);
-                if (r < 0)
-                        return log_error_errno(r, "Could not send rtnetlink message: %m");
+        r = netlink_call_async(manager->rtnl, NULL, req, route_remove_handler,
+                               link ? link_netlink_destroy_callback : NULL, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
 
-                manager->route_remove_messages++;
-        }
+        link_ref(link);
 
+        route_enter_removing(route);
         return 0;
 }
 
-static bool link_has_static_route(const Link *link, const Route *route) {
-        Route *net_route;
-
-        assert(link);
-        assert(route);
+static int manager_drop_routes(Manager *manager, bool foreign, const Link *except) {
+        Route *route;
+        Link *link;
+        int k, r;
 
-        if (!link->network)
-                return false;
+        assert(manager);
 
-        HASHMAP_FOREACH(net_route, link->network->routes_by_section)
-                if (route_equal(net_route, route))
-                        return true;
+        /* First, mark all routes. */
+        SET_FOREACH(route, manager->routes) {
+                /* Do not touch routes managed by the kernel. */
+                if (route->protocol == RTPROT_KERNEL)
+                        continue;
 
-        return false;
-}
+                /* When 'foreign' is true, do not remove routes we configured. */
+                if (foreign && route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
+                        continue;
 
-static bool links_have_static_route(const Manager *manager, const Route *route, const Link *except) {
-        Link *link;
+                /* Ignore routes not assigned yet or already removed. */
+                if (!route_exists(route))
+                        continue;
 
-        assert(manager);
+                route_mark(route);
+        }
 
+        /* Then, unmark all routes requested by active links. */
         HASHMAP_FOREACH(link, manager->links_by_index) {
                 if (link == except)
                         continue;
 
-                if (link_has_static_route(link, route))
-                        return true;
-        }
-
-        return false;
-}
+                if (!link->network)
+                        continue;
 
-static int manager_drop_routes_internal(Manager *manager, bool foreign, const Link *except) {
-        Route *route;
-        int k, r = 0;
-        Set *routes;
+                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                        continue;
 
-        assert(manager);
+                HASHMAP_FOREACH(route, link->network->routes_by_section) {
+                        _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
+                        Route *existing;
 
-        routes = foreign ? manager->routes_foreign : manager->routes;
-        SET_FOREACH(route, routes) {
-                if (route->removing)
-                        continue;
+                        r = route_convert(manager, route, &converted);
+                        if (r < 0)
+                                continue;
+                        if (r == 0) {
+                                if (route_get(manager, NULL, route, &existing) >= 0)
+                                        route_unmark(existing);
+                                continue;
+                        }
 
-                /* Do not touch routes managed by the kernel. */
-                if (route->protocol == RTPROT_KERNEL)
-                        continue;
+                        for (size_t i = 0; i < converted->n; i++)
+                                if (route_get(manager, NULL, converted->routes[i], &existing) >= 0)
+                                        route_unmark(existing);
+                }
+        }
 
-                /* The route will be configured later, or already configured by a link. */
-                if (links_have_static_route(manager, route, except))
+        /* Finally, remove all marked routes. */
+        r = 0;
+        SET_FOREACH(route, manager->routes) {
+                if (!route_is_marked(route))
                         continue;
 
-                /* The existing links do not have the route. Let's drop this now. It may be
-                 * re-configured later. */
-                k = route_remove(route, manager, NULL);
+                k = route_remove(route);
                 if (k < 0 && r >= 0)
                         r = k;
-
-                route->removing = true;
         }
 
         return r;
 }
 
-static int manager_drop_foreign_routes(Manager *manager) {
-        return manager_drop_routes_internal(manager, true, NULL);
-}
-
-static int manager_drop_routes(Manager *manager, const Link *except) {
-        return manager_drop_routes_internal(manager, false, except);
-}
-
 static bool route_by_kernel(const Route *route) {
         assert(route);
 
@@ -1189,16 +1124,24 @@ static bool route_by_kernel(const Route *route) {
 
 int link_drop_foreign_routes(Link *link) {
         Route *route;
-        int k, r = 0;
+        int k, r;
 
         assert(link);
         assert(link->manager);
 
-        SET_FOREACH(route, link->routes_foreign) {
+        SET_FOREACH(route, link->routes) {
                 /* do not touch routes managed by the kernel */
                 if (route_by_kernel(route))
                         continue;
 
+                /* Do not remove routes we configured. */
+                if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
+                        continue;
+
+                /* Ignore routes not assigned yet or already removed. */
+                if (!route_exists(route))
+                        continue;
+
                 if (route->protocol == RTPROT_STATIC && link->network &&
                     FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
                         continue;
@@ -1207,15 +1150,38 @@ int link_drop_foreign_routes(Link *link) {
                     FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
                         continue;
 
-                if (link_has_static_route(link, route))
-                        k = route_add(NULL, link, route, NULL, NULL, UINT8_MAX, NULL);
-                else
-                        k = route_remove(route, NULL, link);
+                route_mark(route);
+        }
+
+        HASHMAP_FOREACH(route, link->network->routes_by_section) {
+                _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
+                Route *existing;
+
+                r = route_convert(link->manager, route, &converted);
+                if (r < 0)
+                        continue;
+                if (r == 0) {
+                        if (route_get(NULL, link, route, &existing) >= 0)
+                                route_unmark(existing);
+                        continue;
+                }
+
+                for (size_t i = 0; i < converted->n; i++)
+                        if (route_get(NULL, link, converted->routes[i], &existing) >= 0)
+                                route_unmark(existing);
+        }
+
+        r = 0;
+        SET_FOREACH(route, link->routes) {
+                if (!route_is_marked(route))
+                        continue;
+
+                k = route_remove(route);
                 if (k < 0 && r >= 0)
                         r = k;
         }
 
-        k = manager_drop_foreign_routes(link->manager);
+        k = manager_drop_routes(link->manager, /* foreign = */ true, NULL);
         if (k < 0 && r >= 0)
                 r = k;
 
@@ -1230,15 +1196,18 @@ int link_drop_routes(Link *link) {
 
         SET_FOREACH(route, link->routes) {
                 /* do not touch routes managed by the kernel */
-                if (route->protocol == RTPROT_KERNEL)
+                if (route_by_kernel(route))
+                        continue;
+
+                if (!route_exists(route))
                         continue;
 
-                k = route_remove(route, NULL, link);
+                k = route_remove(route);
                 if (k < 0 && r >= 0)
                         r = k;
         }
 
-        k = manager_drop_routes(link->manager, link);
+        k = manager_drop_routes(link->manager, /* foreign = */ false, link);
         if (k < 0 && r >= 0)
                 r = k;
 
@@ -1250,131 +1219,37 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
         int r;
 
         assert(route);
+        assert(route->link);
 
-        r = route_remove(route, route->manager, route->link);
+        r = route_remove(route);
         if (r < 0) {
                 log_link_warning_errno(route->link, r, "Could not remove route: %m");
-                route_free(route);
+                link_enter_failed(route->link);
         }
 
         return 1;
 }
 
-static int route_add_and_setup_timer_one(Link *link, const Route *route, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) {
-        _cleanup_(sd_event_source_disable_unrefp) sd_event_source *expire = NULL;
-        Route *nr;
-        int r;
-
-        assert(link);
-        assert(link->manager);
-        assert(route);
-        assert(!(m && nh));
-        assert(ret);
-
-        if (route_type_is_reject(route) || (nh && nh->blackhole))
-                r = route_add(link->manager, NULL, route, NULL, nh, nh_weight, &nr);
-        else if (nh) {
-                assert(nh->link);
-                assert(hashmap_isempty(nh->group));
-
-                r = route_add(NULL, nh->link, route, NULL, nh, nh_weight, &nr);
-        } else if (m && m->ifindex != 0 && m->ifindex != link->ifindex) {
-                Link *link_gw;
-
-                r = link_get_by_index(link->manager, m->ifindex, &link_gw);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
-
-                r = route_add(NULL, link_gw, route, m, NULL, UINT8_MAX, &nr);
-        } else
-                r = route_add(NULL, link, route, m, NULL, UINT8_MAX, &nr);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not add route: %m");
+static int route_setup_timer(Route *route) {
+        Manager *manager;
 
         /* TODO: drop expiration handling once it can be pushed into the kernel */
-        if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
-                r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
-                                      nr->lifetime, 0, route_expire_handler, nr);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
-        }
-
-        sd_event_source_disable_unref(nr->expire);
-        nr->expire = TAKE_PTR(expire);
-
-        *ret = nr;
-        return 0;
-}
 
-static int route_add_and_setup_timer(Link *link, const Route *route, unsigned *ret_n_routes, Route ***ret_routes) {
-        _cleanup_free_ Route **routes = NULL;
-        unsigned n_routes;
-        NextHop *nh;
-        Route **p;
-        int r;
-
-        assert(link);
         assert(route);
-        assert(ret_n_routes);
-        assert(ret_routes);
+        assert(route->manager || (route->link && route->link->manager));
 
-        if (route->nexthop_id > 0) {
-                r = manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not get nexthop by ID %"PRIu32": %m", route->nexthop_id);
-        } else
-                nh = NULL;
+        manager = route->manager ?: route->link->manager;
 
-        if (nh && !hashmap_isempty(nh->group)) {
-                struct nexthop_grp *nhg;
-
-                n_routes = hashmap_size(nh->group);
-                p = routes = new(Route*, n_routes);
-                if (!routes)
-                        return log_oom();
-
-                HASHMAP_FOREACH(nhg, nh->group) {
-                        NextHop *h;
-
-                        r = manager_get_nexthop_by_id(link->manager, nhg->id, &h);
-                        if (r < 0)
-                                return log_link_error_errno(link, r, "Could not get nexthop group member by ID %"PRIu32": %m", nhg->id);
-
-                        /* The nexthop h may be a blackhole nexthop. In that case, h->link is NULL. */
-                        r = route_add_and_setup_timer_one(h->link ?: link, route, NULL, h, nhg->weight, p++);
-                        if (r < 0)
-                                return r;
-                }
-        } else if (!ordered_set_isempty(route->multipath_routes)) {
-                MultipathRoute *m;
-
-                assert(!nh);
-                assert(!in_addr_is_set(route->gw_family, &route->gw));
-
-                n_routes = ordered_set_size(route->multipath_routes);
-                p = routes = new(Route*, n_routes);
-                if (!routes)
-                        return log_oom();
+        if (route->lifetime == USEC_INFINITY)
+                return 0;
 
-                ORDERED_SET_FOREACH(m, route->multipath_routes) {
-                        r = route_add_and_setup_timer_one(link, route, m, NULL, UINT8_MAX, p++);
-                        if (r < 0)
-                                return r;
-                }
-        } else {
-                n_routes = 1;
-                routes = new(Route*, n_routes);
-                if (!routes)
-                        return log_oom();
+        if (kernel_route_expiration_supported())
+                return 0;
 
-                r = route_add_and_setup_timer_one(link, route, NULL, nh, UINT8_MAX, routes);
-                if (r < 0)
-                        return r;
-        }
+        sd_event_source_disable_unref(route->expire);
 
-        *ret_n_routes = n_routes;
-        *ret_routes = TAKE_PTR(routes);
-        return 0;
+        return sd_event_add_time(manager->event, &route->expire, clock_boottime_or_monotonic(),
+                                 route->lifetime, 0, route_expire_handler, route);
 }
 
 static int append_nexthop_one(const Link *link, const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
@@ -1485,27 +1360,18 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li
 static int route_configure(
                 const Route *route,
                 Link *link,
-                link_netlink_message_handler_t callback,
-                unsigned *ret_n_routes,
-                Route ***ret_routes) {
+                link_netlink_message_handler_t callback) {
 
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
-        _cleanup_free_ Route **routes = NULL;
-        unsigned n_routes = 0;  /* avoid false maybe-uninitialized warning */
         int r;
 
+        assert(route);
+        assert(IN_SET(route->family, AF_INET, AF_INET6));
         assert(link);
         assert(link->manager);
         assert(link->manager->rtnl);
         assert(link->ifindex > 0);
-        assert(IN_SET(route->family, AF_INET, AF_INET6));
         assert(callback);
-        assert(!!ret_n_routes == !!ret_routes);
-
-        if (route_get(link->manager, link, route, NULL) <= 0 &&
-            set_size(link->routes) >= routes_max())
-                return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
-                                            "Too many routes are configured, refusing: %m");
 
         log_route_debug(route, "Configuring", link, link->manager);
 
@@ -1589,23 +1455,34 @@ static int route_configure(
                         return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
         }
 
-        r = route_add_and_setup_timer(link, route, &n_routes, &routes);
-        if (r < 0)
-                return r;
-
         r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
                                link_netlink_destroy_callback, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
 
         link_ref(link);
+        return 0;
+}
 
-        if (ret_routes) {
-                *ret_n_routes = n_routes;
-                *ret_routes = TAKE_PTR(routes);
-        }
+void route_cancel_request(Route *route) {
+        Request req;
 
-        return r;
+        assert(route);
+
+        if (!route_is_requesting(route))
+                return;
+
+        if (!route->link)
+                return;
+
+        req = (Request) {
+                .link = route->link,
+                .type = REQUEST_TYPE_ROUTE,
+                .route = route,
+        };
+
+        request_drop(ordered_set_get(route->link->manager->request_queue, &req));
+        route_cancel_requesting(route);
 }
 
 static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -1637,13 +1514,60 @@ int link_request_route(
                 link_netlink_message_handler_t netlink_handler,
                 Request **ret) {
 
+        Route *existing;
+        int r;
+
         assert(link);
         assert(link->manager);
         assert(route);
+        assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
+        assert(!route_needs_convert(route));
+
+        if (route_get(link->manager, link, route, &existing) < 0) {
+                _cleanup_(route_freep) Route *tmp = NULL;
+
+                if (consume_object)
+                        tmp = route;
+                else {
+                        r = route_dup(route, &tmp);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = route_add(link->manager, link, tmp);
+                if (r < 0)
+                        return r;
+
+                existing = TAKE_PTR(tmp);
+        } else {
+                existing->source = route->source;
+                existing->provider = route->provider;
+                if (consume_object)
+                        route_free(route);
+        }
+
+        log_route_debug(existing, "Requesting", link, link->manager);
+        r = link_queue_request(link, REQUEST_TYPE_ROUTE, existing, false,
+                               message_counter, netlink_handler, ret);
+        if (r <= 0)
+                return r;
+
+        route_enter_requesting(existing);
+        return 1;
+}
+
+static int link_request_static_route(Link *link, Route *route) {
+        assert(link);
+        assert(link->manager);
+        assert(route);
+
+        if (!route_needs_convert(route))
+                return link_request_route(link, route, false, &link->static_route_messages,
+                                          static_route_handler, NULL);
 
         log_route_debug(route, "Requesting", link, link->manager);
-        return link_queue_request(link, REQUEST_TYPE_ROUTE, route, consume_object,
-                                  message_counter, netlink_handler, ret);
+        return link_queue_request(link, REQUEST_TYPE_ROUTE, route, false,
+                                  &link->static_route_messages, static_route_handler, NULL);
 }
 
 int link_request_static_routes(Link *link, bool only_ipv4) {
@@ -1662,8 +1586,7 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
                 if (only_ipv4 && route->family != AF_INET)
                         continue;
 
-                r = link_request_route(link, route, false, &link->static_route_messages,
-                                       static_route_handler, NULL);
+                r = link_request_static_route(link, route);
                 if (r < 0)
                         return r;
         }
@@ -1696,15 +1619,20 @@ bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_un
 }
 
 static int route_is_ready_to_configure(const Route *route, Link *link) {
-        MultipathRoute *m;
-        NextHop *nh = NULL;
         int r;
 
         assert(route);
         assert(link);
 
+        if (!link_is_ready_to_configure(link, false))
+                return false;
+
+        if (set_size(link->routes) >= routes_max())
+                return false;
+
         if (route->nexthop_id > 0) {
                 struct nexthop_grp *nhg;
+                NextHop *nh;
 
                 if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
                         return false;
@@ -1723,20 +1651,6 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
                 }
         }
 
-        if (route_type_is_reject(route)) {
-                if (link->manager->route_remove_messages > 0)
-                        return false;
-        } else {
-                Link *l;
-
-                HASHMAP_FOREACH(l, link->manager->links_by_index) {
-                        if (l->address_remove_messages > 0)
-                                return false;
-                        if (l->route_remove_messages > 0)
-                                return false;
-                }
-        }
-
         if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
                 r = manager_has_address(link->manager, route->family, &route->prefsrc, route->family == AF_INET6);
                 if (r <= 0)
@@ -1746,21 +1660,20 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
         if (!gateway_is_ready(link, route->gateway_onlink, route->gw_family, &route->gw))
                 return false;
 
+        MultipathRoute *m;
         ORDERED_SET_FOREACH(m, route->multipath_routes) {
                 union in_addr_union a = m->gateway.address;
                 Link *l = NULL;
 
-                if (m->ifname) {
-                        if (link_get_by_name(link->manager, m->ifname, &l) < 0)
+                r = multipath_route_get_link(link->manager, m, &l);
+                if (r < 0)
+                        return false;
+                if (r > 0) {
+                        if (!link_is_ready_to_configure(l, true))
                                 return false;
 
                         m->ifindex = l->ifindex;
-                } else if (m->ifindex > 0) {
-                        if (link_get_by_index(link->manager, m->ifindex, &l) < 0)
-                                return false;
                 }
-                if (l && !link_is_ready_to_configure(l, true))
-                        return false;
 
                 if (!gateway_is_ready(l ?: link, route->gateway_onlink, m->gateway.family, &a))
                         return false;
@@ -1770,8 +1683,9 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
 }
 
 int request_process_route(Request *req) {
-        _cleanup_free_ Route **routes = NULL;
-        unsigned n_routes;
+        _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
+        Route *route;
+        Link *link;
         int r;
 
         assert(req);
@@ -1779,113 +1693,116 @@ int request_process_route(Request *req) {
         assert(req->route);
         assert(req->type == REQUEST_TYPE_ROUTE);
 
-        if (!link_is_ready_to_configure(req->link, false))
+        link = req->link;
+        route = req->route;
+
+        r = route_is_ready_to_configure(route, link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m");
+        if (r == 0)
                 return 0;
 
-        r = route_is_ready_to_configure(req->route, req->link);
-        if (r <= 0)
-                return r;
+        if (route_needs_convert(route)) {
+                r = route_convert(link->manager, route, &converted);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to convert route: %m");
 
-        r = route_configure(req->route, req->link, req->netlink_handler,
-                            req->after_configure ? &n_routes : NULL,
-                            req->after_configure ? &routes : NULL);
-        if (r < 0)
-                return r;
+                assert(r > 0);
+                assert(converted);
 
-        /* To prevent a double decrement on failure in after_configure(). */
-        req->message_counter = NULL;
+                for (size_t i = 0; i < converted->n; i++) {
+                        Route *existing;
 
-        if (req->after_configure) {
-                assert(n_routes > 0);
+                        if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) {
+                                _cleanup_(route_freep) Route *tmp = NULL;
 
-                for (unsigned i = 0; i < n_routes; i++) {
-                        r = req->after_configure(req, routes[i]);
-                        if (r < 0)
-                                return r;
+                                r = route_dup(converted->routes[i], &tmp);
+                                if (r < 0)
+                                        return log_oom();
+
+                                r = route_add(link->manager, converted->links[i] ?: link, tmp);
+                                if (r < 0)
+                                        return log_link_warning_errno(link, r, "Failed to add route: %m");
+
+                                TAKE_PTR(tmp);
+                        } else {
+                                existing->source = converted->routes[i]->source;
+                                existing->provider = converted->routes[i]->provider;
+                        }
                 }
+        } else {
+                r = route_setup_timer(route);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to setup timer for route: %m");
         }
 
+        r = route_configure(route, link, req->netlink_handler);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to configure route: %m");
+
+        if (converted)
+                for (size_t i = 0; i < converted->n; i++) {
+                        Route *existing;
+
+                        assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0);
+                        route_enter_configuring(existing);
+                }
+        else
+                route_enter_configuring(route);
+
         return 1;
 }
 
-static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
-        _cleanup_(route_freep) Route *nr = NULL;
+static int process_route_one(Manager *manager, Link *link, uint16_t type, Route *in) {
+        _cleanup_(route_freep) Route *tmp = in;
         Route *route = NULL;
-        NextHop *nh = NULL;
         int r;
 
         assert(manager);
         assert(tmp);
         assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
 
-        (void) manager_get_nexthop_by_id(manager, tmp->nexthop_id, &nh);
-
-        if (nh && hashmap_isempty(nh->group)) {
-                if (link && nh->link && link != nh->link)
-                        return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
-                                                      "rtnl: received RTA_OIF and ifindex of nexthop corresponding to RTA_NH_ID do not match, ignoring.");
-
-                if (nh->link)
-                        link = nh->link;
-
-                r = route_new(&nr);
-                if (r < 0)
-                        return log_oom();
-
-                route_copy(nr, tmp, NULL, nh, UINT8_MAX);
-
-                tmp = nr;
-        } else if (m) {
-                if (link)
-                        return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
-                                                "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
-
-                if (m->ifindex <= 0)
-                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                 "rtnl: received multipath route with invalid ifindex, ignoring.");
-
-                r = link_get_by_index(manager, m->ifindex, &link);
-                if (r < 0) {
-                        log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex);
-                        return 0;
-                }
-
-                r = route_new(&nr);
-                if (r < 0)
-                        return log_oom();
-
-                route_copy(nr, tmp, m, NULL, UINT8_MAX);
-
-                tmp = nr;
-        }
+        /* link may be NULL. This consumes 'in'. */
 
         (void) route_get(manager, link, tmp, &route);
 
         switch (type) {
         case RTM_NEWROUTE:
-                if (!route) {
-                        if (!manager->manage_foreign_routes)
-                                log_route_debug(tmp, "Ignoring received foreign", link, manager);
-                        else {
-                                /* A route appeared that we did not request */
-                                log_route_debug(tmp, "Remembering foreign", link, manager);
-                                r = route_add_foreign(manager, link, tmp, NULL);
-                                if (r < 0) {
-                                        log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
-                                        return 0;
-                                }
+                if (route) {
+                        route_enter_configured(route);
+                        log_route_debug(route, "Received remembered", link, manager);
+
+                } else if (!manager->manage_foreign_routes) {
+                        route_enter_configured(tmp);
+                        log_route_debug(tmp, "Ignoring received", link, manager);
+
+                } else {
+                        /* A route appeared that we did not request */
+                        route_enter_configured(tmp);
+                        log_route_debug(tmp, "Received new", link, manager);
+                        r = route_add(manager, link, tmp);
+                        if (r < 0) {
+                                log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
+                                return 0;
                         }
-                } else
-                        log_route_debug(tmp, "Received remembered", link, manager);
+                        TAKE_PTR(tmp);
+                }
 
                 break;
 
         case RTM_DELROUTE:
-                log_route_debug(tmp,
-                                route ? "Forgetting" :
-                                manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received foreign",
-                                link, manager);
-                route_free(route);
+                if (route) {
+                        route_enter_removed(route);
+                        if (route->state == 0) {
+                                log_route_debug(route, "Forgetting", link, manager);
+                                route_free(route);
+                        } else
+                                log_route_debug(route, "Removed", link, manager);
+                } else
+                        log_route_debug(tmp,
+                                        manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
+                                        link, manager);
+
                 break;
 
         default:
@@ -1896,7 +1813,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const
 }
 
 int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
-        _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
+        _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
         _cleanup_(route_freep) Route *tmp = NULL;
         _cleanup_free_ void *rta_multipath = NULL;
         Link *link = NULL;
@@ -2088,7 +2005,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
                 return 0;
         } else if (r >= 0) {
-                r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &multipath_routes);
+                r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &tmp->multipath_routes);
                 if (r < 0) {
                         log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
                         return 0;
@@ -2101,18 +2018,21 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
         if (route_type_is_reject(tmp))
                 link = NULL;
 
-        if (ordered_set_isempty(multipath_routes))
-                (void) process_route_one(m, link, type, tmp, NULL);
-        else {
-                MultipathRoute *mr;
+        if (!route_needs_convert(tmp))
+                return process_route_one(m, link, type, TAKE_PTR(tmp));
 
-                ORDERED_SET_FOREACH(mr, multipath_routes) {
-                        r = process_route_one(m, link, type, tmp, mr);
-                        if (r < 0)
-                                break;
-                }
+        r = route_convert(m, tmp, &converted);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: failed to convert received route, ignoring: %m");
+                return 0;
         }
 
+        assert(r > 0);
+        assert(converted);
+
+        for (size_t i = 0; i < converted->n; i++)
+                (void) process_route_one(m, converted->links[i] ?: link, type, TAKE_PTR(converted->routes[i]));
+
         return 1;
 }
 
@@ -3094,6 +3014,9 @@ static int route_section_verify(Route *route, Network *network) {
         if (section_is_invalid(route->section))
                 return -EINVAL;
 
+        /* Currently, we do not support static route with finite lifetime. */
+        assert(route->lifetime == USEC_INFINITY);
+
         if (route->gateway_from_dhcp_or_ra) {
                 if (route->gw_family == AF_UNSPEC) {
                         /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
index 2e5a073a2b0eb2e85f8896e8b975f3d7c21d9311..d3acdfa2e7448484ee63ba22e10d791fcb013467 100644 (file)
@@ -16,11 +16,13 @@ typedef struct Network Network;
 typedef struct Request Request;
 
 typedef struct Route {
-        Network *network;
-        NetworkConfigSection *section;
-
         Link *link;
         Manager *manager;
+        Network *network;
+        NetworkConfigSection *section;
+        NetworkConfigSource source;
+        NetworkConfigState state;
+        union in_addr_union provider; /* DHCP server or router address */
 
         int family;
         int gw_family;
@@ -52,7 +54,6 @@ typedef struct Route {
         bool protocol_set:1;
         bool pref_set:1;
         bool gateway_from_dhcp_or_ra:1;
-        bool removing:1;
 
         union in_addr_union gw;
         union in_addr_union dst;
@@ -66,7 +67,6 @@ typedef struct Route {
 
 void route_hash_func(const Route *route, struct siphash *state);
 int route_compare_func(const Route *a, const Route *b);
-bool route_equal(const Route *r1, const Route *r2);
 extern const struct hash_ops route_hash_ops;
 
 int route_new(Route **ret);
@@ -75,15 +75,16 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
 int route_dup(const Route *src, Route **ret);
 
 int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
-int route_remove(const Route *route, Manager *manager, Link *link);
+int route_remove(Route *route);
 
-int link_has_route(Link *link, const Route *route);
+int route_get(Manager *manager, Link *link, const Route *in, Route **ret);
 int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret);
 bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw);
 
 int link_drop_routes(Link *link);
 int link_drop_foreign_routes(Link *link);
 
+void route_cancel_request(Route *route);
 int link_request_route(
                 Link *link,
                 Route *route,
@@ -103,6 +104,9 @@ void network_drop_invalid_routes(Network *network);
 int manager_get_route_table_from_string(const Manager *m, const char *table, uint32_t *ret);
 int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret);
 
+DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Route, route);
+void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_gateway);
 CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src);
 CONFIG_PARSER_PROTOTYPE(config_parse_destination);
index 21e20f10f5958a9bc6a47744d509588c06b22294..54243e7968672b5105bc50876813c57417577c9e 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "missing_network.h"
 #include "netlink-util.h"
+#include "networkd-address.h"
 #include "networkd-can.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
index d04a7e35e8efafe8659caea66a7f274551902ae9..49a332a7f9f51a069b5372a62cede29f3fec9379 100644 (file)
@@ -10,7 +10,9 @@
 #include "ether-addr-util.h"
 #include "hostname-setup.h"
 #include "network-internal.h"
+#include "networkd-address.h"
 #include "networkd-manager.h"
+#include "networkd-route.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tests.h"
index 5156fcc7d7805ff0c034cc97a886b4b29713f489..3d7ce3aca86d4df04a5befc213bb7cae530c5ab4 100644 (file)
@@ -4,6 +4,7 @@
 #include "log.h"
 #include "macro.h"
 #include "net-condition.h"
+#include "networkd-address.h"
 #include "networkd-conf.h"
 #include "networkd-network.h"
 #include "strv.h"