summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-neighbor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/networkd-neighbor.c')
-rw-r--r--src/network/networkd-neighbor.c230
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;