From 8d01e44c1f0e00b414d36bd1b46ecff548242208 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 14 Jan 2024 14:20:03 +0900 Subject: [PATCH] network/route: manage all routes by Manager object 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 | 6 +- src/network/networkd-dhcp-prefix-delegation.c | 21 +- src/network/networkd-dhcp4.c | 10 +- src/network/networkd-dhcp6.c | 5 +- src/network/networkd-json.c | 19 +- src/network/networkd-link.c | 1 - src/network/networkd-link.h | 1 - src/network/networkd-ndisc.c | 14 +- src/network/networkd-route-util.c | 28 +- src/network/networkd-route.c | 495 ++++++++---------- src/network/networkd-route.h | 16 +- 11 files changed, 291 insertions(+), 325 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 631c64ec06e..c86e6d1a0f9 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -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; diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c index b0e4afdc45f..b8b03f6d3b9 100644 --- a/src/network/networkd-dhcp-prefix-delegation.c +++ b/src/network/networkd-dhcp-prefix-delegation.c @@ -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); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 28702ff4469..be7abcf6f38 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -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) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 0024adb816b..26ed034bbea 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -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) diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index b2bcc1bf75e..4db622b638d 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -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; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 49ed8f59bb1..a79ab0d43c1 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -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); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index b5b1995361a..985670fcd2f 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -126,7 +126,6 @@ typedef struct Link { Set *addresses; Set *neighbors; - Set *routes; Set *qdiscs; Set *tclasses; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index ca8bbb2d5ef..7186187fd82 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -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; diff --git a/src/network/networkd-route-util.c b/src/network/networkd-route-util.c index fde7dfa5d42..b7e07f46623 100644 --- a/src/network/networkd-route-util.c +++ b/src/network/networkd-route-util.c @@ -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; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 05280a582f6..3a33769f8b5 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -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; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index e86693e3152..090bf34a61a 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -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);