1
0
mirror of https://github.com/systemd/systemd.git synced 2025-02-08 09:57:41 +03:00

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.
This commit is contained in:
Yu Watanabe 2024-01-14 14:20:03 +09:00
parent bd922111fd
commit 8d01e44c1f
11 changed files with 291 additions and 325 deletions

View 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;

View 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);

View 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)

View 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)

View 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;

View 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);

View File

@ -126,7 +126,6 @@ typedef struct Link {
Set *addresses;
Set *neighbors;
Set *routes;
Set *qdiscs;
Set *tclasses;

View 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;

View 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;

View 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;

View 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);