]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/route: manage all routes by Manager object
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 14 Jan 2024 05:20:03 +0000 (14:20 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 24 Jan 2024 12:52:33 +0000 (21:52 +0900)
Previously, a Route object is owned by a Link object corresponding to the
outgoing interface of the route, and a Route object that does not have
outgoing interface is owned by the Manager object.

However, there were several issues:
- if a route has a nexthop ID, then the corresponding nexthop may be
  changed to use another interface, hence the outgoing interface of the
  route may be changed.
- if a route requested with MultiPathRoute=, then the link who requests
  the route is different from the outgoing interface of the configured
  route. So, we need to find routes on other interfaces on reconfiguring
  or so.

By this change, the limit of the number of routes per-interface is
tentatively dropped. Let's re-introduce the limit later in a nicer way.

src/network/networkd-address.c
src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-json.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-ndisc.c
src/network/networkd-route-util.c
src/network/networkd-route.c
src/network/networkd-route.h

index 631c64ec06e85db2971e86fd474aacffc9ef0228..c86e6d1a0f9431eb63381cb019c64ac3e9ab8670 100644 (file)
@@ -1208,6 +1208,7 @@ bool link_address_is_dynamic(const Link *link, const Address *address) {
         Route *route;
 
         assert(link);
+        assert(link->manager);
         assert(address);
 
         if (address->lifetime_preferred_usec != USEC_INFINITY)
@@ -1216,7 +1217,7 @@ 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) {
+        SET_FOREACH(route, link->manager->routes) {
                 if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
                         continue;
 
@@ -1227,6 +1228,9 @@ bool link_address_is_dynamic(const Link *link, const Address *address) {
                 if (route->protocol != RTPROT_DHCP)
                         continue;
 
+                if (route->nexthop.ifindex != link->ifindex)
+                        continue;
+
                 if (address->family != route->family)
                         continue;
 
index b0e4afdc45fb950b22714fa9b7ed3dc1a7c87924..b8b03f6d3b94882d0cf11c10091aa7c92e0dab6d 100644 (file)
@@ -101,6 +101,7 @@ static int link_get_by_dhcp_pd_subnet_prefix(Manager *manager, const struct in6_
 
 static int dhcp_pd_get_assigned_subnet_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) {
         assert(link);
+        assert(link->manager);
         assert(pd_prefix);
 
         if (!link_dhcp_pd_is_enabled(link))
@@ -128,11 +129,14 @@ static int dhcp_pd_get_assigned_subnet_prefix(Link *link, const struct in6_addr
         } else {
                 Route *route;
 
-                SET_FOREACH(route, link->routes) {
+                SET_FOREACH(route, link->manager->routes) {
                         if (route->source != NETWORK_CONFIG_SOURCE_DHCP_PD)
                                 continue;
                         assert(route->family == AF_INET6);
 
+                        if (route->nexthop.ifindex != link->ifindex)
+                                continue;
+
                         if (in6_addr_prefix_covers(pd_prefix, pd_prefix_len, &route->dst.in6) > 0) {
                                 if (ret)
                                         *ret = route->dst.in6;
@@ -159,9 +163,11 @@ int dhcp_pd_remove(Link *link, bool only_marked) {
         if (!link->network->dhcp_pd_assign) {
                 Route *route;
 
-                SET_FOREACH(route, link->routes) {
+                SET_FOREACH(route, link->manager->routes) {
                         if (route->source != NETWORK_CONFIG_SOURCE_DHCP_PD)
                                 continue;
+                        if (route->nexthop.ifindex != link->ifindex)
+                                continue;
                         if (only_marked && !route_is_marked(route))
                                 continue;
 
@@ -282,6 +288,7 @@ static int dhcp_pd_request_route(Link *link, const struct in6_addr *prefix, usec
         int r;
 
         assert(link);
+        assert(link->manager);
         assert(link->network);
         assert(prefix);
 
@@ -304,7 +311,7 @@ static int dhcp_pd_request_route(Link *link, const struct in6_addr *prefix, usec
         if (r < 0)
                 return r;
 
-        if (route_get(NULL, link, route, &existing) < 0)
+        if (route_get(link->manager, route, &existing) < 0)
                 link->dhcp_pd_configured = false;
         else
                 route_unmark(existing);
@@ -548,7 +555,7 @@ static int dhcp_pd_prepare(Link *link) {
                 return 0;
 
         link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP_PD);
-        link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP_PD);
+        manager_mark_routes(link->manager, link, NETWORK_CONFIG_SOURCE_DHCP_PD);
 
         return 1;
 }
@@ -671,6 +678,7 @@ static int dhcp_request_unreachable_route(
         int r;
 
         assert(link);
+        assert(link->manager);
         assert(addr);
         assert(IN_SET(source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6));
         assert(server_address);
@@ -702,7 +710,7 @@ static int dhcp_request_unreachable_route(
         if (r < 0)
                 return r;
 
-        if (route_get(link->manager, NULL, route, &existing) < 0)
+        if (route_get(link->manager, route, &existing) < 0)
                 *configured = false;
         else
                 route_unmark(existing);
@@ -781,6 +789,7 @@ static int dhcp4_pd_request_default_gateway_on_6rd_tunnel(Link *link, const stru
         int r;
 
         assert(link);
+        assert(link->manager);
         assert(br_address);
 
         r = route_new(&route);
@@ -800,7 +809,7 @@ static int dhcp4_pd_request_default_gateway_on_6rd_tunnel(Link *link, const stru
         if (r < 0)
                 return r;
 
-        if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
+        if (route_get(link->manager, route, &existing) < 0) /* This is a new route. */
                 link->dhcp_pd_configured = false;
         else
                 route_unmark(existing);
index 28702ff4469073d7c5b6f45e05a4a42e2670f8f5..be7abcf6f3849f53e166cfc1cc878bd74b2f7952 100644 (file)
@@ -246,10 +246,13 @@ static int dhcp4_remove_address_and_routes(Link *link, bool only_marked) {
         int ret = 0;
 
         assert(link);
+        assert(link->manager);
 
-        SET_FOREACH(route, link->routes) {
+        SET_FOREACH(route, link->manager->routes) {
                 if (route->source != NETWORK_CONFIG_SOURCE_DHCP4)
                         continue;
+                if (route->nexthop.ifindex != 0 && route->nexthop.ifindex != link->ifindex)
+                        continue;
                 if (only_marked && !route_is_marked(route))
                         continue;
 
@@ -360,6 +363,7 @@ static int dhcp4_request_route(Route *route, Link *link) {
 
         assert(route);
         assert(link);
+        assert(link->manager);
         assert(link->network);
         assert(link->dhcp_lease);
 
@@ -393,7 +397,7 @@ static int dhcp4_request_route(Route *route, Link *link) {
         if (r < 0)
                 return r;
 
-        if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
+        if (route_get(link->manager, route, &existing) < 0) /* This is a new route. */
                 link->dhcp4_configured = false;
         else
                 route_unmark(existing);
@@ -986,7 +990,7 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) {
         assert(link);
 
         link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP4);
-        link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP4);
+        manager_mark_routes(link->manager, link, NETWORK_CONFIG_SOURCE_DHCP4);
 
         r = dhcp4_request_address(link, announce);
         if (r < 0)
index 0024adb816bf833d5b13942aacbf356587bfa5a1..26ed034bbeae03243782198d64f3a3d601980b72 100644 (file)
@@ -51,11 +51,12 @@ static int dhcp6_remove(Link *link, bool only_marked) {
         int ret = 0;
 
         assert(link);
+        assert(link->manager);
 
         if (!only_marked)
                 link->dhcp6_configured = false;
 
-        SET_FOREACH(route, link->routes) {
+        SET_FOREACH(route, link->manager->routes) {
                 if (route->source != NETWORK_CONFIG_SOURCE_DHCP6)
                         continue;
                 if (only_marked && !route_is_marked(route))
@@ -292,7 +293,7 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
         int r;
 
         link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6);
-        link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP6);
+        manager_mark_routes(link->manager, NULL, NETWORK_CONFIG_SOURCE_DHCP6);
 
         r = sd_dhcp6_client_get_lease(client, &lease);
         if (r < 0)
index b2bcc1bf75eeb80bea6da60ef838f7e5ec0e05fe..4db622b638de9ae8ecd2a53978094e5fc6f72b50 100644 (file)
@@ -202,16 +202,11 @@ static int nexthops_append_json(Manager *manager, int ifindex, JsonVariant **v)
 
 static int route_append_json(Route *route, JsonVariant **array) {
         _cleanup_free_ char *scope = NULL, *protocol = NULL, *table = NULL, *flags = NULL, *state = NULL;
-        Manager *manager;
         int r;
 
         assert(route);
         assert(array);
 
-        manager = route->link ? route->link->manager : route->manager;
-
-        assert(manager);
-
         r = route_scope_to_string_alloc(route->scope, &scope);
         if (r < 0)
                 return r;
@@ -220,7 +215,7 @@ static int route_append_json(Route *route, JsonVariant **array) {
         if (r < 0)
                 return r;
 
-        r = manager_get_route_table_to_string(manager, route->table, /* append_num = */ false, &table);
+        r = manager_get_route_table_to_string(route->manager, route->table, /* append_num = */ false, &table);
         if (r < 0)
                 return r;
 
@@ -262,14 +257,18 @@ static int route_append_json(Route *route, JsonVariant **array) {
                                 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &route->provider, route->family)));
 }
 
-static int routes_append_json(Set *routes, JsonVariant **v) {
+static int routes_append_json(Manager *manager, int ifindex, JsonVariant **v) {
         _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
         Route *route;
         int r;
 
+        assert(manager);
         assert(v);
 
-        SET_FOREACH(route, routes) {
+        SET_FOREACH(route, manager->routes) {
+                if (route->nexthop.ifindex != ifindex)
+                        continue;
+
                 r = route_append_json(route, &array);
                 if (r < 0)
                         return r;
@@ -1330,7 +1329,7 @@ int link_build_json(Link *link, JsonVariant **ret) {
         if (r < 0)
                 return r;
 
-        r = routes_append_json(link->routes, &v);
+        r = routes_append_json(link->manager, link->ifindex, &v);
         if (r < 0)
                 return r;
 
@@ -1393,7 +1392,7 @@ int manager_build_json(Manager *manager, JsonVariant **ret) {
         if (r < 0)
                 return r;
 
-        r = routes_append_json(manager->routes, &v);
+        r = routes_append_json(manager, /* ifindex = */ 0, &v);
         if (r < 0)
                 return r;
 
index 49ed8f59bb1804c67d1eb46203e3edd8135fe3a7..a79ab0d43c12bac6c595119071851aec2c89f011 100644 (file)
@@ -222,7 +222,6 @@ static Link *link_free(Link *link) {
         link_ntp_settings_clear(link);
         link_dns_settings_clear(link);
 
-        link->routes = set_free(link->routes);
         link->neighbors = set_free(link->neighbors);
         link->addresses = set_free(link->addresses);
         link->qdiscs = set_free(link->qdiscs);
index b5b1995361a77ad38d11a6d8d5332d16419f6218..985670fcd2fb9243bd39af9392ad63df87e2e16a 100644 (file)
@@ -126,7 +126,6 @@ typedef struct Link {
 
         Set *addresses;
         Set *neighbors;
-        Set *routes;
         Set *qdiscs;
         Set *tclasses;
 
index ca8bbb2d5ef6766b1c6ddd8f6e4980fc25ef693b..7186187fd828f379247a31cbc595dea4840b11cc 100644 (file)
@@ -181,6 +181,7 @@ static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
 
         assert(route);
         assert(link);
+        assert(link->manager);
         assert(link->network);
         assert(rt);
 
@@ -221,7 +222,7 @@ static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return r;
 
-        is_new = route_get(NULL, link, route, NULL) < 0;
+        is_new = route_get(link->manager, route, NULL) < 0;
 
         r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
         if (r < 0)
@@ -1154,6 +1155,7 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
         int r, ret = 0;
 
         assert(link);
+        assert(link->manager);
 
         /* If an address or friends is already assigned, but not valid anymore, then refuse to update it,
          * and let's immediately remove it.
@@ -1161,10 +1163,13 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
          * valid lifetimes to improve the reaction of SLAAC to renumbering events.
          * See draft-ietf-6man-slaac-renum-02, section 4.2. */
 
-        SET_FOREACH(route, link->routes) {
+        SET_FOREACH(route, link->manager->routes) {
                 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
                         continue;
 
+                if (route->nexthop.ifindex != link->ifindex)
+                        continue;
+
                 if (route->lifetime_usec >= timestamp_usec)
                         continue; /* the route is still valid */
 
@@ -1252,10 +1257,13 @@ static int ndisc_setup_expire(Link *link) {
         assert(link);
         assert(link->manager);
 
-        SET_FOREACH(route, link->routes) {
+        SET_FOREACH(route, link->manager->routes) {
                 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
                         continue;
 
+                if (route->nexthop.ifindex != link->ifindex)
+                        continue;
+
                 if (!route_exists(route))
                         continue;
 
index fde7dfa5d42e52c8b1bd03f130836de10a805547..b7e07f46623213c36e591c4acf5cbc15c9fd2bf0 100644 (file)
@@ -58,8 +58,11 @@ bool link_find_default_gateway(Link *link, int family, Route **gw) {
         Route *route;
 
         assert(link);
+        assert(link->manager);
 
-        SET_FOREACH(route, link->routes) {
+        SET_FOREACH(route, link->manager->routes) {
+                if (route->nexthop.ifindex != link->ifindex)
+                        continue;
                 if (!route_exists(route))
                         continue;
                 if (family != AF_UNSPEC && route->family != family)
@@ -119,12 +122,7 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) {
         if (!gw)
                 return -ENOENT;
 
-        if (ret) {
-                assert(gw->link);
-                *ret = gw->link;
-        }
-
-        return 0;
+        return link_get_by_index(m, gw->nexthop.ifindex, ret);
 }
 
 bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw) {
@@ -143,7 +141,9 @@ bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_u
         if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6))
                 return true;
 
-        SET_FOREACH(route, link->routes) {
+        SET_FOREACH(route, link->manager->routes) {
+                if (route->nexthop.ifindex != link->ifindex)
+                        continue;
                 if (!route_exists(route))
                         continue;
                 if (!route_lifetime_is_valid(route))
@@ -187,10 +187,14 @@ static int link_address_is_reachable_internal(
         Route *route, *found = NULL;
 
         assert(link);
+        assert(link->manager);
         assert(IN_SET(family, AF_INET, AF_INET6));
         assert(address);
 
-        SET_FOREACH(route, link->routes) {
+        SET_FOREACH(route, link->manager->routes) {
+                if (route->nexthop.ifindex != link->ifindex)
+                        continue;
+
                 if (!route_exists(route))
                         continue;
 
@@ -304,7 +308,11 @@ int manager_address_is_reachable(
                 return 0;
         }
 
-        r = link_get_address(found->link, found->family, &found->prefsrc, 0, &a);
+        r = link_get_by_index(manager, found->nexthop.ifindex, &link);
+        if (r < 0)
+                return r;
+
+        r = link_get_address(link, found->family, &found->prefsrc, 0, &a);
         if (r < 0)
                 return r;
 
index 05280a582f6ac6d57b1680e73adbb95b27b0fc4f..3a33769f8b5094bb0d4aa822852e434a0f59e959 100644 (file)
@@ -30,9 +30,6 @@ Route* route_free(Route *route) {
                 hashmap_remove(route->network->routes_by_section, route->section);
         }
 
-        if (route->link)
-                set_remove(route->link->routes, route);
-
         if (route->manager)
                 set_remove(route->manager->routes, route);
 
@@ -270,18 +267,6 @@ static int route_add(Manager *manager, Route *route) {
         assert(!route->network);
         assert(!route->wireguard);
 
-        Link *link;
-        if (route_nexthop_get_link(manager, &route->nexthop, &link) >= 0) {
-                r = set_ensure_put(&link->routes, &route_hash_ops, route);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        return -EEXIST;
-
-                route->link = link;
-                return 0;
-        }
-
         r = set_ensure_put(&manager->routes, &route_hash_ops, route);
         if (r < 0)
                 return r;
@@ -292,17 +277,13 @@ static int route_add(Manager *manager, Route *route) {
         return 0;
 }
 
-int route_get(Manager *manager, Link *link, const Route *route, Route **ret) {
+int route_get(Manager *manager, const Route *route, Route **ret) {
         Route *existing;
 
-        if (!manager)
-                manager = ASSERT_PTR(ASSERT_PTR(link)->manager);
+        assert(manager);
         assert(route);
 
-        if (route_nexthop_get_link(manager, &route->nexthop, &link) >= 0)
-                existing = set_get(link->routes, route);
-        else
-                existing = set_get(manager->routes, route);
+        existing = set_get(manager->routes, route);
         if (!existing)
                 return -ENOENT;
 
@@ -368,7 +349,6 @@ int route_dup(const Route *src, const RouteNextHop *nh, Route **ret) {
         dest->network = NULL;
         dest->wireguard = NULL;
         dest->section = NULL;
-        dest->link = NULL;
         dest->nexthop = ROUTE_NEXTHOP_NULL;
         dest->nexthops = NULL;
         dest->metric = ROUTE_METRIC_NULL;
@@ -386,19 +366,6 @@ int route_dup(const Route *src, const RouteNextHop *nh, Route **ret) {
         return 0;
 }
 
-void link_mark_routes(Link *link, NetworkConfigSource source) {
-        Route *route;
-
-        assert(link);
-
-        SET_FOREACH(route, link->routes) {
-                if (route->source != source)
-                        continue;
-
-                route_mark(route);
-        }
-}
-
 static void log_route_debug(const Route *route, const char *str, Manager *manager) {
         _cleanup_free_ char *state = NULL, *nexthop = NULL, *prefsrc = NULL,
                 *table = NULL, *scope = NULL, *proto = NULL, *flags = NULL;
@@ -545,18 +512,16 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l
 
 int route_remove(Route *route) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        Manager *manager;
-        Link *link;
+        Link *link = NULL;
         int r;
 
         assert(route);
-        assert(route->manager || (route->link && route->link->manager));
-
-        link = route->link;
-        manager = route->manager ?: link->manager;
+        Manager *manager = ASSERT_PTR(route->manager);
 
         log_route_debug(route, "Removing", manager);
 
+        (void) route_get_link(manager, route, &link);
+
         r = sd_rtnl_message_new_route(manager->rtnl, &m, RTM_DELROUTE, route->family, route->protocol);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Could not create netlink message: %m");
@@ -591,239 +556,16 @@ int route_remove_and_drop(Route *route) {
         return 0;
 }
 
-static int link_unmark_route(Link *link, const Route *route, const RouteNextHop *nh) {
-        _cleanup_(route_freep) Route *tmp = NULL;
-        Route *existing;
-        int r;
-
-        assert(link);
-        assert(route);
-
-        r = route_dup(route, nh, &tmp);
-        if (r < 0)
-                return r;
-
-        r = route_adjust_nexthops(tmp, link);
-        if (r < 0)
-                return r;
-
-        if (route_get(link->manager, link, tmp, &existing) < 0)
-                return 0;
-
-        route_unmark(existing);
-        return 1;
-}
-
-static void manager_mark_routes(Manager *manager, bool foreign, const Link *except) {
-        Route *route;
-        Link *link;
-
-        assert(manager);
-
-        /* First, mark all routes. */
-        SET_FOREACH(route, manager->routes) {
-                /* Do not touch routes managed by the kernel. */
-                if (route->protocol == RTPROT_KERNEL)
-                        continue;
-
-                /* When 'foreign' is true, mark only foreign routes, and vice versa. */
-                if (foreign != (route->source == NETWORK_CONFIG_SOURCE_FOREIGN))
-                        continue;
-
-                /* Do not touch dynamic routes. They will removed by dhcp_pd_prefix_lost() */
-                if (IN_SET(route->source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6))
-                        continue;
-
-                /* Ignore routes not assigned yet or already removed. */
-                if (!route_exists(route))
-                        continue;
-
-                route_mark(route);
-        }
-
-        /* Then, unmark all routes requested by active links. */
-        HASHMAP_FOREACH(link, manager->links_by_index) {
-                if (link == except)
-                        continue;
-
-                if (!link->network)
-                        continue;
-
-                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
-                        continue;
-
-                HASHMAP_FOREACH(route, link->network->routes_by_section) {
-                        if (route->family == AF_INET || ordered_set_isempty(route->nexthops))
-                                (void) link_unmark_route(link, route, NULL);
-
-                        else {
-                                RouteNextHop *nh;
-                                ORDERED_SET_FOREACH(nh, route->nexthops)
-                                        (void) link_unmark_route(link, route, nh);
-                        }
-                }
-        }
-}
-
-static int manager_drop_marked_routes(Manager *manager) {
-        Route *route;
-        int r = 0;
-
-        assert(manager);
-
-        SET_FOREACH(route, manager->routes) {
-                if (!route_is_marked(route))
-                        continue;
-
-                RET_GATHER(r, route_remove(route));
-        }
-
-        return r;
-}
-
-static bool route_by_kernel(const Route *route) {
-        assert(route);
-
-        if (route->protocol == RTPROT_KERNEL)
-                return true;
-
-        /* The kernels older than a826b04303a40d52439aa141035fca5654ccaccd (v5.11) create the IPv6
-         * multicast with RTPROT_BOOT. Do not touch it. */
-        if (route->protocol == RTPROT_BOOT &&
-            route->family == AF_INET6 &&
-            route->dst_prefixlen == 8 &&
-            in6_addr_equal(&route->dst.in6, & (struct in6_addr) {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}}))
-                return true;
-
-        return false;
-}
-
-static void link_unmark_wireguard_routes(Link *link) {
-        assert(link);
-
-        if (!link->netdev || link->netdev->kind != NETDEV_KIND_WIREGUARD)
-                return;
-
-        Route *route;
-        Wireguard *w = WIREGUARD(link->netdev);
-
-        SET_FOREACH(route, w->routes)
-                (void) link_unmark_route(link, route, NULL);
-}
-
-int link_drop_foreign_routes(Link *link) {
-        Route *route;
-        int r;
-
-        assert(link);
-        assert(link->manager);
-        assert(link->network);
-
-        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 &&
-                    FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
-                        continue;
-
-                if (route->protocol == RTPROT_DHCP &&
-                    FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
-                        continue;
-
-                route_mark(route);
-        }
-
-        HASHMAP_FOREACH(route, link->network->routes_by_section) {
-                if (route->family == AF_INET || ordered_set_isempty(route->nexthops))
-                        (void) link_unmark_route(link, route, NULL);
-
-                else {
-                        RouteNextHop *nh;
-                        ORDERED_SET_FOREACH(nh, route->nexthops)
-                                (void) link_unmark_route(link, route, nh);
-                }
-        }
-
-        link_unmark_wireguard_routes(link);
-
-        r = 0;
-        SET_FOREACH(route, link->routes) {
-                if (!route_is_marked(route))
-                        continue;
-
-                RET_GATHER(r, route_remove(route));
-        }
-
-        manager_mark_routes(link->manager, /* foreign = */ true, NULL);
-
-        return RET_GATHER(r, manager_drop_marked_routes(link->manager));
-}
-
-int link_drop_managed_routes(Link *link) {
-        Route *route;
-        int r = 0;
-
-        assert(link);
-
-        SET_FOREACH(route, link->routes) {
-                /* do not touch routes managed by the kernel */
-                if (route_by_kernel(route))
-                        continue;
-
-                /* Do not touch routes managed by kernel or other tools. */
-                if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN)
-                        continue;
-
-                if (!route_exists(route))
-                        continue;
-
-                RET_GATHER(r, route_remove(route));
-        }
-
-        manager_mark_routes(link->manager, /* foreign = */ false, link);
-
-        return RET_GATHER(r, manager_drop_marked_routes(link->manager));
-}
-
-void link_foreignize_routes(Link *link) {
-        Route *route;
-
-        assert(link);
-
-        SET_FOREACH(route, link->routes)
-                route->source = NETWORK_CONFIG_SOURCE_FOREIGN;
-
-        manager_mark_routes(link->manager, /* foreign = */ false, link);
-
-        SET_FOREACH(route, link->manager->routes) {
-                if (!route_is_marked(route))
-                        continue;
-
-                route->source = NETWORK_CONFIG_SOURCE_FOREIGN;
-        }
-}
-
 static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
         Route *route = ASSERT_PTR(userdata);
-        Link *link;
         int r;
 
-        assert(route->manager || (route->link && route->link->manager));
-
-        link = route->link; /* This may be NULL. */
+        assert(route->manager);
 
         r = route_remove(route);
         if (r < 0) {
+                Link *link = NULL;
+                (void) route_get_link(route->manager, route, &link);
                 log_link_warning_errno(link, r, "Could not remove route: %m");
                 if (link)
                         link_enter_failed(link);
@@ -846,11 +588,14 @@ static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo
                 return 0;
         }
 
-        Manager *manager = ASSERT_PTR(route->manager ?: ASSERT_PTR(route->link)->manager);
+        Manager *manager = ASSERT_PTR(route->manager);
         r = event_reset_time(manager->event, &route->expire, CLOCK_BOOTTIME,
                              route->lifetime_usec, 0, route_expire_handler, route, 0, "route-expiration", true);
-        if (r < 0)
-                return log_link_warning_errno(route->link, r, "Failed to configure expiration timer for route, ignoring: %m");
+        if (r < 0) {
+                Link *link = NULL;
+                (void) route_get_link(manager, route, &link);
+                return log_link_warning_errno(link, r, "Failed to configure expiration timer for route, ignoring: %m");
+        }
 
         log_route_debug(route, "Configured expiration timer for", manager);
         return 1;
@@ -869,7 +614,7 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li
         if (r == -EEXIST) {
                 Route *existing;
 
-                if (route_get(link->manager, link, route, &existing) >= 0) {
+                if (route_get(link->manager, route, &existing) >= 0) {
                         /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE
                          * notification, so we need to update the timer here. */
                         existing->lifetime_usec = route->lifetime_usec;
@@ -993,9 +738,6 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
         if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return false;
 
-        if (set_size(link->routes) >= routes_max())
-                return false;
-
         if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
                 r = manager_has_address(link->manager, route->family, &route->prefsrc);
                 if (r <= 0)
@@ -1028,7 +770,7 @@ static int route_process_request(Request *req, Link *link, Route *route) {
                                network_config_source_to_string(route->source));
 
                 route_cancel_requesting(route);
-                if (route_get(link->manager, link, route, &existing) >= 0)
+                if (route_get(link->manager, route, &existing) >= 0)
                         route_cancel_requesting(existing);
                 return 1;
         }
@@ -1042,7 +784,7 @@ static int route_process_request(Request *req, Link *link, Route *route) {
                 return log_link_warning_errno(link, r, "Failed to configure route: %m");
 
         route_enter_configuring(route);
-        if (route_get(link->manager, link, route, &existing) >= 0)
+        if (route_get(link->manager, route, &existing) >= 0)
                 route_enter_configuring(existing);
         return 1;
 }
@@ -1070,7 +812,7 @@ static int link_request_route_one(
         if (r < 0)
                 return r;
 
-        if (route_get(link->manager, link, tmp, &existing) >= 0)
+        if (route_get(link->manager, tmp, &existing) >= 0)
                 /* Copy state for logging below. */
                 tmp->state = existing->state;
 
@@ -1204,9 +946,7 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
 
 void route_cancel_request(Route *route, Link *link) {
         assert(route);
-        Manager *manager = ASSERT_PTR(route->manager ?:
-                                      route->link ? route->link->manager :
-                                      ASSERT_PTR(link)->manager);
+        Manager *manager = ASSERT_PTR(route->manager ?: ASSERT_PTR(link)->manager);
 
         if (!route_is_requesting(route))
                 return;
@@ -1235,7 +975,7 @@ static int process_route_one(
         assert(tmp);
         assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
 
-        (void) route_get(manager, NULL, tmp, &route);
+        (void) route_get(manager, tmp, &route);
         (void) route_get_request(manager, tmp, &req);
         (void) route_get_link(manager, tmp, &link);
 
@@ -1472,6 +1212,197 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
         return 1;
 }
 
+void manager_mark_routes(Manager *manager, Link *link, NetworkConfigSource source) {
+        Route *route;
+
+        assert(manager);
+
+        SET_FOREACH(route, manager->routes) {
+                if (route->source != source)
+                        continue;
+
+                if (link) {
+                        Link *route_link;
+
+                        if (route_get_link(manager, route, &route_link) < 0)
+                                continue;
+                        if (route_link != link)
+                                continue;
+                }
+
+                route_mark(route);
+        }
+}
+
+static bool route_by_kernel(const Route *route) {
+        assert(route);
+
+        if (route->protocol == RTPROT_KERNEL)
+                return true;
+
+        /* The kernels older than a826b04303a40d52439aa141035fca5654ccaccd (v5.11) create the IPv6
+         * multicast with RTPROT_BOOT. Do not touch it. */
+        if (route->protocol == RTPROT_BOOT &&
+            route->family == AF_INET6 &&
+            route->dst_prefixlen == 8 &&
+            in6_addr_equal(&route->dst.in6, & (struct in6_addr) {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}}))
+                return true;
+
+        return false;
+}
+
+static int link_unmark_route(Link *link, const Route *route, const RouteNextHop *nh) {
+        _cleanup_(route_freep) Route *tmp = NULL;
+        Route *existing;
+        int r;
+
+        assert(link);
+        assert(route);
+
+        r = route_dup(route, nh, &tmp);
+        if (r < 0)
+                return r;
+
+        r = route_adjust_nexthops(tmp, link);
+        if (r < 0)
+                return r;
+
+        if (route_get(link->manager, tmp, &existing) < 0)
+                return 0;
+
+        route_unmark(existing);
+        return 1;
+}
+
+static int link_mark_routes(Link *link, bool foreign) {
+        Route *route;
+        Link *other;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        /* First, mark all routes. */
+        SET_FOREACH(route, link->manager->routes) {
+                /* Do not touch routes managed by the kernel. */
+                if (route_by_kernel(route))
+                        continue;
+
+                /* When 'foreign' is true, mark only foreign routes, and vice versa.
+                 * Note, do not touch dynamic routes. They will removed by when e.g. lease is lost. */
+                if (route->source != (foreign ? NETWORK_CONFIG_SOURCE_FOREIGN : NETWORK_CONFIG_SOURCE_STATIC))
+                        continue;
+
+                /* Ignore routes not assigned yet or already removed. */
+                if (!route_exists(route))
+                        continue;
+
+                if (link->network) {
+                        if (route->protocol == RTPROT_STATIC &&
+                            FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
+                                continue;
+
+                        if (route->protocol == RTPROT_DHCP &&
+                            FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
+                                continue;
+                }
+
+                /* When we mark foreign routes, do not mark routes assigned to other interfaces.
+                 * Otherwise, routes assigned to unmanaged interfaces will be dropped.
+                 * Note, route_get_link() does not provide assigned link for routes with an unreachable type
+                 * or IPv4 multipath routes. So, the current implementation does not support managing such
+                 * routes by other daemon or so, unless ManageForeignRoutes=no. */
+                if (foreign) {
+                        Link *route_link;
+
+                        if (route_get_link(link->manager, route, &route_link) >= 0 && route_link != link)
+                                continue;
+                }
+
+                route_mark(route);
+        }
+
+        /* Then, unmark all routes requested by active links. */
+        HASHMAP_FOREACH(other, link->manager->links_by_index) {
+                if (!foreign && other == link)
+                        continue;
+
+                if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                        continue;
+
+                HASHMAP_FOREACH(route, other->network->routes_by_section) {
+                        if (route->family == AF_INET || ordered_set_isempty(route->nexthops)) {
+                                r = link_unmark_route(other, route, NULL);
+                                if (r < 0)
+                                        return r;
+
+                        } else {
+                                RouteNextHop *nh;
+                                ORDERED_SET_FOREACH(nh, route->nexthops) {
+                                        r = link_unmark_route(other, route, nh);
+                                        if (r < 0)
+                                                return r;
+                                }
+                        }
+                }
+        }
+
+        /* Also unmark routes requested in .netdev file. */
+        if (foreign && link->netdev && link->netdev->kind == NETDEV_KIND_WIREGUARD) {
+                Wireguard *w = WIREGUARD(link->netdev);
+
+                SET_FOREACH(route, w->routes) {
+                        r = link_unmark_route(link, route, NULL);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
+int link_drop_routes(Link *link, bool foreign) {
+        Route *route;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        r = link_mark_routes(link, foreign);
+        if (r < 0)
+                return r;
+
+        SET_FOREACH(route, link->manager->routes) {
+                if (!route_is_marked(route))
+                        continue;
+
+                RET_GATHER(r, route_remove(route));
+        }
+
+        return r;
+}
+
+int link_foreignize_routes(Link *link) {
+        Route *route;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        r = link_mark_routes(link, /* foreign = */ false);
+        if (r < 0)
+                return r;
+
+        SET_FOREACH(route, link->manager->routes) {
+                if (!route_is_marked(route))
+                        continue;
+
+                route->source = NETWORK_CONFIG_SOURCE_FOREIGN;
+        }
+
+        return 0;
+}
+
 int network_add_ipv4ll_route(Network *network) {
         _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
         unsigned section_line;
index e86693e3152d437d8d64835fa7c3faf594b79c53..090bf34a61aadf34f4f028ae37169ef23e053c45 100644 (file)
@@ -27,7 +27,6 @@ typedef int (*route_netlink_handler_t)(
                 Route *route);
 
 struct Route {
-        Link *link;
         Manager *manager;
         Network *network;
         Wireguard *wireguard;
@@ -92,11 +91,16 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li
 int route_remove(Route *route);
 int route_remove_and_drop(Route *route);
 
-int route_get(Manager *manager, Link *link, const Route *in, Route **ret);
+int route_get(Manager *manager, const Route *route, Route **ret);
 
-int link_drop_managed_routes(Link *link);
-int link_drop_foreign_routes(Link *link);
-void link_foreignize_routes(Link *link);
+int link_drop_routes(Link *link, bool foreign);
+static inline int link_drop_managed_routes(Link *link) {
+        return link_drop_routes(link, false);
+}
+static inline int link_drop_foreign_routes(Link *link) {
+        return link_drop_routes(link, true);
+}
+int link_foreignize_routes(Link *link);
 
 void route_cancel_request(Route *route, Link *link);
 int link_request_route(
@@ -114,7 +118,7 @@ void network_drop_invalid_routes(Network *network);
 int route_section_verify(Route *route);
 
 DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Route, route);
-void link_mark_routes(Link *link, NetworkConfigSource source);
+void manager_mark_routes(Manager *manager, Link *link, NetworkConfigSource source);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src);
 CONFIG_PARSER_PROTOTYPE(config_parse_destination);