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:
parent
bd922111fd
commit
8d01e44c1f
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -126,7 +126,6 @@ typedef struct Link {
|
||||
|
||||
Set *addresses;
|
||||
Set *neighbors;
|
||||
Set *routes;
|
||||
Set *qdiscs;
|
||||
Set *tclasses;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user