summaryrefslogtreecommitdiff
path: root/src/network/networkd-route.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/networkd-route.c')
-rw-r--r--src/network/networkd-route.c1163
1 files changed, 543 insertions, 620 deletions
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index c94887ef8a..009afa1b29 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -6,6 +6,7 @@
#include "alloc-util.h"
#include "netlink-util.h"
+#include "networkd-address.h"
#include "networkd-ipv4ll.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@@ -226,6 +227,7 @@ static int route_new_static(Network *network, const char *filename, unsigned sec
route->protocol = RTPROT_STATIC;
route->network = network;
route->section = TAKE_PTR(n);
+ route->source = NETWORK_CONFIG_SOURCE_STATIC;
r = hashmap_ensure_put(&network->routes_by_section, &network_config_hash_ops, route->section, route);
if (r < 0)
@@ -246,26 +248,11 @@ Route *route_free(Route *route) {
network_config_section_free(route->section);
- if (route->link) {
- NDiscRoute *n;
-
+ if (route->link)
set_remove(route->link->routes, route);
- set_remove(route->link->routes_foreign, route);
- set_remove(route->link->dhcp_routes, route);
- set_remove(route->link->dhcp_routes_old, route);
- set_remove(route->link->dhcp6_routes, route);
- set_remove(route->link->dhcp6_routes_old, route);
- set_remove(route->link->dhcp6_pd_routes, route);
- set_remove(route->link->dhcp6_pd_routes_old, route);
- SET_FOREACH(n, route->link->ndisc_routes)
- if (route_equal(n->route, route))
- free(set_remove(route->link->ndisc_routes, n));
- }
-
- if (route->manager) {
+
+ if (route->manager)
set_remove(route->manager->routes, route);
- set_remove(route->manager->routes_foreign, route);
- }
ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
@@ -414,87 +401,77 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
route_compare_func,
route_free);
-bool route_equal(const Route *r1, const Route *r2) {
- if (r1 == r2)
- return true;
+static bool route_type_is_reject(const Route *route) {
+ assert(route);
- if (!r1 || !r2)
- return false;
+ return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
+}
+
+static bool route_needs_convert(const Route *route) {
+ assert(route);
- return route_compare_func(r1, r2) == 0;
+ return route->nexthop_id > 0 || !ordered_set_isempty(route->multipath_routes);
}
-static int route_get(const Manager *manager, const Link *link, const Route *in, Route **ret) {
- Route *existing;
+static int route_add(Manager *manager, Link *link, Route *route) {
+ int r;
- assert(manager || link);
- assert(in);
+ assert(route);
- existing = set_get(link ? link->routes : manager->routes, in);
- if (existing) {
- if (ret)
- *ret = existing;
- return 1;
- }
+ if (route_type_is_reject(route)) {
+ assert(manager);
- existing = set_get(link ? link->routes_foreign : manager->routes_foreign, in);
- if (existing) {
- if (ret)
- *ret = existing;
- return 0;
+ r = set_ensure_put(&manager->routes, &route_hash_ops, route);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
+
+ route->manager = manager;
+ } else {
+ assert(link);
+
+ r = set_ensure_put(&link->routes, &route_hash_ops, route);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
+
+ route->link = link;
}
- return -ENOENT;
+ return 0;
}
-static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight) {
- assert(dest);
- assert(src);
+int route_get(Manager *manager, Link *link, const Route *in, Route **ret) {
+ Route *route;
- /* This only copies entries used by the above hash and compare functions. */
-
- dest->family = src->family;
- dest->src = src->src;
- dest->src_prefixlen = src->src_prefixlen;
- dest->dst = src->dst;
- dest->dst_prefixlen = src->dst_prefixlen;
- dest->prefsrc = src->prefsrc;
- dest->scope = src->scope;
- dest->protocol = src->protocol;
- if (nh && nh->blackhole)
- dest->type = RTN_BLACKHOLE;
- else
- dest->type = src->type;
- dest->tos = src->tos;
- dest->priority = src->priority;
- dest->table = src->table;
- dest->initcwnd = src->initcwnd;
- dest->initrwnd = src->initrwnd;
- dest->lifetime = src->lifetime;
- dest->advmss = src->advmss;
- dest->nexthop_id = src->nexthop_id;
-
- if (nh) {
- assert(hashmap_isempty(nh->group));
-
- dest->gw_family = nh->family;
- dest->gw = nh->gw;
- dest->gw_weight = nh_weight != UINT8_MAX ? nh_weight : src->gw_weight;
- } else if (m) {
- dest->gw_family = m->gateway.family;
- dest->gw = m->gateway.address;
- dest->gw_weight = m->weight;
+ assert(in);
+
+ if (route_type_is_reject(in)) {
+ if (!manager)
+ return -ENOENT;
+
+ route = set_get(manager->routes, in);
} else {
- dest->gw_family = src->gw_family;
- dest->gw = src->gw;
- dest->gw_weight = src->gw_weight;
+ if (!link)
+ return -ENOENT;
+
+ route = set_get(link->routes, in);
}
+ if (!route)
+ return -ENOENT;
+
+ if (ret)
+ *ret = route;
+
+ return 0;
}
int route_dup(const Route *src, Route **ret) {
_cleanup_(route_freep) Route *dest = NULL;
- MultipathRoute *m;
- int r;
+
+ /* This does not copy mulipath routes. */
assert(src);
assert(ret);
@@ -511,278 +488,261 @@ int route_dup(const Route *src, Route **ret) {
dest->multipath_routes = NULL;
dest->expire = NULL;
- ORDERED_SET_FOREACH(m, src->multipath_routes) {
- _cleanup_(multipath_route_freep) MultipathRoute *n = NULL;
+ *ret = TAKE_PTR(dest);
+ return 0;
+}
- r = multipath_route_dup(m, &n);
- if (r < 0)
- return r;
+static void route_apply_nexthop(Route *route, const NextHop *nh, uint8_t nh_weight) {
+ assert(route);
+ assert(nh);
+ assert(hashmap_isempty(nh->group));
- r = ordered_set_ensure_put(&dest->multipath_routes, NULL, n);
- if (r < 0)
- return r;
+ route->gw_family = nh->family;
+ route->gw = nh->gw;
- TAKE_PTR(n);
- }
+ if (nh_weight != UINT8_MAX)
+ route->gw_weight = nh_weight;
- *ret = TAKE_PTR(dest);
- return 0;
+ if (nh->blackhole)
+ route->type = RTN_BLACKHOLE;
}
-static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, Route **ret) {
- _cleanup_(route_freep) Route *route = NULL;
- int r;
+static void route_apply_multipath_route(Route *route, const MultipathRoute *m) {
+ assert(route);
+ assert(m);
- assert(manager || link);
- assert(routes);
- assert(in);
+ route->gw_family = m->gateway.family;
+ route->gw = m->gateway.address;
+ route->gw_weight = m->weight;
+}
- r = route_new(&route);
- if (r < 0)
- return r;
+static int multipath_route_get_link(Manager *manager, const MultipathRoute *m, Link **ret) {
+ int r;
- route_copy(route, in, NULL, NULL, UINT8_MAX);
+ assert(manager);
+ assert(m);
- r = set_ensure_put(routes, &route_hash_ops, route);
- if (r < 0)
- return r;
- if (r == 0)
- return -EEXIST;
+ if (m->ifname) {
+ r = link_get_by_name(manager, m->ifname, ret);
+ return r < 0 ? r : 1;
- route->link = link;
- route->manager = manager;
+ } else if (m->ifindex > 0) { /* Always ignore ifindex if ifname is set. */
+ r = link_get_by_index(manager, m->ifindex, ret);
+ return r < 0 ? r : 1;
+ }
if (ret)
- *ret = route;
-
- route = NULL;
-
+ *ret = NULL;
return 0;
}
-static int route_add_foreign(Manager *manager, Link *link, const Route *in, Route **ret) {
- assert(manager || link);
- return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret);
-}
+typedef struct ConvertedRoutes {
+ size_t n;
+ Route **routes;
+ Link **links;
+} ConvertedRoutes;
-static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) {
- _cleanup_(route_freep) Route *tmp = NULL;
- Route *route;
- int r;
+static ConvertedRoutes *converted_routes_free(ConvertedRoutes *c) {
+ if (!c)
+ return NULL;
- assert(manager || link);
- assert(in);
+ for (size_t i = 0; i < c->n; i++)
+ route_free(c->routes[i]);
- if (nh) {
- assert(hashmap_isempty(nh->group));
+ free(c->routes);
+ free(c->links);
- r = route_new(&tmp);
- if (r < 0)
- return r;
+ return mfree(c);
+}
- route_copy(tmp, in, NULL, nh, nh_weight);
- in = tmp;
- } else if (m) {
- assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex));
+DEFINE_TRIVIAL_CLEANUP_FUNC(ConvertedRoutes*, converted_routes_free);
- r = route_new(&tmp);
- if (r < 0)
- return r;
+static int converted_routes_new(size_t n, ConvertedRoutes **ret) {
+ _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
+ _cleanup_free_ Route **routes = NULL;
+ _cleanup_free_ Link **links = NULL;
- route_copy(tmp, in, m, NULL, UINT8_MAX);
- in = tmp;
- }
+ assert(n > 0);
+ assert(ret);
- r = route_get(manager, link, in, &route);
- if (r == -ENOENT) {
- /* Route does not exist, create a new one */
- r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, &route);
- if (r < 0)
- return r;
- } else if (r == 0) {
- /* Take over a foreign route */
- r = set_ensure_put(link ? &link->routes : &manager->routes, &route_hash_ops, route);
- if (r < 0)
- return r;
+ routes = new0(Route*, n);
+ if (!routes)
+ return -ENOMEM;
- set_remove(link ? link->routes_foreign : manager->routes_foreign, route);
- } else if (r == 1) {
- /* Route exists, do nothing */
- ;
- } else
- return r;
+ links = new0(Link*, n);
+ if (!links)
+ return -ENOMEM;
- if (ret)
- *ret = route;
- return 0;
-}
+ c = new(ConvertedRoutes, 1);
+ if (!c)
+ return -ENOMEM;
-static bool route_type_is_reject(const Route *route) {
- assert(route);
+ *c = (ConvertedRoutes) {
+ .n = n,
+ .routes = TAKE_PTR(routes),
+ .links = TAKE_PTR(links),
+ };
- return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
+ *ret = TAKE_PTR(c);
+ return 0;
}
-static int link_has_route_one(Link *link, const Route *route, const NextHop *nh, uint8_t nh_weight) {
- _cleanup_(route_freep) Route *tmp = NULL;
+static int route_convert(Manager *manager, const Route *route, ConvertedRoutes **ret) {
+ _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
int r;
- assert(link);
+ assert(manager);
assert(route);
- assert(nh);
+ assert(ret);
- r = route_new(&tmp);
- if (r < 0)
- return r;
+ if (!route_needs_convert(route)) {
+ *ret = NULL;
+ return 0;
+ }
- route_copy(tmp, route, NULL, nh, nh_weight);
+ if (route->nexthop_id > 0) {
+ struct nexthop_grp *nhg;
+ NextHop *nh;
- if (route_type_is_reject(route) || (nh && nh->blackhole))
- return route_get(link->manager, NULL, tmp, NULL) >= 0;
- else
- return route_get(NULL, link, tmp, NULL) >= 0;
-}
+ r = manager_get_nexthop_by_id(manager, route->nexthop_id, &nh);
+ if (r < 0)
+ return r;
-int link_has_route(Link *link, const Route *route) {
- MultipathRoute *m;
- int r;
+ if (hashmap_isempty(nh->group)) {
+ r = converted_routes_new(1, &c);
+ if (r < 0)
+ return r;
- assert(link);
- assert(route);
+ r = route_dup(route, &c->routes[0]);
+ if (r < 0)
+ return r;
- if (route->nexthop_id > 0) {
- struct nexthop_grp *nhg;
- NextHop *nh;
+ route_apply_nexthop(c->routes[0], nh, UINT8_MAX);
+ c->links[0] = nh->link;
- if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
- return false;
+ *ret = TAKE_PTR(c);
+ return 1;
+ }
- if (hashmap_isempty(nh->group))
- return link_has_route_one(link, route, nh, UINT8_MAX);
+ r = converted_routes_new(hashmap_size(nh->group), &c);
+ if (r < 0)
+ return r;
+ size_t i = 0;
HASHMAP_FOREACH(nhg, nh->group) {
NextHop *h;
- if (manager_get_nexthop_by_id(link->manager, nhg->id, &h) < 0)
- return false;
+ r = manager_get_nexthop_by_id(manager, nhg->id, &h);
+ if (r < 0)
+ return r;
- r = link_has_route_one(link, route, h, nhg->weight);
- if (r <= 0)
+ r = route_dup(route, &c->routes[i]);
+ if (r < 0)
return r;
+
+ route_apply_nexthop(c->routes[i], h, nhg->weight);
+ c->links[i] = h->link;
+
+ i++;
}
- return true;
- }
+ *ret = TAKE_PTR(c);
+ return 1;
- if (ordered_set_isempty(route->multipath_routes)) {
- if (route_type_is_reject(route))
- return route_get(link->manager, NULL, route, NULL) >= 0;
- else
- return route_get(NULL, link, route, NULL) >= 0;
}
- ORDERED_SET_FOREACH(m, route->multipath_routes) {
- _cleanup_(route_freep) Route *tmp = NULL;
- Link *l;
-
- if (m->ifname) {
- if (link_get_by_name(link->manager, m->ifname, &l) < 0)
- return false;
+ assert(!ordered_set_isempty(route->multipath_routes));
- m->ifindex = l->ifindex;
- } else
- l = link;
+ r = converted_routes_new(ordered_set_size(route->multipath_routes), &c);
+ if (r < 0)
+ return r;
- r = route_new(&tmp);
+ size_t i = 0;
+ MultipathRoute *m;
+ ORDERED_SET_FOREACH(m, route->multipath_routes) {
+ r = route_dup(route, &c->routes[i]);
if (r < 0)
return r;
- route_copy(tmp, route, m, NULL, UINT8_MAX);
+ route_apply_multipath_route(c->routes[i], m);
- if (route_get(NULL, l, tmp, NULL) < 0)
- return false;
+ r = multipath_route_get_link(manager, m, &c->links[i]);
+ if (r < 0)
+ return r;
+
+ i++;
}
- return true;
+ *ret = TAKE_PTR(c);
+ return 1;
}
-static bool route_address_is_reachable(const Route *route, int family, const union in_addr_union *address) {
- assert(route);
- assert(IN_SET(family, AF_INET, AF_INET6));
- assert(address);
-
- if (route->family != family)
- return false;
-
- if (!in_addr_is_set(route->family, &route->dst))
- return false;
+void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router) {
+ Route *route;
- return in_addr_prefix_intersect(
- route->family,
- &route->dst,
- route->dst_prefixlen,
- address,
- FAMILY_ADDRESS_SIZE(family) * 8) > 0;
-}
+ assert(link);
-static bool prefix_route_address_is_reachable(const Address *a, int family, const union in_addr_union *address) {
- assert(a);
- assert(IN_SET(family, AF_INET, AF_INET6));
- assert(address);
+ SET_FOREACH(route, link->routes) {
+ if (route->source != source)
+ continue;
- if (a->family != family)
- return false;
- if (!address_is_ready(a))
- return false;
- if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
- return false;
- if (in_addr_is_set(a->family, &a->in_addr_peer))
- return false;
+ if (source == NETWORK_CONFIG_SOURCE_NDISC &&
+ router && !in6_addr_equal(router, &route->provider.in6))
+ continue;
- return in_addr_prefix_intersect(
- family,
- &a->in_addr,
- a->prefixlen,
- address,
- FAMILY_ADDRESS_SIZE(family) * 8) > 0;
+ route_mark(route);
+ }
}
static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) {
Route *route;
+ Address *a;
assert(link);
assert(link->manager);
assert(IN_SET(family, AF_INET, AF_INET6));
assert(address);
-
- SET_FOREACH(route, link->routes)
- if (route_address_is_reachable(route, family, address))
- return true;
- SET_FOREACH(route, link->routes_foreign)
- if (route_address_is_reachable(route, family, address))
+ SET_FOREACH(route, link->routes) {
+ if (!route_exists(route))
+ continue;
+ if (route->family != family)
+ continue;
+ if (!in_addr_is_set(route->family, &route->dst))
+ continue;
+ if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) > 0)
return true;
+ }
+
+ if (link->manager->manage_foreign_routes)
+ return false;
/* If we do not manage foreign routes, then there may exist a prefix route we do not know,
* which was created on configuring an address. Hence, also check the addresses. */
- if (!link->manager->manage_foreign_routes) {
- Address *a;
-
- SET_FOREACH(a, link->addresses)
- if (prefix_route_address_is_reachable(a, family, address))
- return true;
- SET_FOREACH(a, link->addresses_foreign)
- if (prefix_route_address_is_reachable(a, family, address))
- return true;
+ SET_FOREACH(a, link->addresses) {
+ if (!address_is_ready(a))
+ continue;
+ if (a->family != family)
+ continue;
+ if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
+ continue;
+ if (in_addr_is_set(a->family, &a->in_addr_peer))
+ continue;
+ if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, address) > 0)
+ return true;
}
return false;
}
-static Route *routes_get_default_gateway(Set *routes, int family, Route *gw) {
+static Route *link_find_default_gateway(Link *link, int family, Route *gw) {
Route *route;
- SET_FOREACH(route, routes) {
+ assert(link);
+
+ SET_FOREACH(route, link->routes) {
+ if (!route_exists(route))
+ continue;
if (family != AF_UNSPEC && route->family != family)
continue;
if (route->dst_prefixlen != 0)
@@ -826,20 +786,22 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) {
if (link->state != LINK_STATE_CONFIGURED)
continue;
- gw = routes_get_default_gateway(link->routes, family, gw);
- gw = routes_get_default_gateway(link->routes_foreign, family, gw);
+ gw = link_find_default_gateway(link, family, gw);
}
if (!gw)
return -ENOENT;
- assert(gw->link);
- *ret = gw->link;
+ if (ret) {
+ assert(gw->link);
+ *ret = gw->link;
+ }
+
return 0;
}
static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) {
- _cleanup_free_ char *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL,
+ _cleanup_free_ char *state = NULL, *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL,
*table = NULL, *scope = NULL, *proto = NULL;
const char *gw = NULL;
@@ -852,6 +814,7 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
if (!DEBUG_LOGGING)
return;
+ (void) network_config_state_to_string_alloc(route->state, &state);
if (in_addr_is_set(route->family, &route->dst))
(void) in_addr_prefix_to_string(route->family, &route->dst, route->dst_prefixlen, &dst);
if (in_addr_is_set(route->family, &route->src))
@@ -889,8 +852,10 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
(void) route_protocol_full_to_string_alloc(route->protocol, &proto);
log_link_debug(link,
- "%s route: dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32,
- str, strna(dst), strna(src), strna(gw), strna(prefsrc),
+ "%s %s route (%s): dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, "
+ "proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32,
+ str, strna(network_config_source_to_string(route->source)), strna(state),
+ strna(dst), strna(src), strna(gw), strna(prefsrc),
strna(scope), strna(table), strna(proto),
strna(route_type_to_string(route->type)),
route->nexthop_id, route->priority);
@@ -1002,16 +967,14 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req
return 0;
}
-static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(m);
- assert(link);
- assert(link->route_remove_messages > 0);
- link->route_remove_messages--;
+ /* link may be NULL. */
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0;
r = sd_netlink_message_get_errno(m);
@@ -1021,32 +984,19 @@ static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Li
return 1;
}
-static int manager_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) {
- int r;
-
- assert(m);
- assert(manager);
- assert(manager->route_remove_messages > 0);
-
- manager->route_remove_messages--;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -ESRCH)
- log_message_warning_errno(m, r, "Could not drop route, ignoring");
-
- return 1;
-}
-
-int route_remove(const Route *route, Manager *manager, Link *link) {
+int route_remove(Route *route) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
unsigned char type;
+ Manager *manager;
+ Link *link;
int r;
- assert(link || manager);
+ assert(route);
+ assert(route->manager || (route->link && route->link->manager));
assert(IN_SET(route->family, AF_INET, AF_INET6));
- if (!manager)
- manager = link->manager;
+ link = route->link;
+ manager = route->manager ?: link->manager;
log_route_debug(route, "Removing", link, manager);
@@ -1075,100 +1025,85 @@ int route_remove(const Route *route, Manager *manager, Link *link) {
if (r < 0)
return r;
- if (link) {
- r = netlink_call_async(manager->rtnl, NULL, req,
- link_route_remove_handler,
- link_netlink_destroy_callback, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
- link_ref(link);
- link->route_remove_messages++;
- } else {
- r = netlink_call_async(manager->rtnl, NULL, req,
- manager_route_remove_handler,
- NULL, manager);
- if (r < 0)
- return log_error_errno(r, "Could not send rtnetlink message: %m");
+ r = netlink_call_async(manager->rtnl, NULL, req, route_remove_handler,
+ link ? link_netlink_destroy_callback : NULL, link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
- manager->route_remove_messages++;
- }
+ link_ref(link);
+ route_enter_removing(route);
return 0;
}
-static bool link_has_static_route(const Link *link, const Route *route) {
- Route *net_route;
-
- assert(link);
- assert(route);
+static int manager_drop_routes(Manager *manager, bool foreign, const Link *except) {
+ Route *route;
+ Link *link;
+ int k, r;
- if (!link->network)
- return false;
+ assert(manager);
- HASHMAP_FOREACH(net_route, link->network->routes_by_section)
- if (route_equal(net_route, route))
- return true;
+ /* First, mark all routes. */
+ SET_FOREACH(route, manager->routes) {
+ /* Do not touch routes managed by the kernel. */
+ if (route->protocol == RTPROT_KERNEL)
+ continue;
- return false;
-}
+ /* When 'foreign' is true, do not remove routes we configured. */
+ if (foreign && route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
+ continue;
-static bool links_have_static_route(const Manager *manager, const Route *route, const Link *except) {
- Link *link;
+ /* Ignore routes not assigned yet or already removed. */
+ if (!route_exists(route))
+ continue;
- assert(manager);
+ route_mark(route);
+ }
+ /* Then, unmark all routes requested by active links. */
HASHMAP_FOREACH(link, manager->links_by_index) {
if (link == except)
continue;
- if (link_has_static_route(link, route))
- return true;
- }
-
- return false;
-}
+ if (!link->network)
+ continue;
-static int manager_drop_routes_internal(Manager *manager, bool foreign, const Link *except) {
- Route *route;
- int k, r = 0;
- Set *routes;
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ continue;
- assert(manager);
+ HASHMAP_FOREACH(route, link->network->routes_by_section) {
+ _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
+ Route *existing;
- routes = foreign ? manager->routes_foreign : manager->routes;
- SET_FOREACH(route, routes) {
- if (route->removing)
- continue;
+ r = route_convert(manager, route, &converted);
+ if (r < 0)
+ continue;
+ if (r == 0) {
+ if (route_get(manager, NULL, route, &existing) >= 0)
+ route_unmark(existing);
+ continue;
+ }
- /* Do not touch routes managed by the kernel. */
- if (route->protocol == RTPROT_KERNEL)
- continue;
+ for (size_t i = 0; i < converted->n; i++)
+ if (route_get(manager, NULL, converted->routes[i], &existing) >= 0)
+ route_unmark(existing);
+ }
+ }
- /* The route will be configured later, or already configured by a link. */
- if (links_have_static_route(manager, route, except))
+ /* Finally, remove all marked routes. */
+ r = 0;
+ SET_FOREACH(route, manager->routes) {
+ if (!route_is_marked(route))
continue;
- /* The existing links do not have the route. Let's drop this now. It may be
- * re-configured later. */
- k = route_remove(route, manager, NULL);
+ k = route_remove(route);
if (k < 0 && r >= 0)
r = k;
-
- route->removing = true;
}
return r;
}
-static int manager_drop_foreign_routes(Manager *manager) {
- return manager_drop_routes_internal(manager, true, NULL);
-}
-
-static int manager_drop_routes(Manager *manager, const Link *except) {
- return manager_drop_routes_internal(manager, false, except);
-}
-
static bool route_by_kernel(const Route *route) {
assert(route);
@@ -1189,16 +1124,24 @@ static bool route_by_kernel(const Route *route) {
int link_drop_foreign_routes(Link *link) {
Route *route;
- int k, r = 0;
+ int k, r;
assert(link);
assert(link->manager);
- SET_FOREACH(route, link->routes_foreign) {
+ SET_FOREACH(route, link->routes) {
/* do not touch routes managed by the kernel */
if (route_by_kernel(route))
continue;
+ /* Do not remove routes we configured. */
+ if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
+ continue;
+
+ /* Ignore routes not assigned yet or already removed. */
+ if (!route_exists(route))
+ continue;
+
if (route->protocol == RTPROT_STATIC && link->network &&
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
continue;
@@ -1207,15 +1150,38 @@ int link_drop_foreign_routes(Link *link) {
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
continue;
- if (link_has_static_route(link, route))
- k = route_add(NULL, link, route, NULL, NULL, UINT8_MAX, NULL);
- else
- k = route_remove(route, NULL, link);
+ route_mark(route);
+ }
+
+ HASHMAP_FOREACH(route, link->network->routes_by_section) {
+ _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
+ Route *existing;
+
+ r = route_convert(link->manager, route, &converted);
+ if (r < 0)
+ continue;
+ if (r == 0) {
+ if (route_get(NULL, link, route, &existing) >= 0)
+ route_unmark(existing);
+ continue;
+ }
+
+ for (size_t i = 0; i < converted->n; i++)
+ if (route_get(NULL, link, converted->routes[i], &existing) >= 0)
+ route_unmark(existing);
+ }
+
+ r = 0;
+ SET_FOREACH(route, link->routes) {
+ if (!route_is_marked(route))
+ continue;
+
+ k = route_remove(route);
if (k < 0 && r >= 0)
r = k;
}
- k = manager_drop_foreign_routes(link->manager);
+ k = manager_drop_routes(link->manager, /* foreign = */ true, NULL);
if (k < 0 && r >= 0)
r = k;
@@ -1230,15 +1196,18 @@ int link_drop_routes(Link *link) {
SET_FOREACH(route, link->routes) {
/* do not touch routes managed by the kernel */
- if (route->protocol == RTPROT_KERNEL)
+ if (route_by_kernel(route))
+ continue;
+
+ if (!route_exists(route))
continue;
- k = route_remove(route, NULL, link);
+ k = route_remove(route);
if (k < 0 && r >= 0)
r = k;
}
- k = manager_drop_routes(link->manager, link);
+ k = manager_drop_routes(link->manager, /* foreign = */ false, link);
if (k < 0 && r >= 0)
r = k;
@@ -1250,131 +1219,37 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
int r;
assert(route);
+ assert(route->link);
- r = route_remove(route, route->manager, route->link);
+ r = route_remove(route);
if (r < 0) {
log_link_warning_errno(route->link, r, "Could not remove route: %m");
- route_free(route);
+ link_enter_failed(route->link);
}
return 1;
}
-static int route_add_and_setup_timer_one(Link *link, const Route *route, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) {
- _cleanup_(sd_event_source_disable_unrefp) sd_event_source *expire = NULL;
- Route *nr;
- int r;
-
- assert(link);
- assert(link->manager);
- assert(route);
- assert(!(m && nh));
- assert(ret);
-
- if (route_type_is_reject(route) || (nh && nh->blackhole))
- r = route_add(link->manager, NULL, route, NULL, nh, nh_weight, &nr);
- else if (nh) {
- assert(nh->link);
- assert(hashmap_isempty(nh->group));
-
- r = route_add(NULL, nh->link, route, NULL, nh, nh_weight, &nr);
- } else if (m && m->ifindex != 0 && m->ifindex != link->ifindex) {
- Link *link_gw;
-
- r = link_get_by_index(link->manager, m->ifindex, &link_gw);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
-
- r = route_add(NULL, link_gw, route, m, NULL, UINT8_MAX, &nr);
- } else
- r = route_add(NULL, link, route, m, NULL, UINT8_MAX, &nr);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not add route: %m");
+static int route_setup_timer(Route *route) {
+ Manager *manager;
/* TODO: drop expiration handling once it can be pushed into the kernel */
- if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
- r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
- nr->lifetime, 0, route_expire_handler, nr);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
- }
-
- sd_event_source_disable_unref(nr->expire);
- nr->expire = TAKE_PTR(expire);
-
- *ret = nr;
- return 0;
-}
-static int route_add_and_setup_timer(Link *link, const Route *route, unsigned *ret_n_routes, Route ***ret_routes) {
- _cleanup_free_ Route **routes = NULL;
- unsigned n_routes;
- NextHop *nh;
- Route **p;
- int r;
-
- assert(link);
assert(route);
- assert(ret_n_routes);
- assert(ret_routes);
+ assert(route->manager || (route->link && route->link->manager));
- if (route->nexthop_id > 0) {
- r = manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not get nexthop by ID %"PRIu32": %m", route->nexthop_id);
- } else
- nh = NULL;
+ manager = route->manager ?: route->link->manager;
- if (nh && !hashmap_isempty(nh->group)) {
- struct nexthop_grp *nhg;
-
- n_routes = hashmap_size(nh->group);
- p = routes = new(Route*, n_routes);
- if (!routes)
- return log_oom();
-
- HASHMAP_FOREACH(nhg, nh->group) {
- NextHop *h;
-
- r = manager_get_nexthop_by_id(link->manager, nhg->id, &h);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not get nexthop group member by ID %"PRIu32": %m", nhg->id);
-
- /* The nexthop h may be a blackhole nexthop. In that case, h->link is NULL. */
- r = route_add_and_setup_timer_one(h->link ?: link, route, NULL, h, nhg->weight, p++);
- if (r < 0)
- return r;
- }
- } else if (!ordered_set_isempty(route->multipath_routes)) {
- MultipathRoute *m;
-
- assert(!nh);
- assert(!in_addr_is_set(route->gw_family, &route->gw));
-
- n_routes = ordered_set_size(route->multipath_routes);
- p = routes = new(Route*, n_routes);
- if (!routes)
- return log_oom();
+ if (route->lifetime == USEC_INFINITY)
+ return 0;
- ORDERED_SET_FOREACH(m, route->multipath_routes) {
- r = route_add_and_setup_timer_one(link, route, m, NULL, UINT8_MAX, p++);
- if (r < 0)
- return r;
- }
- } else {
- n_routes = 1;
- routes = new(Route*, n_routes);
- if (!routes)
- return log_oom();
+ if (kernel_route_expiration_supported())
+ return 0;
- r = route_add_and_setup_timer_one(link, route, NULL, nh, UINT8_MAX, routes);
- if (r < 0)
- return r;
- }
+ sd_event_source_disable_unref(route->expire);
- *ret_n_routes = n_routes;
- *ret_routes = TAKE_PTR(routes);
- return 0;
+ return sd_event_add_time(manager->event, &route->expire, clock_boottime_or_monotonic(),
+ route->lifetime, 0, route_expire_handler, route);
}
static int append_nexthop_one(const Link *link, const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
@@ -1485,27 +1360,18 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li
static int route_configure(
const Route *route,
Link *link,
- link_netlink_message_handler_t callback,
- unsigned *ret_n_routes,
- Route ***ret_routes) {
+ link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
- _cleanup_free_ Route **routes = NULL;
- unsigned n_routes = 0; /* avoid false maybe-uninitialized warning */
int r;
+ assert(route);
+ assert(IN_SET(route->family, AF_INET, AF_INET6));
assert(link);
assert(link->manager);
assert(link->manager->rtnl);
assert(link->ifindex > 0);
- assert(IN_SET(route->family, AF_INET, AF_INET6));
assert(callback);
- assert(!!ret_n_routes == !!ret_routes);
-
- if (route_get(link->manager, link, route, NULL) <= 0 &&
- set_size(link->routes) >= routes_max())
- return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
- "Too many routes are configured, refusing: %m");
log_route_debug(route, "Configuring", link, link->manager);
@@ -1589,23 +1455,34 @@ static int route_configure(
return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
}
- r = route_add_and_setup_timer(link, route, &n_routes, &routes);
- if (r < 0)
- return r;
-
r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
+ return 0;
+}
- if (ret_routes) {
- *ret_n_routes = n_routes;
- *ret_routes = TAKE_PTR(routes);
- }
+void route_cancel_request(Route *route) {
+ Request req;
- return r;
+ assert(route);
+
+ if (!route_is_requesting(route))
+ return;
+
+ if (!route->link)
+ return;
+
+ req = (Request) {
+ .link = route->link,
+ .type = REQUEST_TYPE_ROUTE,
+ .route = route,
+ };
+
+ request_drop(ordered_set_get(route->link->manager->request_queue, &req));
+ route_cancel_requesting(route);
}
static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -1637,13 +1514,60 @@ int link_request_route(
link_netlink_message_handler_t netlink_handler,
Request **ret) {
+ Route *existing;
+ int r;
+
assert(link);
assert(link->manager);
assert(route);
+ assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
+ assert(!route_needs_convert(route));
+
+ if (route_get(link->manager, link, route, &existing) < 0) {
+ _cleanup_(route_freep) Route *tmp = NULL;
+
+ if (consume_object)
+ tmp = route;
+ else {
+ r = route_dup(route, &tmp);
+ if (r < 0)
+ return r;
+ }
+
+ r = route_add(link->manager, link, tmp);
+ if (r < 0)
+ return r;
+
+ existing = TAKE_PTR(tmp);
+ } else {
+ existing->source = route->source;
+ existing->provider = route->provider;
+ if (consume_object)
+ route_free(route);
+ }
+
+ log_route_debug(existing, "Requesting", link, link->manager);
+ r = link_queue_request(link, REQUEST_TYPE_ROUTE, existing, false,
+ message_counter, netlink_handler, ret);
+ if (r <= 0)
+ return r;
+
+ route_enter_requesting(existing);
+ return 1;
+}
+
+static int link_request_static_route(Link *link, Route *route) {
+ assert(link);
+ assert(link->manager);
+ assert(route);
+
+ if (!route_needs_convert(route))
+ return link_request_route(link, route, false, &link->static_route_messages,
+ static_route_handler, NULL);
log_route_debug(route, "Requesting", link, link->manager);
- return link_queue_request(link, REQUEST_TYPE_ROUTE, route, consume_object,
- message_counter, netlink_handler, ret);
+ return link_queue_request(link, REQUEST_TYPE_ROUTE, route, false,
+ &link->static_route_messages, static_route_handler, NULL);
}
int link_request_static_routes(Link *link, bool only_ipv4) {
@@ -1662,8 +1586,7 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
if (only_ipv4 && route->family != AF_INET)
continue;
- r = link_request_route(link, route, false, &link->static_route_messages,
- static_route_handler, NULL);
+ r = link_request_static_route(link, route);
if (r < 0)
return r;
}
@@ -1696,15 +1619,20 @@ bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_un
}
static int route_is_ready_to_configure(const Route *route, Link *link) {
- MultipathRoute *m;
- NextHop *nh = NULL;
int r;
assert(route);
assert(link);
+ if (!link_is_ready_to_configure(link, false))
+ return false;
+
+ if (set_size(link->routes) >= routes_max())
+ return false;
+
if (route->nexthop_id > 0) {
struct nexthop_grp *nhg;
+ NextHop *nh;
if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
return false;
@@ -1723,20 +1651,6 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
}
}
- if (route_type_is_reject(route)) {
- if (link->manager->route_remove_messages > 0)
- return false;
- } else {
- Link *l;
-
- HASHMAP_FOREACH(l, link->manager->links_by_index) {
- if (l->address_remove_messages > 0)
- return false;
- if (l->route_remove_messages > 0)
- return false;
- }
- }
-
if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
r = manager_has_address(link->manager, route->family, &route->prefsrc, route->family == AF_INET6);
if (r <= 0)
@@ -1746,21 +1660,20 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
if (!gateway_is_ready(link, route->gateway_onlink, route->gw_family, &route->gw))
return false;
+ MultipathRoute *m;
ORDERED_SET_FOREACH(m, route->multipath_routes) {
union in_addr_union a = m->gateway.address;
Link *l = NULL;
- if (m->ifname) {
- if (link_get_by_name(link->manager, m->ifname, &l) < 0)
+ r = multipath_route_get_link(link->manager, m, &l);
+ if (r < 0)
+ return false;
+ if (r > 0) {
+ if (!link_is_ready_to_configure(l, true))
return false;
m->ifindex = l->ifindex;
- } else if (m->ifindex > 0) {
- if (link_get_by_index(link->manager, m->ifindex, &l) < 0)
- return false;
}
- if (l && !link_is_ready_to_configure(l, true))
- return false;
if (!gateway_is_ready(l ?: link, route->gateway_onlink, m->gateway.family, &a))
return false;
@@ -1770,8 +1683,9 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
}
int request_process_route(Request *req) {
- _cleanup_free_ Route **routes = NULL;
- unsigned n_routes;
+ _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
+ Route *route;
+ Link *link;
int r;
assert(req);
@@ -1779,113 +1693,116 @@ int request_process_route(Request *req) {
assert(req->route);
assert(req->type == REQUEST_TYPE_ROUTE);
- if (!link_is_ready_to_configure(req->link, false))
+ link = req->link;
+ route = req->route;
+
+ r = route_is_ready_to_configure(route, link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m");
+ if (r == 0)
return 0;
- r = route_is_ready_to_configure(req->route, req->link);
- if (r <= 0)
- return r;
+ if (route_needs_convert(route)) {
+ r = route_convert(link->manager, route, &converted);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to convert route: %m");
- r = route_configure(req->route, req->link, req->netlink_handler,
- req->after_configure ? &n_routes : NULL,
- req->after_configure ? &routes : NULL);
- if (r < 0)
- return r;
+ assert(r > 0);
+ assert(converted);
- /* To prevent a double decrement on failure in after_configure(). */
- req->message_counter = NULL;
+ for (size_t i = 0; i < converted->n; i++) {
+ Route *existing;
- if (req->after_configure) {
- assert(n_routes > 0);
+ if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) {
+ _cleanup_(route_freep) Route *tmp = NULL;
- for (unsigned i = 0; i < n_routes; i++) {
- r = req->after_configure(req, routes[i]);
- if (r < 0)
- return r;
+ r = route_dup(converted->routes[i], &tmp);
+ if (r < 0)
+ return log_oom();
+
+ r = route_add(link->manager, converted->links[i] ?: link, tmp);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to add route: %m");
+
+ TAKE_PTR(tmp);
+ } else {
+ existing->source = converted->routes[i]->source;
+ existing->provider = converted->routes[i]->provider;
+ }
}
+ } else {
+ r = route_setup_timer(route);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to setup timer for route: %m");
}
+ r = route_configure(route, link, req->netlink_handler);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure route: %m");
+
+ if (converted)
+ for (size_t i = 0; i < converted->n; i++) {
+ Route *existing;
+
+ assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0);
+ route_enter_configuring(existing);
+ }
+ else
+ route_enter_configuring(route);
+
return 1;
}
-static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
- _cleanup_(route_freep) Route *nr = NULL;
+static int process_route_one(Manager *manager, Link *link, uint16_t type, Route *in) {
+ _cleanup_(route_freep) Route *tmp = in;
Route *route = NULL;
- NextHop *nh = NULL;
int r;
assert(manager);
assert(tmp);
assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
- (void) manager_get_nexthop_by_id(manager, tmp->nexthop_id, &nh);
-
- if (nh && hashmap_isempty(nh->group)) {
- if (link && nh->link && link != nh->link)
- return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
- "rtnl: received RTA_OIF and ifindex of nexthop corresponding to RTA_NH_ID do not match, ignoring.");
-
- if (nh->link)
- link = nh->link;
-
- r = route_new(&nr);
- if (r < 0)
- return log_oom();
-
- route_copy(nr, tmp, NULL, nh, UINT8_MAX);
-
- tmp = nr;
- } else if (m) {
- if (link)
- return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
- "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
-
- if (m->ifindex <= 0)
- return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "rtnl: received multipath route with invalid ifindex, ignoring.");
-
- r = link_get_by_index(manager, m->ifindex, &link);
- if (r < 0) {
- log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex);
- return 0;
- }
-
- r = route_new(&nr);
- if (r < 0)
- return log_oom();
-
- route_copy(nr, tmp, m, NULL, UINT8_MAX);
-
- tmp = nr;
- }
+ /* link may be NULL. This consumes 'in'. */
(void) route_get(manager, link, tmp, &route);
switch (type) {
case RTM_NEWROUTE:
- if (!route) {
- if (!manager->manage_foreign_routes)
- log_route_debug(tmp, "Ignoring received foreign", link, manager);
- else {
- /* A route appeared that we did not request */
- log_route_debug(tmp, "Remembering foreign", link, manager);
- r = route_add_foreign(manager, link, tmp, NULL);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
- return 0;
- }
+ if (route) {
+ route_enter_configured(route);
+ log_route_debug(route, "Received remembered", link, manager);
+
+ } else if (!manager->manage_foreign_routes) {
+ route_enter_configured(tmp);
+ log_route_debug(tmp, "Ignoring received", link, manager);
+
+ } else {
+ /* A route appeared that we did not request */
+ route_enter_configured(tmp);
+ log_route_debug(tmp, "Received new", link, manager);
+ r = route_add(manager, link, tmp);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
+ return 0;
}
- } else
- log_route_debug(tmp, "Received remembered", link, manager);
+ TAKE_PTR(tmp);
+ }
break;
case RTM_DELROUTE:
- log_route_debug(tmp,
- route ? "Forgetting" :
- manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received foreign",
- link, manager);
- route_free(route);
+ if (route) {
+ route_enter_removed(route);
+ if (route->state == 0) {
+ log_route_debug(route, "Forgetting", link, manager);
+ route_free(route);
+ } else
+ log_route_debug(route, "Removed", link, manager);
+ } else
+ log_route_debug(tmp,
+ manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
+ link, manager);
+
break;
default:
@@ -1896,7 +1813,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const
}
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
- _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
+ _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
_cleanup_(route_freep) Route *tmp = NULL;
_cleanup_free_ void *rta_multipath = NULL;
Link *link = NULL;
@@ -2088,7 +2005,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
return 0;
} else if (r >= 0) {
- r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &multipath_routes);
+ r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &tmp->multipath_routes);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
return 0;
@@ -2101,18 +2018,21 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
if (route_type_is_reject(tmp))
link = NULL;
- if (ordered_set_isempty(multipath_routes))
- (void) process_route_one(m, link, type, tmp, NULL);
- else {
- MultipathRoute *mr;
+ if (!route_needs_convert(tmp))
+ return process_route_one(m, link, type, TAKE_PTR(tmp));
- ORDERED_SET_FOREACH(mr, multipath_routes) {
- r = process_route_one(m, link, type, tmp, mr);
- if (r < 0)
- break;
- }
+ r = route_convert(m, tmp, &converted);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: failed to convert received route, ignoring: %m");
+ return 0;
}
+ assert(r > 0);
+ assert(converted);
+
+ for (size_t i = 0; i < converted->n; i++)
+ (void) process_route_one(m, converted->links[i] ?: link, type, TAKE_PTR(converted->routes[i]));
+
return 1;
}
@@ -3094,6 +3014,9 @@ static int route_section_verify(Route *route, Network *network) {
if (section_is_invalid(route->section))
return -EINVAL;
+ /* Currently, we do not support static route with finite lifetime. */
+ assert(route->lifetime == USEC_INFINITY);
+
if (route->gateway_from_dhcp_or_ra) {
if (route->gw_family == AF_UNSPEC) {
/* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */