Merge pull request #31009 from yuwata/network-route-convert-before-requesting

network/route: convert routes before requesting
This commit is contained in:
Luca Boccassi 2024-01-19 13:20:32 +00:00 committed by GitHub
commit b26be60efc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 338 additions and 507 deletions

View file

@ -309,8 +309,7 @@ static int dhcp_pd_request_route(Link *link, const struct in6_addr *prefix, usec
else
route_unmark(existing);
r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp_pd_messages,
dhcp_pd_route_handler, NULL);
r = link_request_route(link, route, &link->dhcp_pd_messages, dhcp_pd_route_handler);
if (r < 0)
return log_link_error_errno(link, r, "Failed to request DHCP-PD prefix route: %m");
@ -708,7 +707,7 @@ static int dhcp_request_unreachable_route(
else
route_unmark(existing);
r = link_request_route(link, TAKE_PTR(route), true, counter, callback, NULL);
r = link_request_route(link, route, counter, callback);
if (r < 0)
return log_link_error_errno(link, r, "Failed to request unreachable route for DHCP delegated prefix %s: %m",
IN6_ADDR_PREFIX_TO_STRING(addr, prefixlen));
@ -806,8 +805,7 @@ static int dhcp4_pd_request_default_gateway_on_6rd_tunnel(Link *link, const stru
else
route_unmark(existing);
r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp_pd_messages,
dhcp_pd_route_handler, NULL);
r = link_request_route(link, route, &link->dhcp_pd_messages, dhcp_pd_route_handler);
if (r < 0)
return log_link_debug_errno(link, r, "Failed to request default gateway for DHCP delegated prefix: %m");

View file

@ -353,8 +353,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
return 1;
}
static int dhcp4_request_route(Route *in, Link *link) {
_cleanup_(route_freep) Route *route = in;
static int dhcp4_request_route(Route *route, Link *link) {
struct in_addr server;
Route *existing;
int r;
@ -399,8 +398,7 @@ static int dhcp4_request_route(Route *in, Link *link) {
else
route_unmark(existing);
return link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
dhcp4_route_handler, NULL);
return link_request_route(link, route, &link->dhcp4_messages, dhcp4_route_handler);
}
static bool link_prefixroute(Link *link) {
@ -433,7 +431,7 @@ static int dhcp4_request_prefix_route(Link *link) {
if (r < 0)
return r;
return dhcp4_request_route(TAKE_PTR(route), link);
return dhcp4_request_route(route, link);
}
static int dhcp4_request_route_to_gateway(Link *link, const struct in_addr *gw) {
@ -458,15 +456,14 @@ static int dhcp4_request_route_to_gateway(Link *link, const struct in_addr *gw)
route->prefsrc.in = address;
route->scope = RT_SCOPE_LINK;
return dhcp4_request_route(TAKE_PTR(route), link);
return dhcp4_request_route(route, link);
}
static int dhcp4_request_route_auto(
Route *in,
Route *route,
Link *link,
const struct in_addr *gw) {
_cleanup_(route_freep) Route *route = in;
struct in_addr address;
int r;
@ -535,7 +532,7 @@ static int dhcp4_request_route_auto(
route->prefsrc.in = address;
}
return dhcp4_request_route(TAKE_PTR(route), link);
return dhcp4_request_route(route, link);
}
static int dhcp4_request_classless_static_or_static_routes(Link *link) {
@ -575,7 +572,7 @@ static int dhcp4_request_classless_static_or_static_routes(Link *link) {
if (r < 0)
return r;
r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw);
r = dhcp4_request_route_auto(route, link, &gw);
if (r < 0)
return r;
}
@ -627,7 +624,7 @@ static int dhcp4_request_default_gateway(Link *link) {
route->nexthop.gw.in = router;
route->prefsrc.in = address;
return dhcp4_request_route(TAKE_PTR(route), link);
return dhcp4_request_route(route, link);
}
static int dhcp4_request_semi_static_routes(Link *link) {
@ -669,7 +666,7 @@ static int dhcp4_request_semi_static_routes(Link *link) {
route->nexthop.gw.in = gw;
r = dhcp4_request_route(TAKE_PTR(route), link);
r = dhcp4_request_route(route, link);
if (r < 0)
return r;
}
@ -712,7 +709,7 @@ static int dhcp4_request_routes_to_servers(
route->dst.in = *dst;
route->dst_prefixlen = 32;
r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw);
r = dhcp4_request_route_auto(route, link, &gw);
if (r < 0)
return r;
}

View file

@ -172,8 +172,7 @@ static void ndisc_set_route_priority(Link *link, Route *route) {
}
}
static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = in;
static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
struct in6_addr router;
uint8_t hop_limit = 0;
uint32_t mtu = 0;
@ -224,8 +223,7 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
is_new = route_get(NULL, link, route, NULL) < 0;
r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages,
ndisc_route_handler, NULL);
r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
if (r < 0)
return r;
if (r > 0 && is_new)
@ -329,7 +327,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
route->nexthop.gw.in6 = gateway;
route->lifetime_usec = lifetime_usec;
r = ndisc_request_route(TAKE_PTR(route), link, rt);
r = ndisc_request_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request default route: %m");
}
@ -353,7 +351,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
route->pref = preference;
route->lifetime_usec = lifetime_usec;
r = ndisc_request_route(TAKE_PTR(route), link, rt);
r = ndisc_request_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request gateway: %m");
}
@ -503,7 +501,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
route->pref = preference;
route->lifetime_usec = lifetime_usec;
r = ndisc_request_route(TAKE_PTR(route), link, rt);
r = ndisc_request_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request prefix route: %m");
@ -636,7 +634,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
route->dst_prefixlen = prefixlen;
route->lifetime_usec = lifetime_usec;
r = ndisc_request_route(TAKE_PTR(route), link, rt);
r = ndisc_request_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request additional route: %m");

View file

@ -58,7 +58,7 @@ static void request_hash_func(const Request *req, struct siphash *state) {
siphash24_compress_typesafe(req->type, state);
if (req->type != REQUEST_TYPE_NEXTHOP) {
if (!IN_SET(req->type, REQUEST_TYPE_NEXTHOP, REQUEST_TYPE_ROUTE)) {
siphash24_compress_boolean(req->link, state);
if (req->link)
siphash24_compress_typesafe(req->link->ifindex, state);
@ -81,7 +81,7 @@ static int request_compare_func(const struct Request *a, const struct Request *b
if (r != 0)
return r;
if (a->type != REQUEST_TYPE_NEXTHOP) {
if (!IN_SET(a->type, REQUEST_TYPE_NEXTHOP, REQUEST_TYPE_ROUTE)) {
r = CMP(!!a->link, !!b->link);
if (r != 0)
return r;

View file

@ -35,7 +35,7 @@ void route_nexthops_done(Route *route) {
ordered_set_free(route->nexthops);
}
static void route_nexthop_hash_func_full(const RouteNextHop *nh, struct siphash *state, bool hash_all_parameters) {
static void route_nexthop_hash_func_full(const RouteNextHop *nh, struct siphash *state, bool with_weight) {
assert(nh);
assert(state);
@ -46,15 +46,14 @@ static void route_nexthop_hash_func_full(const RouteNextHop *nh, struct siphash
return;
in_addr_hash_func(&nh->gw, nh->family, state);
if (!hash_all_parameters)
return;
siphash24_compress_typesafe(nh->weight, state);
if (with_weight)
siphash24_compress_typesafe(nh->weight, state);
siphash24_compress_typesafe(nh->ifindex, state);
if (nh->ifindex == 0)
siphash24_compress_string(nh->ifname, state); /* For Network or Request object. */
}
static int route_nexthop_compare_func_full(const RouteNextHop *a, const RouteNextHop *b, bool hash_all_parameters) {
static int route_nexthop_compare_func_full(const RouteNextHop *a, const RouteNextHop *b, bool with_weight) {
int r;
assert(a);
@ -71,12 +70,11 @@ static int route_nexthop_compare_func_full(const RouteNextHop *a, const RouteNex
if (r != 0)
return r;
if (!hash_all_parameters)
return 0;
r = CMP(a->weight, b->weight);
if (r != 0)
return r;
if (with_weight) {
r = CMP(a->weight, b->weight);
if (r != 0)
return r;
}
r = CMP(a->ifindex, b->ifindex);
if (r != 0)
@ -92,11 +90,11 @@ static int route_nexthop_compare_func_full(const RouteNextHop *a, const RouteNex
}
static void route_nexthop_hash_func(const RouteNextHop *nh, struct siphash *state) {
route_nexthop_hash_func_full(nh, state, /* hash_all_parameters = */ true);
route_nexthop_hash_func_full(nh, state, /* with_weight = */ true);
}
static int route_nexthop_compare_func(const RouteNextHop *a, const RouteNextHop *b) {
return route_nexthop_compare_func_full(a, b, /* hash_all_parameters = */ true);
return route_nexthop_compare_func_full(a, b, /* with_weight = */ true);
}
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
@ -107,8 +105,6 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
route_nexthop_free);
static size_t route_n_nexthops(const Route *route) {
assert(route);
if (route->nexthop_id != 0 || route_type_is_reject(route))
return 0;
@ -130,7 +126,7 @@ void route_nexthops_hash_func(const Route *route, struct siphash *state) {
return;
case 1:
route_nexthop_hash_func_full(&route->nexthop, state, /* hash_all_parameters = */ false);
route_nexthop_hash_func_full(&route->nexthop, state, /* with_weight = */ false);
return;
default: {
@ -157,7 +153,7 @@ int route_nexthops_compare_func(const Route *a, const Route *b) {
return CMP(a->nexthop_id, b->nexthop_id);
case 1:
return route_nexthop_compare_func_full(&a->nexthop, &b->nexthop, /* hash_all_parameters = */ false);
return route_nexthop_compare_func_full(&a->nexthop, &b->nexthop, /* with_weight = */ false);
default: {
RouteNextHop *nh;
@ -179,10 +175,31 @@ static int route_nexthop_copy(const RouteNextHop *src, RouteNextHop *dest) {
/* unset pointer copied in the above. */
dest->ifname = NULL;
return strdup_or_null(src->ifindex == 0 ? NULL : src->ifname, &dest->ifname);
return strdup_or_null(src->ifindex > 0 ? NULL : src->ifname, &dest->ifname);
}
static int route_nexthop_dup(const RouteNextHop *src, RouteNextHop **ret) {
_cleanup_(route_nexthop_freep) RouteNextHop *dest = NULL;
int r;
assert(src);
assert(ret);
dest = new(RouteNextHop, 1);
if (!dest)
return -ENOMEM;
r = route_nexthop_copy(src, dest);
if (r < 0)
return r;
*ret = TAKE_PTR(dest);
return 0;
}
int route_nexthops_copy(const Route *src, const RouteNextHop *nh, Route *dest) {
int r;
assert(src);
assert(dest);
@ -195,7 +212,20 @@ int route_nexthops_copy(const Route *src, const RouteNextHop *nh, Route *dest) {
if (ordered_set_isempty(src->nexthops))
return route_nexthop_copy(&src->nexthop, &dest->nexthop);
/* Currently, this does not copy multipath routes. */
ORDERED_SET_FOREACH(nh, src->nexthops) {
_cleanup_(route_nexthop_freep) RouteNextHop *nh_dup = NULL;
r = route_nexthop_dup(nh, &nh_dup);
if (r < 0)
return r;
r = ordered_set_ensure_put(&dest->nexthops, &route_nexthop_hash_ops, nh_dup);
if (r < 0)
return r;
assert(r > 0);
TAKE_PTR(nh_dup);
}
return 0;
}
@ -311,7 +341,7 @@ int route_adjust_nexthops(Route *route, Link *link) {
return true; /* updated */
}
int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret) {
int route_nexthop_get_link(Manager *manager, const RouteNextHop *nh, Link **ret) {
assert(manager);
assert(nh);
@ -320,20 +350,16 @@ int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh,
if (nh->ifname)
return link_get_by_name(manager, nh->ifname, ret);
if (link) {
if (ret)
*ret = link;
return 0;
}
return -ENOENT;
}
static bool route_nexthop_is_ready_to_configure(const RouteNextHop *nh, Link *link, bool onlink) {
assert(nh);
assert(link);
static bool route_nexthop_is_ready_to_configure(const RouteNextHop *nh, Manager *manager, bool onlink) {
Link *link;
if (route_nexthop_get_link(link->manager, link, nh, &link))
assert(nh);
assert(manager);
if (route_nexthop_get_link(manager, nh, &link) < 0)
return false;
if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ true))
@ -347,13 +373,11 @@ static bool route_nexthop_is_ready_to_configure(const RouteNextHop *nh, Link *li
return gateway_is_ready(link, onlink, nh->family, &nh->gw);
}
int route_nexthops_is_ready_to_configure(const Route *route, Link *link) {
int route_nexthops_is_ready_to_configure(const Route *route, Manager *manager) {
int r;
assert(route);
assert(link);
Manager *manager = ASSERT_PTR(link->manager);
assert(manager);
if (route->nexthop_id != 0) {
struct nexthop_grp *nhg;
@ -376,11 +400,11 @@ int route_nexthops_is_ready_to_configure(const Route *route, Link *link) {
return true;
if (ordered_set_isempty(route->nexthops))
return route_nexthop_is_ready_to_configure(&route->nexthop, link, FLAGS_SET(route->flags, RTNH_F_ONLINK));
return route_nexthop_is_ready_to_configure(&route->nexthop, manager, FLAGS_SET(route->flags, RTNH_F_ONLINK));
RouteNextHop *nh;
ORDERED_SET_FOREACH(nh, route->nexthops)
if (!route_nexthop_is_ready_to_configure(nh, link, FLAGS_SET(route->flags, RTNH_F_ONLINK)))
if (!route_nexthop_is_ready_to_configure(nh, manager, FLAGS_SET(route->flags, RTNH_F_ONLINK)))
return false;
return true;
@ -451,7 +475,7 @@ int route_nexthops_to_string(const Route *route, char **ret) {
return 0;
}
static int append_nexthop_one(Link *link, const Route *route, const RouteNextHop *nh, struct rtattr **rta, size_t offset) {
static int append_nexthop_one(const Route *route, const RouteNextHop *nh, struct rtattr **rta, size_t offset) {
struct rtnexthop *rtnh;
struct rtattr *new_rta;
int r;
@ -462,15 +486,6 @@ static int append_nexthop_one(Link *link, const Route *route, const RouteNextHop
assert(rta);
assert(*rta);
if (nh->ifindex <= 0) {
assert(link);
assert(link->manager);
r = route_nexthop_get_link(link->manager, link, nh, &link);
if (r < 0)
return r;
}
new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
if (!new_rta)
return -ENOMEM;
@ -479,7 +494,7 @@ static int append_nexthop_one(Link *link, const Route *route, const RouteNextHop
rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
*rtnh = (struct rtnexthop) {
.rtnh_len = sizeof(*rtnh),
.rtnh_ifindex = nh->ifindex > 0 ? nh->ifindex : link->ifindex,
.rtnh_ifindex = nh->ifindex,
.rtnh_hops = nh->weight,
};
@ -517,7 +532,7 @@ clear:
return r;
}
static int netlink_message_append_multipath_route(Link *link, const Route *route, sd_netlink_message *message) {
static int netlink_message_append_multipath_route(const Route *route, sd_netlink_message *message) {
_cleanup_free_ struct rtattr *rta = NULL;
size_t offset;
int r;
@ -536,7 +551,7 @@ static int netlink_message_append_multipath_route(Link *link, const Route *route
offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
if (ordered_set_isempty(route->nexthops)) {
r = append_nexthop_one(link, route, &route->nexthop, &rta, offset);
r = append_nexthop_one(route, &route->nexthop, &rta, offset);
if (r < 0)
return r;
@ -545,7 +560,7 @@ static int netlink_message_append_multipath_route(Link *link, const Route *route
ORDERED_SET_FOREACH(nh, route->nexthops) {
struct rtnexthop *rtnh;
r = append_nexthop_one(link, route, nh, &rta, offset);
r = append_nexthop_one(route, nh, &rta, offset);
if (r < 0)
return r;
@ -557,7 +572,7 @@ static int netlink_message_append_multipath_route(Link *link, const Route *route
return sd_netlink_message_append_data(message, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
}
int route_nexthops_set_netlink_message(Link *link, const Route *route, sd_netlink_message *message) {
int route_nexthops_set_netlink_message(const Route *route, sd_netlink_message *message) {
int r;
assert(route);
@ -589,10 +604,11 @@ int route_nexthops_set_netlink_message(Link *link, const Route *route, sd_netlin
return r;
}
return sd_netlink_message_append_u32(message, RTA_OIF, route->nexthop.ifindex > 0 ? route->nexthop.ifindex : ASSERT_PTR(link)->ifindex);
assert(route->nexthop.ifindex > 0);
return sd_netlink_message_append_u32(message, RTA_OIF, route->nexthop.ifindex);
}
return netlink_message_append_multipath_route(link, route, message);
return netlink_message_append_multipath_route(route, message);
}
static int route_parse_nexthops(Route *route, const struct rtnexthop *rtnh, size_t size) {
@ -857,6 +873,16 @@ int route_section_verify_nexthops(Route *route) {
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (ordered_set_size(route->nexthops) == 1) {
_cleanup_(route_nexthop_freep) RouteNextHop *nh = ordered_set_steal_first(route->nexthops);
route_nexthop_done(&route->nexthop);
route->nexthop = TAKE_STRUCT(*nh);
assert(ordered_set_isempty(route->nexthops));
route->nexthops = ordered_set_free(route->nexthops);
}
return 0;
}

View file

@ -38,12 +38,12 @@ int route_nexthops_copy(const Route *src, const RouteNextHop *nh, Route *dest);
bool route_nexthops_needs_adjust(const Route *route);
int route_adjust_nexthops(Route *route, Link *link);
int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret);
int route_nexthops_is_ready_to_configure(const Route *route, Link *link);
int route_nexthop_get_link(Manager *manager, const RouteNextHop *nh, Link **ret);
int route_nexthops_is_ready_to_configure(const Route *route, Manager *manager);
int route_nexthops_to_string(const Route *route, char **ret);
int route_nexthops_set_netlink_message(Link *link, const Route *route, sd_netlink_message *message);
int route_nexthops_set_netlink_message(const Route *route, sd_netlink_message *message);
int route_nexthops_read_netlink_message(Route *route, sd_netlink_message *message);
int route_section_verify_nexthops(Route *route);

View file

@ -262,24 +262,16 @@ int route_new_static(Network *network, const char *filename, unsigned section_li
return 0;
}
static int route_add(Manager *manager, Link *link, Route *route) {
static int route_add(Manager *manager, Route *route) {
int r;
assert(manager);
assert(route);
assert(!route->network);
assert(!route->wireguard);
if (route_type_is_reject(route)) {
assert(manager);
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);
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;
@ -287,32 +279,35 @@ static int route_add(Manager *manager, Link *link, Route *route) {
return -EEXIST;
route->link = link;
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;
return 0;
}
int route_get(Manager *manager, Link *link, const Route *in, Route **ret) {
Route *route;
int route_get(Manager *manager, Link *link, const Route *route, Route **ret) {
Route *existing;
assert(in);
if (!manager)
manager = ASSERT_PTR(ASSERT_PTR(link)->manager);
assert(route);
if (route_type_is_reject(in)) {
if (!manager)
return -ENOENT;
route = set_get(manager->routes, in);
} else {
if (!link)
return -ENOENT;
route = set_get(link->routes, in);
}
if (!route)
if (route_nexthop_get_link(manager, &route->nexthop, &link) >= 0)
existing = set_get(link->routes, route);
else
existing = set_get(manager->routes, route);
if (!existing)
return -ENOENT;
if (ret)
*ret = route;
*ret = existing;
return 0;
}
@ -333,19 +328,17 @@ static int route_get_link(Manager *manager, const Route *route, Link **ret) {
return link_get_by_index(manager, nh->ifindex, ret);
}
return route_nexthop_get_link(manager, NULL, &route->nexthop, ret);
return route_nexthop_get_link(manager, &route->nexthop, ret);
}
static int route_get_request(Link *link, const Route *route, Request **ret) {
static int route_get_request(Manager *manager, const Route *route, Request **ret) {
Request *req;
assert(link);
assert(link->manager);
assert(manager);
assert(route);
req = ordered_set_get(link->manager->request_queue,
req = ordered_set_get(manager->request_queue,
&(const Request) {
.link = link,
.type = REQUEST_TYPE_ROUTE,
.userdata = (void*) route,
.hash_func = (hash_func_t) route_hash_func,
@ -393,179 +386,6 @@ int route_dup(const Route *src, const RouteNextHop *nh, Route **ret) {
return 0;
}
static void route_apply_nexthop(Route *route, const NextHop *nh, uint8_t nh_weight) {
assert(route);
assert(nh);
assert(hashmap_isempty(nh->group));
route->nexthop.family = nh->family;
route->nexthop.gw = nh->gw;
if (nh_weight != UINT8_MAX)
route->nexthop.weight = nh_weight;
if (nh->blackhole)
route->type = RTN_BLACKHOLE;
}
static void route_apply_route_nexthop(Route *route, const RouteNextHop *nh) {
assert(route);
assert(nh);
route->nexthop.family = nh->family;
route->nexthop.gw = nh->gw;
route->nexthop.weight = nh->weight;
}
typedef struct ConvertedRoutes {
size_t n;
Route **routes;
Link **links;
} ConvertedRoutes;
static ConvertedRoutes *converted_routes_free(ConvertedRoutes *c) {
if (!c)
return NULL;
for (size_t i = 0; i < c->n; i++)
route_free(c->routes[i]);
free(c->routes);
free(c->links);
return mfree(c);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(ConvertedRoutes*, converted_routes_free);
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;
assert(n > 0);
assert(ret);
routes = new0(Route*, n);
if (!routes)
return -ENOMEM;
links = new0(Link*, n);
if (!links)
return -ENOMEM;
c = new(ConvertedRoutes, 1);
if (!c)
return -ENOMEM;
*c = (ConvertedRoutes) {
.n = n,
.routes = TAKE_PTR(routes),
.links = TAKE_PTR(links),
};
*ret = TAKE_PTR(c);
return 0;
}
static bool route_needs_convert(const Route *route) {
assert(route);
return route->nexthop_id > 0 || !ordered_set_isempty(route->nexthops);
}
static int route_convert(Manager *manager, Link *link, const Route *route, ConvertedRoutes **ret) {
_cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
int r;
assert(manager);
assert(route);
assert(ret);
/* link may be NULL */
if (!route_needs_convert(route)) {
*ret = NULL;
return 0;
}
if (route->nexthop_id > 0) {
struct nexthop_grp *nhg;
NextHop *nh;
r = nexthop_get_by_id(manager, route->nexthop_id, &nh);
if (r < 0)
return r;
if (hashmap_isempty(nh->group)) {
r = converted_routes_new(1, &c);
if (r < 0)
return r;
r = route_dup(route, NULL, &c->routes[0]);
if (r < 0)
return r;
route_apply_nexthop(c->routes[0], nh, UINT8_MAX);
(void) link_get_by_index(manager, nh->ifindex, c->links);
*ret = TAKE_PTR(c);
return 1;
}
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;
r = nexthop_get_by_id(manager, nhg->id, &h);
if (r < 0)
return r;
r = route_dup(route, NULL, &c->routes[i]);
if (r < 0)
return r;
route_apply_nexthop(c->routes[i], h, nhg->weight);
(void) link_get_by_index(manager, h->ifindex, c->links + i);
i++;
}
*ret = TAKE_PTR(c);
return 1;
}
assert(!ordered_set_isempty(route->nexthops));
r = converted_routes_new(ordered_set_size(route->nexthops), &c);
if (r < 0)
return r;
size_t i = 0;
RouteNextHop *nh;
ORDERED_SET_FOREACH(nh, route->nexthops) {
r = route_dup(route, NULL, &c->routes[i]);
if (r < 0)
return r;
route_apply_route_nexthop(c->routes[i], nh);
r = route_nexthop_get_link(manager, link, nh, &c->links[i]);
if (r < 0)
return r;
i++;
}
*ret = TAKE_PTR(c);
return 1;
}
void link_mark_routes(Link *link, NetworkConfigSource source) {
Route *route;
@ -620,14 +440,12 @@ static void log_route_debug(const Route *route, const char *str, Manager *manage
strna(proto), strna(scope), strna(route_type_to_string(route->type)), strna(flags));
}
static int route_set_netlink_message(const Route *route, sd_netlink_message *m, Link *link) {
static int route_set_netlink_message(const Route *route, sd_netlink_message *m) {
int r;
assert(route);
assert(m);
/* link may be NULL */
/* rtmsg header (and relevant attributes) */
if (route->dst_prefixlen > 0) {
r = netlink_message_append_in_addr_union(m, RTA_DST, route->family, &route->dst);
@ -696,7 +514,7 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *m,
return r;
/* nexthops */
r = route_nexthops_set_netlink_message(link, route, m);
r = route_nexthops_set_netlink_message(route, m);
if (r < 0)
return r;
@ -727,14 +545,12 @@ 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;
unsigned char type;
Manager *manager;
Link *link;
int r;
assert(route);
assert(route->manager || (route->link && route->link->manager));
assert(IN_SET(route->family, AF_INET, AF_INET6));
link = route->link;
manager = route->manager ?: link->manager;
@ -745,25 +561,10 @@ int route_remove(Route *route) {
if (r < 0)
return log_link_warning_errno(link, r, "Could not create netlink message: %m");
r = route_set_netlink_message(route, m, link);
r = route_set_netlink_message(route, m);
if (r < 0)
return log_link_warning_errno(link, r, "Could not fill netlink message: %m");
if (route->family == AF_INET && route->nexthop_id > 0 && route->type == RTN_BLACKHOLE)
/* When IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel
* sends RTM_NEWROUTE netlink message with blackhole type, kernel's internal route type
* fib_rt_info::type may not be blackhole. Thus, we cannot know the internal value.
* Moreover, on route removal, the matching is done with the hidden value if we set
* non-zero type in RTM_DELROUTE message. Note, sd_rtnl_message_new_route() sets
* RTN_UNICAST by default. So, we need to clear the type here. */
type = RTN_UNSPEC;
else
type = route->type;
r = sd_rtnl_message_route_set_type(m, type);
if (r < 0)
return log_link_error_errno(link, r, "Could not set route type: %m");
r = netlink_call_async(manager->rtnl, NULL, m, route_remove_handler,
link ? link_netlink_destroy_callback : NULL, link);
if (r < 0)
@ -790,10 +591,32 @@ 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;
int r;
assert(manager);
@ -830,21 +653,14 @@ static void manager_mark_routes(Manager *manager, bool foreign, const Link *exce
continue;
HASHMAP_FOREACH(route, link->network->routes_by_section) {
_cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
Route *existing;
if (route->family == AF_INET || ordered_set_isempty(route->nexthops))
(void) link_unmark_route(link, route, NULL);
r = route_convert(manager, link, route, &converted);
if (r < 0)
continue;
if (r == 0) {
if (route_get(manager, NULL, route, &existing) >= 0)
route_unmark(existing);
continue;
else {
RouteNextHop *nh;
ORDERED_SET_FOREACH(nh, route->nexthops)
(void) link_unmark_route(link, route, nh);
}
for (size_t i = 0; i < converted->n; i++)
if (route_get(manager, NULL, converted->routes[i], &existing) >= 0)
route_unmark(existing);
}
}
}
@ -888,12 +704,11 @@ static void link_unmark_wireguard_routes(Link *link) {
if (!link->netdev || link->netdev->kind != NETDEV_KIND_WIREGUARD)
return;
Route *route, *existing;
Route *route;
Wireguard *w = WIREGUARD(link->netdev);
SET_FOREACH(route, w->routes)
if (route_get(NULL, link, route, &existing) >= 0)
route_unmark(existing);
(void) link_unmark_route(link, route, NULL);
}
int link_drop_foreign_routes(Link *link) {
@ -929,21 +744,14 @@ int link_drop_foreign_routes(Link *link) {
}
HASHMAP_FOREACH(route, link->network->routes_by_section) {
_cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
Route *existing;
if (route->family == AF_INET || ordered_set_isempty(route->nexthops))
(void) link_unmark_route(link, route, NULL);
r = route_convert(link->manager, link, route, &converted);
if (r < 0)
continue;
if (r == 0) {
if (route_get(NULL, link, route, &existing) >= 0)
route_unmark(existing);
continue;
else {
RouteNextHop *nh;
ORDERED_SET_FOREACH(nh, route->nexthops)
(void) link_unmark_route(link, route, nh);
}
for (size_t i = 0; i < converted->n; i++)
if (route_get(NULL, link, converted->routes[i], &existing) >= 0)
route_unmark(existing);
}
link_unmark_wireguard_routes(link);
@ -1092,7 +900,7 @@ static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link
if (r < 0)
return r;
r = route_set_netlink_message(route, m, link);
r = route_set_netlink_message(route, m);
if (r < 0)
return r;
@ -1105,6 +913,64 @@ static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link
return request_call_netlink_async(link->manager->rtnl, m, req);
}
static int route_requeue_request(Request *req, Link *link, const Route *route) {
_unused_ _cleanup_(request_unrefp) Request *req_unref = NULL;
_cleanup_(route_freep) Route *tmp = NULL;
int r;
assert(req);
assert(link);
assert(link->manager);
assert(route);
/* It is not possible to adjust the Route object owned by Request, as it is used as a key to manage
* Request objects in the queue. Hence, we need to re-request with the updated Route object. */
if (!route_nexthops_needs_adjust(route))
return 0; /* The Route object does not need the adjustment. Continue with it. */
r = route_dup(route, NULL, &tmp);
if (r < 0)
return r;
r = route_adjust_nexthops(tmp, link);
if (r <= 0)
return r;
if (route_compare_func(route, tmp) == 0 && route->type == tmp->type)
return 0; /* No effective change?? That's OK. */
/* Avoid the request to be freed by request_detach(). */
req_unref = request_ref(req);
/* Detach the request from the queue, to make not the new request is deduped.
* Why this is necessary? IPv6 routes with different type may be handled as the same,
* As commented in route_adjust_nexthops(), we need to configure the adjusted type,
* otherwise we cannot remove the route on reconfigure or so. If we request the new Route object
* without detaching the current request, the new request is deduped, and the route is configured
* with unmodified type. */
request_detach(req);
/* Request the route with the adjusted Route object combined with the same other parameters. */
r = link_queue_request_full(link,
req->type,
tmp,
req->free_func,
req->hash_func,
req->compare_func,
req->process,
req->counter,
req->netlink_handler,
NULL);
if (r < 0)
return r;
if (r == 0)
return 1; /* Already queued?? That's OK. Maybe, [Route] section is effectively duplicated. */
TAKE_PTR(tmp);
return 1; /* New request is queued. Finish to process this request. */
}
static int route_is_ready_to_configure(const Route *route, Link *link) {
int r;
@ -1123,11 +989,11 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
return r;
}
return route_nexthops_is_ready_to_configure(route, link);
return route_nexthops_is_ready_to_configure(route, link->manager);
}
static int route_process_request(Request *req, Link *link, Route *route) {
_cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
Route *existing;
int r;
assert(req);
@ -1141,36 +1007,6 @@ static int route_process_request(Request *req, Link *link, Route *route) {
if (r == 0)
return 0;
if (route_needs_convert(route)) {
r = route_convert(link->manager, link, route, &converted);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to convert route: %m");
assert(r > 0);
assert(converted);
for (size_t i = 0; i < converted->n; i++) {
Route *existing;
if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) {
_cleanup_(route_freep) Route *tmp = NULL;
r = route_dup(converted->routes[i], NULL, &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;
}
}
}
usec_t now_usec;
assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec);
@ -1178,101 +1014,100 @@ static int route_process_request(Request *req, Link *link, Route *route) {
log_link_debug(link, "Refuse to configure %s route with zero lifetime.",
network_config_source_to_string(route->source));
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_cancel_requesting(existing);
}
else
route_cancel_requesting(route);
route_cancel_requesting(route);
if (route_get(link->manager, link, route, &existing) >= 0)
route_cancel_requesting(existing);
return 1;
}
r = route_requeue_request(req, link, route);
if (r != 0)
return r;
r = route_configure(route, sec, link, req);
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);
route_enter_configuring(route);
if (route_get(link->manager, link, route, &existing) >= 0)
route_enter_configuring(existing);
return 1;
}
int link_request_route(
static int link_request_route_one(
Link *link,
Route *route,
bool consume_object,
const Route *route,
const RouteNextHop *nh,
unsigned *message_counter,
route_netlink_handler_t netlink_handler,
Request **ret) {
route_netlink_handler_t netlink_handler) {
_cleanup_(route_freep) Route *tmp = NULL;
Route *existing = NULL;
int r;
assert(link);
assert(link->manager);
assert(route);
assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
assert(!route_needs_convert(route));
(void) route_get(link->manager, link, route, &existing);
r = route_dup(route, nh, &tmp);
if (r < 0)
return r;
if (route->lifetime_usec == 0) {
if (consume_object)
route_free(route);
r = route_adjust_nexthops(tmp, link);
if (r < 0)
return r;
/* The requested route is outdated. Let's remove it. */
return route_remove_and_drop(existing);
}
if (route_get(link->manager, link, tmp, &existing) >= 0)
/* Copy state for logging below. */
tmp->state = existing->state;
if (!existing) {
_cleanup_(route_freep) Route *tmp = NULL;
if (consume_object)
tmp = route;
else {
r = route_dup(route, NULL, &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;
existing->lifetime_usec = route->lifetime_usec;
if (consume_object)
route_free(route);
}
log_route_debug(existing, "Requesting", link->manager);
log_route_debug(tmp, "Requesting", link->manager);
r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
existing, NULL,
tmp,
route_free,
route_hash_func,
route_compare_func,
route_process_request,
message_counter, netlink_handler, ret);
message_counter,
netlink_handler,
NULL);
if (r <= 0)
return r;
route_enter_requesting(existing);
route_enter_requesting(tmp);
if (existing)
route_enter_requesting(existing);
TAKE_PTR(tmp);
return 1;
}
int link_request_route(
Link *link,
const Route *route,
unsigned *message_counter,
route_netlink_handler_t netlink_handler) {
int r;
assert(link);
assert(link->manager);
assert(route);
assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
if (route->family == AF_INET || route_type_is_reject(route) || ordered_set_isempty(route->nexthops))
return link_request_route_one(link, route, NULL, message_counter, netlink_handler);
RouteNextHop *nh;
ORDERED_SET_FOREACH(nh, route->nexthops) {
r = link_request_route_one(link, route, nh, message_counter, netlink_handler);
if (r < 0)
return r;
}
return 0;
}
static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
int r;
@ -1291,22 +1126,6 @@ static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
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->manager);
return link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
route, NULL, route_hash_func, route_compare_func,
route_process_request,
&link->static_route_messages, static_route_handler, NULL);
}
static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
NetDev *netdev;
Route *route;
@ -1326,7 +1145,7 @@ static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
if (only_ipv4 && route->family != AF_INET)
continue;
r = link_request_static_route(link, route);
r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
if (r < 0)
return r;
}
@ -1350,7 +1169,7 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
if (only_ipv4 && route->family != AF_INET)
continue;
r = link_request_static_route(link, route);
r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
if (r < 0)
return r;
}
@ -1372,14 +1191,15 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
void route_cancel_request(Route *route, Link *link) {
assert(route);
link = ASSERT_PTR(route->link ?: link);
Manager *manager = ASSERT_PTR(route->manager ?:
route->link ? route->link->manager :
ASSERT_PTR(link)->manager);
if (!route_is_requesting(route))
return;
Request *req;
if (route_get_request(link, route, &req) >= 0)
if (route_get_request(manager, route, &req) >= 0)
request_detach(req);
route_cancel_requesting(route);
@ -1387,13 +1207,14 @@ void route_cancel_request(Route *route, Link *link) {
static int process_route_one(
Manager *manager,
Link *link,
uint16_t type,
Route *in,
const struct rta_cacheinfo *cacheinfo) {
_cleanup_(route_freep) Route *tmp = in;
Request *req = NULL;
Route *route = NULL;
Link *link = NULL;
bool is_new = false, update_dhcp4;
int r;
@ -1401,23 +1222,23 @@ static int process_route_one(
assert(tmp);
assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
/* link may be NULL. This consumes 'in'. */
(void) route_get(manager, NULL, tmp, &route);
(void) route_get_request(manager, tmp, &req);
(void) route_get_link(manager, tmp, &link);
update_dhcp4 = link && tmp->family == AF_INET6 && tmp->dst_prefixlen == 0;
(void) route_get(manager, link, tmp, &route);
switch (type) {
case RTM_NEWROUTE:
if (!route) {
if (!manager->manage_foreign_routes) {
if (!manager->manage_foreign_routes && !(req && req->waiting_reply)) {
route_enter_configured(tmp);
log_route_debug(tmp, "Ignoring received", manager);
return 0;
}
/* If we do not know the route, then save it. */
r = route_add(manager, link, tmp);
r = route_add(manager, tmp);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
return 0;
@ -1428,7 +1249,16 @@ static int process_route_one(
} else
/* Update remembered route with the received notification. */
route->flags = tmp->flags;
route->nexthop.weight = tmp->nexthop.weight;
/* Also update information that cannot be obtained through netlink notification. */
if (req && req->waiting_reply) {
Route *rt = ASSERT_PTR(req->userdata);
route->source = rt->source;
route->provider = rt->provider;
route->lifetime_usec = rt->lifetime_usec;
}
route_enter_configured(route);
log_route_debug(route, is_new ? "Received new" : "Received remembered", manager);
@ -1440,16 +1270,16 @@ static int process_route_one(
case RTM_DELROUTE:
if (route) {
route_enter_removed(route);
if (route->state == 0) {
log_route_debug(route, "Forgetting", manager);
route_free(route);
} else
log_route_debug(route, "Removed", manager);
log_route_debug(route, "Forgetting removed", manager);
route_free(route);
} else
log_route_debug(tmp,
manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
manager);
if (req)
route_enter_removed(req->userdata);
break;
default:
@ -1610,38 +1440,22 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
}
has_cacheinfo = r >= 0;
Link *link = NULL;
if (tmp->nexthop.ifindex > 0) {
r = link_get_by_index(m, tmp->nexthop.ifindex, &link);
if (r < 0) {
/* when enumerating we might be out of sync, but we will
* get the route again, so just ignore it */
if (!m->enumerating)
log_warning("rtnl: received route message for link (%i) we do not know about, ignoring", tmp->nexthop.ifindex);
return 0;
}
if (tmp->family == AF_INET || ordered_set_isempty(tmp->nexthops))
return process_route_one(m, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
RouteNextHop *nh;
ORDERED_SET_FOREACH(nh, tmp->nexthops) {
_cleanup_(route_freep) Route *dup = NULL;
r = route_dup(tmp, nh, &dup);
if (r < 0)
return log_oom();
r = process_route_one(m, type, TAKE_PTR(dup), has_cacheinfo ? &cacheinfo : NULL);
if (r < 0)
return r;
}
if (!route_needs_convert(tmp))
return process_route_one(m, link, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
_cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
r = route_convert(m, link, 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]),
has_cacheinfo ? &cacheinfo : NULL);
return 1;
}

View file

@ -101,11 +101,9 @@ void link_foreignize_routes(Link *link);
void route_cancel_request(Route *route, Link *link);
int link_request_route(
Link *link,
Route *route,
bool consume_object,
const Route *route,
unsigned *message_counter,
route_netlink_handler_t netlink_handler,
Request **ret);
route_netlink_handler_t netlink_handler);
int link_request_static_routes(Link *link, bool only_ipv4);
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);