diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index ed4d7c34e4..b31b68756d 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -38,7 +38,7 @@ static int dhcp4_release_old_lease(Link *link) { link_dirty(link); SET_FOREACH(route, link->dhcp_routes_old) { - k = route_remove(route, link, NULL); + k = route_remove(route, NULL, link, NULL); if (k < 0) r = k; } @@ -512,7 +512,7 @@ static int dhcp4_remove_all(Link *link) { assert(link); SET_FOREACH(route, link->dhcp_routes) { - k = route_remove(route, link, dhcp4_remove_route_handler); + k = route_remove(route, NULL, link, dhcp4_remove_route_handler); if (k < 0) r = k; else diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index ea331c95db..5217497f98 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -164,7 +164,7 @@ static int dhcp6_pd_remove_old(Link *link, bool force) { link_dirty(link); SET_FOREACH(route, link->dhcp6_pd_routes_old) { - k = route_remove(route, link, NULL); + k = route_remove(route, NULL, link, NULL); if (k < 0) r = k; @@ -208,7 +208,7 @@ int dhcp6_pd_remove(Link *link) { link_dirty(link); SET_FOREACH(route, link->dhcp6_pd_routes) { - k = route_remove(route, link, NULL); + k = route_remove(route, NULL, link, NULL); if (k < 0) r = k; @@ -704,7 +704,7 @@ static int dhcp6_remove_old(Link *link, bool force) { link_dirty(link); SET_FOREACH(route, link->dhcp6_routes_old) { - k = route_remove(route, link, NULL); + k = route_remove(route, NULL, link, NULL); if (k < 0) r = k; } @@ -740,7 +740,7 @@ static int dhcp6_remove(Link *link) { link_dirty(link); SET_FOREACH(route, link->dhcp6_routes) { - k = route_remove(route, link, NULL); + k = route_remove(route, NULL, link, NULL); if (k < 0) r = k; } diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index e8d270987d..2cd6830072 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -886,6 +886,9 @@ void manager_free(Manager *m) { m->rules_foreign = set_free(m->rules_foreign); set_free(m->rules_saved); + m->routes = set_free(m->routes); + m->routes_foreign = set_free(m->routes_foreign); + sd_netlink_unref(m->rtnl); sd_netlink_unref(m->genl); sd_resolve_unref(m->resolve); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 142a6eb358..ac7de58c64 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -61,6 +61,10 @@ struct Manager { Set *rules_foreign; Set *rules_saved; + /* Manager stores routes without RTA_OIF attribute. */ + Set *routes; + Set *routes_foreign; + /* For link speed meter*/ bool use_speed_meter; sd_event_source *speed_meter_event_source; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 2599ec2232..e2e049201a 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -163,7 +163,7 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool SET_FOREACH(nr, link->ndisc_routes) if (nr->marked && IN6_ARE_ADDR_EQUAL(&nr->router, router)) { - k = route_remove(nr->route, link, NULL); + k = route_remove(nr->route, NULL, link, NULL); if (k < 0) r = k; } diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 870de18b8c..c3de2c7c08 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -272,6 +272,11 @@ Route *route_free(Route *route) { free(set_remove(route->link->ndisc_routes, n)); } + if (route->manager) { + set_remove(route->manager->routes, route); + set_remove(route->manager->routes_foreign, route); + } + ordered_set_free_free(route->multipath_routes); sd_event_source_unref(route->expire); @@ -404,36 +409,51 @@ static bool route_equal(Route *r1, Route *r2) { return route_compare_func(r1, r2) == 0; } -static int route_get(Link *link, Route *in, Route **ret) { - +static int route_get(Manager *manager, Link *link, Route *in, Route **ret) { Route *existing; - assert(link); + assert(manager || link); assert(in); - existing = set_get(link->routes, in); - if (existing) { - if (ret) - *ret = existing; - return 1; - } + if (link) { + existing = set_get(link->routes, in); + if (existing) { + if (ret) + *ret = existing; + return 1; + } - existing = set_get(link->routes_foreign, in); - if (existing) { - if (ret) - *ret = existing; - return 0; + existing = set_get(link->routes_foreign, in); + if (existing) { + if (ret) + *ret = existing; + return 0; + } + } else { + existing = set_get(manager->routes, in); + if (existing) { + if (ret) + *ret = existing; + return 1; + } + + existing = set_get(manager->routes_foreign, in); + if (existing) { + if (ret) + *ret = existing; + return 0; + } } return -ENOENT; } -static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) { +static int route_add_internal(Manager *manager, Link *link, Set **routes, Route *in, Route **ret) { _cleanup_(route_freep) Route *route = NULL; int r; - assert(link); + assert(manager || link); assert(routes); assert(in); @@ -465,6 +485,7 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) return -EEXIST; route->link = link; + route->manager = manager; if (ret) *ret = route; @@ -474,28 +495,39 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) return 0; } -static int route_add_foreign(Link *link, Route *in, Route **ret) { - return route_add_internal(link, &link->routes_foreign, in, ret); +static int route_add_foreign(Manager *manager, Link *link, Route *in, Route **ret) { + assert(manager || link); + return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret); } -static int route_add(Link *link, Route *in, Route **ret) { - +static int route_add(Manager *manager, Link *link, Route *in, Route **ret) { Route *route; int r; - r = route_get(link, in, &route); + assert(manager || link); + assert(in); + + r = route_get(manager, link, in, &route); if (r == -ENOENT) { /* Route does not exist, create a new one */ - r = route_add_internal(link, &link->routes, in, &route); + 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->routes, &route_hash_ops, route); - if (r < 0) - return r; + if (link) { + r = set_ensure_put(&link->routes, &route_hash_ops, route); + if (r < 0) + return r; - set_remove(link->routes_foreign, route); + set_remove(link->routes_foreign, route); + } else { + r = set_ensure_put(&manager->routes, &route_hash_ops, route); + if (r < 0) + return r; + + set_remove(manager->routes_foreign, route); + } } else if (r == 1) { /* Route exists, do nothing */ ; @@ -512,10 +544,9 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l int r; assert(m); - assert(link); - assert(link->ifname); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + /* Note that link may be NULL. */ + if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; r = sd_netlink_message_get_errno(m); @@ -525,19 +556,22 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l return 1; } -int route_remove(Route *route, Link *link, - link_netlink_message_handler_t callback) { +int route_remove( + Route *route, + Manager *manager, + Link *link, + link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - assert(link->ifindex > 0); + assert(link || manager); assert(IN_SET(route->family, AF_INET, AF_INET6)); - r = sd_rtnl_message_new_route(link->manager->rtnl, &req, + if (!manager) + manager = link->manager; + + r = sd_rtnl_message_new_route(manager->rtnl, &req, RTM_DELROUTE, route->family, route->protocol); if (r < 0) @@ -618,7 +652,8 @@ int route_remove(Route *route, Link *link, if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - link_ref(link); + if (link) + link_ref(link); return 0; } @@ -668,9 +703,9 @@ int link_drop_foreign_routes(Link *link) { continue; if (link_is_static_route_configured(link, route)) - k = route_add(link, route, NULL); + k = route_add(NULL, link, route, NULL); else - k = route_remove(route, link, NULL); + k = route_remove(route, NULL, link, NULL); if (k < 0 && r >= 0) r = k; } @@ -689,7 +724,7 @@ int link_drop_routes(Link *link) { if (route->protocol == RTPROT_KERNEL) continue; - k = route_remove(route, link, NULL); + k = route_remove(route, NULL, link, NULL); if (k < 0 && r >= 0) r = k; } @@ -703,7 +738,7 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat assert(route); - r = route_remove(route, route->link, NULL); + r = route_remove(route, route->manager, route->link, NULL); if (r < 0) log_link_warning_errno(route->link, r, "Could not remove route: %m"); else @@ -810,7 +845,7 @@ int route_configure( assert(IN_SET(route->family, AF_INET, AF_INET6)); assert(callback); - if (route_get(link, route, NULL) <= 0 && + 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"); @@ -988,7 +1023,10 @@ int route_configure( link_ref(link); - r = route_add(link, route, &route); + if (IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW) || !ordered_set_isempty(route->multipath_routes)) + r = route_add(link->manager, NULL, route, &route); + else + r = route_add(NULL, link, route, &route); if (r < 0) return log_link_error_errno(link, r, "Could not add route: %m"); @@ -1123,24 +1161,23 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma } r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex); - if (r == -ENODATA) { - log_debug("rtnl: received route message without ifindex, ignoring"); - return 0; - } else if (r < 0) { + if (r < 0 && r != -ENODATA) { log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m"); return 0; - } else if (ifindex <= 0) { - log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex); - return 0; - } + } else if (r >= 0) { + if (ifindex <= 0) { + log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex); + return 0; + } - r = link_get(m, ifindex, &link); - if (r < 0 || !link) { - /* 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 (%d) we do not know about, ignoring", ifindex); - return 0; + r = link_get(m, ifindex, &link); + if (r < 0 || !link) { + /* 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 (%d) we do not know about, ignoring", ifindex); + return 0; + } } r = route_new(&tmp); @@ -1290,7 +1327,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma } } - (void) route_get(link, tmp, &route); + (void) route_get(m, link, tmp, &route); if (DEBUG_LOGGING) { _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL, @@ -1311,7 +1348,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma log_link_debug(link, "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s", - (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" : + (!route && !m->manage_foreign_routes) ? "Ignoring received foreign" : type == RTM_DELROUTE ? "Forgetting" : route ? "Received remembered" : "Remembering", strna(buf_dst), strempty(buf_dst_prefixlen), @@ -1324,9 +1361,9 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma switch (type) { case RTM_NEWROUTE: - if (!route && link->manager->manage_foreign_routes) { + if (!route && m->manage_foreign_routes) { /* A route appeared that we did not request */ - r = route_add_foreign(link, tmp, &route); + r = route_add_foreign(m, link, tmp, &route); if (r < 0) { log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m"); return 0; @@ -1420,7 +1457,7 @@ int link_deserialize_routes(Link *link, const char *routes) { continue; } - r = route_add(link, tmp, &route); + r = route_add(NULL, link, tmp, &route); if (r < 0) return log_link_debug_errno(link, r, "Failed to add route: %m"); diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 3347c7c57b..5a82c3ac58 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -31,6 +31,7 @@ typedef struct Route { NetworkConfigSection *section; Link *link; + Manager *manager; int family; int quickack; @@ -74,7 +75,7 @@ Route *route_free(Route *route); DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free); int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret); -int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback); +int route_remove(Route *route, Manager *manager, Link *link, link_netlink_message_handler_t callback); int link_set_routes(Link *link); int link_drop_routes(Link *link);