diff options
Diffstat (limited to 'src/network/networkd-neighbor.c')
-rw-r--r-- | src/network/networkd-neighbor.c | 230 |
1 files changed, 143 insertions, 87 deletions
diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 8321831..6b81950 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -10,28 +10,87 @@ #include "networkd-queue.h" #include "set.h" -Neighbor *neighbor_free(Neighbor *neighbor) { - if (!neighbor) - return NULL; +static Neighbor* neighbor_detach_impl(Neighbor *neighbor) { + assert(neighbor); + assert(!neighbor->link || !neighbor->network); if (neighbor->network) { assert(neighbor->section); ordered_hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section); + neighbor->network = NULL; + return neighbor; } - config_section_free(neighbor->section); - - if (neighbor->link) + if (neighbor->link) { set_remove(neighbor->link->neighbors, neighbor); + neighbor->link = NULL; + return neighbor; + } + + return NULL; +} + +static void neighbor_detach(Neighbor *neighbor) { + neighbor_unref(neighbor_detach_impl(neighbor)); +} + +static Neighbor* neighbor_free(Neighbor *neighbor) { + if (!neighbor) + return NULL; + + neighbor_detach_impl(neighbor); + config_section_free(neighbor->section); return mfree(neighbor); } -DEFINE_SECTION_CLEANUP_FUNCTIONS(Neighbor, neighbor_free); +DEFINE_TRIVIAL_REF_UNREF_FUNC(Neighbor, neighbor, neighbor_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(Neighbor, neighbor_unref); + +static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state); +static int neighbor_compare_func(const Neighbor *a, const Neighbor *b); + +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + neighbor_hash_ops_detach, + Neighbor, + neighbor_hash_func, + neighbor_compare_func, + neighbor_detach); + +DEFINE_PRIVATE_HASH_OPS( + neighbor_hash_ops, + Neighbor, + neighbor_hash_func, + neighbor_compare_func); + +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + neighbor_section_hash_ops, + ConfigSection, + config_section_hash_func, + config_section_compare_func, + Neighbor, + neighbor_detach); + +static int neighbor_new(Neighbor **ret) { + Neighbor *neighbor; + + assert(ret); + + neighbor = new(Neighbor, 1); + if (!neighbor) + return -ENOMEM; + + *neighbor = (Neighbor) { + .n_ref = 1, + }; + + *ret = TAKE_PTR(neighbor); + return 0; +} static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) { _cleanup_(config_section_freep) ConfigSection *n = NULL; - _cleanup_(neighbor_freep) Neighbor *neighbor = NULL; + _cleanup_(neighbor_unrefp) Neighbor *neighbor = NULL; int r; assert(network); @@ -49,18 +108,15 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned return 0; } - neighbor = new(Neighbor, 1); - if (!neighbor) - return -ENOMEM; + r = neighbor_new(&neighbor); + if (r < 0) + return r; - *neighbor = (Neighbor) { - .network = network, - .family = AF_UNSPEC, - .section = TAKE_PTR(n), - .source = NETWORK_CONFIG_SOURCE_STATIC, - }; + neighbor->network = network; + neighbor->section = TAKE_PTR(n); + neighbor->source = NETWORK_CONFIG_SOURCE_STATIC; - r = ordered_hashmap_ensure_put(&network->neighbors_by_section, &config_section_hash_ops, neighbor->section, neighbor); + r = ordered_hashmap_ensure_put(&network->neighbors_by_section, &neighbor_section_hash_ops, neighbor->section, neighbor); if (r < 0) return r; @@ -69,7 +125,7 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned } static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) { - _cleanup_(neighbor_freep) Neighbor *dest = NULL; + _cleanup_(neighbor_unrefp) Neighbor *dest = NULL; assert(neighbor); assert(ret); @@ -78,7 +134,8 @@ static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) { if (!dest) return -ENOMEM; - /* Unset all pointers */ + /* Clear the reference counter and all pointers */ + dest->n_ref = 1; dest->link = NULL; dest->network = NULL; dest->section = NULL; @@ -90,7 +147,7 @@ static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) { static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) { assert(neighbor); - siphash24_compress(&neighbor->family, sizeof(neighbor->family), state); + siphash24_compress_typesafe(neighbor->family, state); if (!IN_SET(neighbor->family, AF_INET, AF_INET6)) /* treat any other address family as AF_UNSPEC */ @@ -98,7 +155,7 @@ static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) /* Equality of neighbors are given by the destination address. * See neigh_lookup() in the kernel. */ - siphash24_compress(&neighbor->in_addr, FAMILY_ADDRESS_SIZE(neighbor->family), state); + in_addr_hash_func(&neighbor->in_addr, neighbor->family, state); } static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { @@ -115,19 +172,6 @@ static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { return memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family)); } -DEFINE_PRIVATE_HASH_OPS( - neighbor_hash_ops, - Neighbor, - neighbor_hash_func, - neighbor_compare_func); - -DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( - neighbor_hash_ops_free, - Neighbor, - neighbor_hash_func, - neighbor_compare_func, - neighbor_free); - static int neighbor_get_request(Link *link, const Neighbor *neighbor, Request **ret) { Request *req; @@ -152,7 +196,7 @@ static int neighbor_get_request(Link *link, const Neighbor *neighbor, Request ** return 0; } -static int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) { +int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) { Neighbor *existing; assert(link); @@ -167,19 +211,21 @@ static int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) { return 0; } -static int neighbor_add(Link *link, Neighbor *neighbor) { +static int neighbor_attach(Link *link, Neighbor *neighbor) { int r; assert(link); assert(neighbor); + assert(!neighbor->link); - r = set_ensure_put(&link->neighbors, &neighbor_hash_ops_free, neighbor); + r = set_ensure_put(&link->neighbors, &neighbor_hash_ops_detach, neighbor); if (r < 0) return r; if (r == 0) return -EEXIST; neighbor->link = link; + neighbor_ref(neighbor); return 0; } @@ -279,7 +325,7 @@ static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_messag } static int link_request_neighbor(Link *link, const Neighbor *neighbor) { - _cleanup_(neighbor_freep) Neighbor *tmp = NULL; + _cleanup_(neighbor_unrefp) Neighbor *tmp = NULL; Neighbor *existing = NULL; int r; @@ -308,7 +354,7 @@ static int link_request_neighbor(Link *link, const Neighbor *neighbor) { log_neighbor_debug(tmp, "Requesting", link); r = link_queue_request_safe(link, REQUEST_TYPE_NEIGHBOR, tmp, - neighbor_free, + neighbor_unref, neighbor_hash_func, neighbor_compare_func, neighbor_process_request, @@ -353,35 +399,51 @@ int link_request_static_neighbors(Link *link) { return 0; } -static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) { int r; assert(m); - assert(link); + assert(rreq); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; + Link *link = ASSERT_PTR(rreq->link); + Neighbor *neighbor = ASSERT_PTR(rreq->userdata); + + if (link->state == LINK_STATE_LINGER) + return 0; r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -ESRCH) + if (r < 0) { /* Neighbor may not exist because it already got deleted, ignore that. */ - log_link_message_warning_errno(link, m, r, "Could not remove neighbor"); + log_link_message_full_errno(link, m, + (r == -ESRCH || !neighbor->link) ? LOG_DEBUG : LOG_WARNING, + r, "Could not remove neighbor"); + + if (neighbor->link) { + /* If the neighbor cannot be removed, then assume the neighbor is already removed. */ + log_neighbor_debug(neighbor, "Forgetting", link); + + Request *req; + if (neighbor_get_request(link, neighbor, &req) >= 0) + neighbor_enter_removed(req->userdata); + + neighbor_detach(neighbor); + } + } return 1; } -static int neighbor_remove(Neighbor *neighbor) { +int neighbor_remove(Neighbor *neighbor, Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - Request *req; - Link *link; int r; assert(neighbor); - assert(neighbor->link); - assert(neighbor->link->manager); - assert(neighbor->link->manager->rtnl); + assert(link); + assert(link->manager); + assert(link->manager->rtnl); - link = neighbor->link; + /* If the neighbor is remembered, then use the remembered object. */ + (void) neighbor_get(link, neighbor, &neighbor); log_neighbor_debug(neighbor, "Removing", link); @@ -394,17 +456,11 @@ static int neighbor_remove(Neighbor *neighbor) { if (r < 0) return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, m, neighbor_remove_handler, - link_netlink_destroy_callback, link); + r = link_remove_request_add(link, neighbor, neighbor, link->manager->rtnl, m, neighbor_remove_handler); if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); + return log_link_error_errno(link, r, "Could not queue rtnetlink message: %m"); neighbor_enter_removing(neighbor); - if (neighbor_get_request(neighbor->link, neighbor, &req) >= 0) - neighbor_enter_removing(req->userdata); - return 0; } @@ -440,13 +496,13 @@ int link_drop_foreign_neighbors(Link *link) { if (!neighbor_is_marked(neighbor)) continue; - RET_GATHER(r, neighbor_remove(neighbor)); + RET_GATHER(r, neighbor_remove(neighbor, link)); } return r; } -int link_drop_managed_neighbors(Link *link) { +int link_drop_static_neighbors(Link *link) { Neighbor *neighbor; int r = 0; @@ -454,14 +510,14 @@ int link_drop_managed_neighbors(Link *link) { SET_FOREACH(neighbor, link->neighbors) { /* Do not touch nexthops managed by kernel or other tools. */ - if (neighbor->source == NETWORK_CONFIG_SOURCE_FOREIGN) + if (neighbor->source != NETWORK_CONFIG_SOURCE_STATIC) continue; /* Ignore neighbors not assigned yet or already removing. */ if (!neighbor_exists(neighbor)) continue; - RET_GATHER(r, neighbor_remove(neighbor)); + RET_GATHER(r, neighbor_remove(neighbor, link)); } return r; @@ -477,7 +533,7 @@ void link_foreignize_neighbors(Link *link) { } int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { - _cleanup_(neighbor_freep) Neighbor *tmp = NULL; + _cleanup_(neighbor_unrefp) Neighbor *tmp = NULL; Neighbor *neighbor = NULL; Request *req = NULL; uint16_t type, state; @@ -510,10 +566,9 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, if (r < 0) { log_warning_errno(r, "rtnl: received neighbor message with invalid state, ignoring: %m"); return 0; - } else if (!FLAGS_SET(state, NUD_PERMANENT)) { - log_debug("rtnl: received non-static neighbor, ignoring."); + } else if (!FLAGS_SET(state, NUD_PERMANENT)) + /* Currently, we are interested in only static neighbors. */ return 0; - } r = sd_rtnl_message_neigh_get_ifindex(message, &ifindex); if (r < 0) { @@ -525,15 +580,13 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, } r = link_get_by_index(m, ifindex, &link); - if (r < 0) { + if (r < 0) /* when enumerating we might be out of sync, but we will get the neighbor again. Also, * kernel sends messages about neighbors after a link is removed. So, just ignore it. */ - log_debug("rtnl: received neighbor for link '%d' we don't know about, ignoring.", ifindex); return 0; - } - tmp = new0(Neighbor, 1); - if (!tmp) + r = neighbor_new(&tmp); + if (r < 0) return log_oom(); /* First, retrieve the fundamental information about the neighbor. */ @@ -541,7 +594,10 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, if (r < 0) { log_link_warning(link, "rtnl: received neighbor message without family, ignoring."); return 0; - } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { + } + if (tmp->family == AF_BRIDGE) /* Currently, we do not support it. */ + return 0; + if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family); return 0; } @@ -560,7 +616,7 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, if (neighbor) { neighbor_enter_removed(neighbor); log_neighbor_debug(neighbor, "Forgetting removed", link); - neighbor_free(neighbor); + neighbor_detach(neighbor); } else log_neighbor_debug(tmp, "Kernel removed unknown", link); @@ -572,12 +628,12 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, /* If we did not know the neighbor, then save it. */ if (!neighbor) { - r = neighbor_add(link, tmp); + r = neighbor_attach(link, tmp); if (r < 0) { log_link_warning_errno(link, r, "Failed to save received neighbor, ignoring: %m"); return 0; } - neighbor = TAKE_PTR(tmp); + neighbor = tmp; is_new = true; } @@ -638,9 +694,9 @@ int network_drop_invalid_neighbors(Network *network) { Neighbor *dup; if (neighbor_section_verify(neighbor) < 0) { - /* Drop invalid [Neighbor] sections. Note that neighbor_free() will drop the + /* Drop invalid [Neighbor] sections. Note that neighbor_detach() will drop the * neighbor from neighbors_by_section. */ - neighbor_free(neighbor); + neighbor_detach(neighbor); continue; } @@ -648,17 +704,17 @@ int network_drop_invalid_neighbors(Network *network) { dup = set_remove(neighbors, neighbor); if (dup) { log_warning("%s: Duplicated neighbor settings for %s is specified at line %u and %u, " - "dropping the address setting specified at line %u.", + "dropping the neighbor setting specified at line %u.", dup->section->filename, IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr), neighbor->section->line, dup->section->line, dup->section->line); - /* neighbor_free() will drop the address from neighbors_by_section. */ - neighbor_free(dup); + /* neighbor_detach() will drop the neighbor from neighbors_by_section. */ + neighbor_detach(dup); } - /* Use neighbor_hash_ops, instead of neighbor_hash_ops_free. Otherwise, the Neighbor objects - * will be freed. */ + /* Use neighbor_hash_ops, instead of neighbor_hash_ops_detach. Otherwise, the Neighbor objects + * will be detached. */ r = set_ensure_put(&neighbors, &neighbor_hash_ops, neighbor); if (r < 0) return log_oom(); @@ -681,7 +737,7 @@ int config_parse_neighbor_address( void *data, void *userdata) { - _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL; + _cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL; Network *network = ASSERT_PTR(userdata); int r; @@ -724,7 +780,7 @@ int config_parse_neighbor_lladdr( void *data, void *userdata) { - _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL; + _cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL; Network *network = ASSERT_PTR(userdata); int r; |