diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c index aecdaa5b25..b0e4afdc45 100644 --- a/src/network/networkd-dhcp-prefix-delegation.c +++ b/src/network/networkd-dhcp-prefix-delegation.c @@ -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"); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 2afda41ab5..28702ff446 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -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; } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 74bb549dac..9f56fd23d4 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -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"); diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 383f259fa7..f1dd6b44d2 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -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; diff --git a/src/network/networkd-route-nexthop.c b/src/network/networkd-route-nexthop.c index 9e14c695be..2228c6280d 100644 --- a/src/network/networkd-route-nexthop.c +++ b/src/network/networkd-route-nexthop.c @@ -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; } diff --git a/src/network/networkd-route-nexthop.h b/src/network/networkd-route-nexthop.h index a63409c9e2..5e4602d3cd 100644 --- a/src/network/networkd-route-nexthop.h +++ b/src/network/networkd-route-nexthop.h @@ -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); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index f877702efc..a142719fb7 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -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; } diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 49e78e9777..e86693e315 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -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);