#include "vrf.h"
#include "wireguard.h"
-Route* route_free(Route *route) {
- if (!route)
- return NULL;
+static Route* route_detach_impl(Route *route) {
+ assert(route);
+ assert(!!route->network + !!route->manager + !!route->wireguard <= 1);
if (route->network) {
assert(route->section);
hashmap_remove(route->network->routes_by_section, route->section);
+ route->network = NULL;
+ return route;
}
- if (route->manager)
+ if (route->manager) {
+ route_detach_from_nexthop(route);
set_remove(route->manager->routes, route);
+ route->manager = NULL;
+ return route;
+ }
- if (route->wireguard)
+ if (route->wireguard) {
set_remove(route->wireguard->routes, route);
+ route->wireguard = NULL;
+ return route;
+ }
+
+ return NULL;
+}
+
+static void route_detach(Route *route) {
+ route_unref(route_detach_impl(route));
+}
+
+static Route* route_free(Route *route) {
+ if (!route)
+ return NULL;
+
+ route_detach_impl(route);
config_section_free(route->section);
route_nexthops_done(route);
return mfree(route);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Route, route, route_free);
+
static void route_hash_func(const Route *route, struct siphash *state) {
assert(route);
Route,
route_hash_func,
route_compare_func,
- route_free);
+ route_detach);
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ route_hash_ops_unref,
+ Route,
+ route_hash_func,
+ route_compare_func,
+ route_unref);
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ route_section_hash_ops,
+ ConfigSection,
+ config_section_hash_func,
+ config_section_compare_func,
+ Route,
+ route_detach);
int route_new(Route **ret) {
- _cleanup_(route_freep) Route *route = NULL;
+ _cleanup_(route_unrefp) Route *route = NULL;
route = new(Route, 1);
if (!route)
return -ENOMEM;
*route = (Route) {
+ .n_ref = 1,
.family = AF_UNSPEC,
.scope = RT_SCOPE_UNIVERSE,
.protocol = RTPROT_UNSPEC,
int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
_cleanup_(config_section_freep) ConfigSection *n = NULL;
- _cleanup_(route_freep) Route *route = NULL;
+ _cleanup_(route_unrefp) Route *route = NULL;
int r;
assert(network);
route->section = TAKE_PTR(n);
route->source = NETWORK_CONFIG_SOURCE_STATIC;
- r = hashmap_ensure_put(&network->routes_by_section, &config_section_hash_ops, route->section, route);
+ r = hashmap_ensure_put(&network->routes_by_section, &route_section_hash_ops, route->section, route);
if (r < 0)
return r;
return 0;
}
-static int route_add(Manager *manager, Route *route) {
+static int route_attach(Manager *manager, Route *route) {
int r;
assert(manager);
}
int route_dup(const Route *src, const RouteNextHop *nh, Route **ret) {
- _cleanup_(route_freep) Route *dest = NULL;
+ _cleanup_(route_unrefp) Route *dest = NULL;
int r;
assert(src);
if (!dest)
return -ENOMEM;
- /* Unset all pointers */
+ /* Unset number of reference and all pointers */
+ dest->n_ref = 1;
dest->manager = NULL;
dest->network = NULL;
dest->wireguard = NULL;
return 0;
}
-static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
int r;
assert(m);
+ assert(rreq);
- /* link may be NULL. */
-
- if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 0;
+ Manager *manager = ASSERT_PTR(rreq->manager);
+ Route *route = ASSERT_PTR(rreq->userdata);
r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -ESRCH)
- log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
+ if (r < 0) {
+ log_message_full_errno(m,
+ (r == -ESRCH || /* the route is already removed? */
+ (r == -EINVAL && route->nexthop_id != 0) || /* The nexthop is already removed? */
+ !route->manager) ? /* already detached? */
+ LOG_DEBUG : LOG_WARNING,
+ r, "Could not drop route, ignoring");
+
+ if (route->manager) {
+ /* If the route cannot be removed, then assume the route is already removed. */
+ log_route_debug(route, "Forgetting", manager);
+
+ Request *req;
+ if (route_get_request(manager, route, &req) >= 0)
+ route_enter_removed(req->userdata);
+
+ route_detach(route);
+ }
+ }
return 1;
}
-int route_remove(Route *route) {
+int route_remove(Route *route, Manager *manager) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
Link *link = NULL;
int r;
assert(route);
- Manager *manager = ASSERT_PTR(route->manager);
+ assert(manager);
log_route_debug(route, "Removing", manager);
if (r < 0)
return log_link_warning_errno(link, r, "Could not fill netlink message: %m");
- r = netlink_call_async(manager->rtnl, NULL, m, route_remove_handler,
- link ? link_netlink_destroy_callback : NULL, link);
+ r = manager_remove_request_add(manager, route, route, manager->rtnl, m, route_remove_handler);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not send netlink message: %m");
-
- link_ref(link);
+ return log_link_warning_errno(link, r, "Could not queue rtnetlink message: %m");
route_enter_removing(route);
return 0;
}
-int route_remove_and_drop(Route *route) {
- if (!route)
- return 0;
+int route_remove_and_cancel(Route *route, Manager *manager) {
+ bool waiting = false;
+ Request *req;
- route_cancel_request(route, NULL);
+ assert(route);
+ assert(manager);
- if (route_exists(route))
- return route_remove(route);
+ /* If the route is remembered by the manager, then use the remembered object. */
+ (void) route_get(manager, route, &route);
- if (route->state == 0)
- route_free(route);
+ /* Cancel the request for the route. If the request is already called but we have not received the
+ * notification about the request, then explicitly remove the route. */
+ if (route_get_request(manager, route, &req) >= 0) {
+ waiting = req->waiting_reply;
+ request_detach(req);
+ route_cancel_requesting(route);
+ }
+
+ /* If we know that the route will come or already exists, remove it. */
+ if (waiting || (route->manager && route_exists(route)))
+ return route_remove(route, manager);
return 0;
}
Route *route = ASSERT_PTR(userdata);
int r;
- assert(route->manager);
+ if (!route->manager)
+ return 0; /* already detached. */
- r = route_remove(route);
+ r = route_remove(route, route->manager);
if (r < 0) {
Link *link = NULL;
(void) route_get_link(route->manager, route, &link);
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;
+ _cleanup_(route_unrefp) Route *tmp = NULL;
int r;
assert(req);
unsigned *message_counter,
route_netlink_handler_t netlink_handler) {
- _cleanup_(route_freep) Route *tmp = NULL;
+ _cleanup_(route_unrefp) Route *tmp = NULL;
Route *existing = NULL;
int r;
log_route_debug(tmp, "Requesting", link->manager);
r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
tmp,
- route_free,
+ route_unref,
route_hash_func,
route_compare_func,
route_process_request,
return 0;
}
-void route_cancel_request(Route *route, Link *link) {
- assert(route);
- Manager *manager = ASSERT_PTR(route->manager ?: ASSERT_PTR(link)->manager);
-
- if (!route_is_requesting(route))
- return;
-
- Request *req;
- if (route_get_request(manager, route, &req) >= 0)
- request_detach(req);
-
- route_cancel_requesting(route);
-}
-
static int process_route_one(
Manager *manager,
uint16_t type,
- Route *in,
+ Route *tmp,
const struct rta_cacheinfo *cacheinfo) {
- _cleanup_(route_freep) Route *tmp = in;
Request *req = NULL;
Route *route = NULL;
Link *link = NULL;
}
/* If we do not know the route, then save it. */
- r = route_add(manager, tmp);
+ r = route_attach(manager, tmp);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
return 0;
}
- route = TAKE_PTR(tmp);
+ route = route_ref(tmp);
is_new = true;
} else
if (route) {
route_enter_removed(route);
log_route_debug(route, "Forgetting removed", manager);
- route_free(route);
+ route_detach(route);
} else
log_route_debug(tmp,
manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
}
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
- _cleanup_(route_freep) Route *tmp = NULL;
+ _cleanup_(route_unrefp) Route *tmp = NULL;
int r;
assert(rtnl);
has_cacheinfo = r >= 0;
if (tmp->family == AF_INET || ordered_set_isempty(tmp->nexthops))
- return process_route_one(m, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
+ return process_route_one(m, type, tmp, has_cacheinfo ? &cacheinfo : NULL);
RouteNextHop *nh;
ORDERED_SET_FOREACH(nh, tmp->nexthops) {
- _cleanup_(route_freep) Route *dup = NULL;
+ _cleanup_(route_unrefp) 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);
+ r = process_route_one(m, type, dup, has_cacheinfo ? &cacheinfo : NULL);
if (r < 0)
return r;
}
}
static int link_unmark_route(Link *link, const Route *route, const RouteNextHop *nh) {
- _cleanup_(route_freep) Route *tmp = NULL;
+ _cleanup_(route_unrefp) Route *tmp = NULL;
Route *existing;
int r;
if (!route_is_marked(route))
continue;
- RET_GATHER(r, route_remove(route));
+ RET_GATHER(r, route_remove(route, link->manager));
}
return r;
}
int network_add_ipv4ll_route(Network *network) {
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
unsigned section_line;
int r;
}
int network_add_default_route_on_device(Network *network) {
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
unsigned section_line;
int r;
void *userdata) {
Network *network = userdata;
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
int r;
assert(filename);
void *userdata) {
Network *network = userdata;
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
union in_addr_union *buffer;
unsigned char *prefixlen;
int r;
void *userdata) {
Network *network = userdata;
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
int r;
assert(filename);
void *userdata) {
Network *network = userdata;
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
int r;
assert(filename);
void *data,
void *userdata) {
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
Network *network = userdata;
int r;
void *userdata) {
Network *network = userdata;
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
int r;
r = route_new_static(network, filename, section_line, &route);
void *userdata) {
Network *network = userdata;
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
int r;
r = route_new_static(network, filename, section_line, &route);
void *userdata) {
Network *network = userdata;
- _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
+ _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
int t, r;
r = route_new_static(network, filename, section_line, &route);
HASHMAP_FOREACH(route, network->routes_by_section)
if (route_section_verify(route) < 0)
- route_free(route);
+ route_detach(route);
}