summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-nexthop.c
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2023-12-12 19:55:45 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2023-12-14 10:58:26 +0100
commitcdf3bee6a2dd281e36ca1e1ac6e4a05e46caa64e (patch)
treec14326b785d676d6f3373d4614f731391a631a16 /src/network/networkd-nexthop.c
parentnetwork/nexthop: manage all nexthops by manager (diff)
downloadsystemd-cdf3bee6a2dd281e36ca1e1ac6e4a05e46caa64e.tar.xz
systemd-cdf3bee6a2dd281e36ca1e1ac6e4a05e46caa64e.zip
network/nexthop: do not add NextHop object to Link on requesting
Then, all nexthops managed by networkd really exist (unless the kernel silently removes a nexthop). This is the same for nexthop already done by 3c283289aefb3cfb8bfa5c759209368b63d1692c and 0a0c2672dbd22dc85d660e5baa7e1bef701beb88 (for address), and 5d098f5d3614d1c0be7c825925637e9ab3d904fb (for neighbor).
Diffstat (limited to 'src/network/networkd-nexthop.c')
-rw-r--r--src/network/networkd-nexthop.c312
1 files changed, 185 insertions, 127 deletions
diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c
index 5f2a8282bf..0683146492 100644
--- a/src/network/networkd-nexthop.c
+++ b/src/network/networkd-nexthop.c
@@ -255,12 +255,70 @@ static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) {
return -ENOENT;
}
-static int nexthop_add(Manager *manager, NextHop *nexthop) {
+static int nexthop_get_request_by_id(Manager *manager, uint32_t id, Request **ret) {
+ Request *req;
+
+ assert(manager);
+
+ req = ordered_set_get(
+ manager->request_queue,
+ &(Request) {
+ .type = REQUEST_TYPE_NEXTHOP,
+ .userdata = (void*) &(const NextHop) { .id = id },
+ .hash_func = (hash_func_t) nexthop_hash_func,
+ .compare_func = (compare_func_t) nexthop_compare_func,
+ });
+ if (!req)
+ return -ENOENT;
+
+ if (ret)
+ *ret = req;
+ return 0;
+}
+
+static int nexthop_get_request(Link *link, const NextHop *in, Request **ret) {
+ Request *req;
+ int ifindex;
+
+ assert(link);
+ assert(link->manager);
+ assert(in);
+
+ if (in->id > 0)
+ return nexthop_get_request_by_id(link->manager, in->id, ret);
+
+ ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
+
+ ORDERED_SET_FOREACH(req, link->manager->request_queue) {
+ if (req->type != REQUEST_TYPE_NEXTHOP)
+ continue;
+
+ NextHop *nexthop = ASSERT_PTR(req->userdata);
+ if (nexthop->ifindex != ifindex)
+ continue;
+ if (nexthop_compare_full(nexthop, in) != 0)
+ continue;
+
+ if (ret)
+ *ret = req;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int nexthop_add_new(Manager *manager, uint32_t id, NextHop **ret) {
+ _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
int r;
assert(manager);
- assert(nexthop);
- assert(nexthop->id > 0);
+ assert(id > 0);
+
+ r = nexthop_new(&nexthop);
+ if (r < 0)
+ return r;
+
+ nexthop->id = id;
r = hashmap_ensure_put(&manager->nexthops_by_id, &nexthop_hash_ops, UINT32_TO_PTR(nexthop->id), nexthop);
if (r < 0)
@@ -269,6 +327,11 @@ static int nexthop_add(Manager *manager, NextHop *nexthop) {
return -EEXIST;
nexthop->manager = manager;
+
+ if (ret)
+ *ret = nexthop;
+
+ TAKE_PTR(nexthop);
return 0;
}
@@ -305,6 +368,8 @@ static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) {
for (uint32_t id = 1; id < UINT32_MAX; id++) {
if (nexthop_get_by_id(manager, id, NULL) >= 0)
continue;
+ if (nexthop_get_request_by_id(manager, id, NULL) >= 0)
+ continue;
if (set_contains(ids, UINT32_TO_PTR(id)))
continue;
@@ -362,6 +427,7 @@ static int nexthop_remove(NextHop *nexthop) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
Manager *manager;
Link *link = NULL;
+ Request *req;
int r;
manager = ASSERT_PTR(ASSERT_PTR(nexthop)->manager);
@@ -392,6 +458,9 @@ static int nexthop_remove(NextHop *nexthop) {
link_ref(link); /* link may be NULL, link_ref() is OK with that */
nexthop_enter_removing(nexthop);
+ if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0)
+ nexthop_enter_removing(req->userdata);
+
return 0;
}
@@ -528,10 +597,12 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
}
static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
+ NextHop *existing;
int r;
assert(req);
assert(link);
+ assert(link->manager);
assert(nexthop);
if (!nexthop_is_ready_to_configure(link, nexthop))
@@ -542,42 +613,49 @@ static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
return log_link_warning_errno(link, r, "Failed to configure nexthop");
nexthop_enter_configuring(nexthop);
+ if (nexthop_get_by_id(link->manager, nexthop->id, &existing) >= 0)
+ nexthop_enter_configuring(existing);
+
return 1;
}
-static int link_request_nexthop(Link *link, NextHop *nexthop) {
- NextHop *existing;
+static int link_request_nexthop(Link *link, const NextHop *nexthop) {
+ _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+ NextHop *existing = NULL;
int r;
assert(link);
+ assert(link->manager);
assert(nexthop);
assert(nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN);
- if (nexthop_get(link, nexthop, &existing) < 0) {
- _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+ if (nexthop_get_request(link, nexthop, NULL) >= 0)
+ return 0; /* already requested, skipping. */
- r = nexthop_dup(nexthop, &tmp);
- if (r < 0)
- return r;
+ r = nexthop_dup(nexthop, &tmp);
+ if (r < 0)
+ return r;
+ if (nexthop_get(link, nexthop, &existing) < 0) {
r = nexthop_acquire_id(link->manager, tmp);
if (r < 0)
return r;
+ } else {
+ /* Copy ID */
+ assert(tmp->id == 0 || tmp->id == existing->id);
+ tmp->id = existing->id;
- if (nexthop_bound_to_link(tmp))
- tmp->ifindex = link->ifindex;
-
- r = nexthop_add(link->manager, tmp);
- if (r < 0)
- return r;
+ /* Copy state for logging below. */
+ tmp->state = existing->state;
+ }
- existing = TAKE_PTR(tmp);
- } else
- existing->source = nexthop->source;
+ if (nexthop_bound_to_link(tmp))
+ tmp->ifindex = link->ifindex;
- log_nexthop_debug(existing, "Requesting", link->manager);
+ log_nexthop_debug(tmp, "Requesting", link->manager);
r = link_queue_request_safe(link, REQUEST_TYPE_NEXTHOP,
- existing, NULL,
+ tmp,
+ nexthop_free,
nexthop_hash_func,
nexthop_compare_func,
nexthop_process_request,
@@ -587,7 +665,11 @@ static int link_request_nexthop(Link *link, NextHop *nexthop) {
if (r <= 0)
return r;
- nexthop_enter_requesting(existing);
+ nexthop_enter_requesting(tmp);
+ if (existing)
+ nexthop_enter_requesting(existing);
+
+ TAKE_PTR(tmp);
return 1;
}
@@ -751,13 +833,13 @@ static int nexthop_update_group(NextHop *nexthop, const struct nexthop_grp *grou
}
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
- _cleanup_(nexthop_freep) NextHop *tmp = NULL;
_cleanup_free_ void *raw_group = NULL;
- NextHop *nexthop = NULL;
size_t raw_group_size;
- uint32_t ifindex;
uint16_t type;
- Link *link = NULL;
+ uint32_t id, ifindex;
+ NextHop *nexthop = NULL;
+ Request *req = NULL;
+ bool is_new = false;
int r;
assert(rtnl);
@@ -781,131 +863,107 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
return 0;
}
- r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
- if (r < 0 && r != -ENODATA) {
- log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+ r = sd_netlink_message_read_u32(message, NHA_ID, &id);
+ if (r == -ENODATA) {
+ log_warning_errno(r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
return 0;
- } else if (r >= 0) {
- if (ifindex <= 0) {
- log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex);
- return 0;
- }
-
- r = link_get_by_index(m, ifindex, &link);
- if (r < 0) {
- if (!m->enumerating)
- log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex);
- return 0;
- }
- }
-
- r = nexthop_new(&tmp);
- if (r < 0)
- return log_oom();
-
- r = sd_rtnl_message_get_family(message, &tmp->family);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m");
+ } else if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
return 0;
- } else if (!IN_SET(tmp->family, AF_UNSPEC, AF_INET, AF_INET6)) {
- log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
+ } else if (id == 0) {
+ log_warning("rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
return 0;
}
- r = sd_rtnl_message_nexthop_get_protocol(message, &tmp->protocol);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get nexthop protocol, ignoring: %m");
- return 0;
- }
+ (void) nexthop_get_by_id(m, id, &nexthop);
+ (void) nexthop_get_request_by_id(m, id, &req);
- r = sd_rtnl_message_nexthop_get_flags(message, &tmp->flags);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get nexthop flags, ignoring: %m");
- return 0;
- }
+ if (type == RTM_DELNEXTHOP) {
+ if (nexthop) {
+ nexthop_enter_removed(nexthop);
+ log_nexthop_debug(nexthop, "Forgetting removed", m);
+ nexthop_free(nexthop);
+ } else
+ log_nexthop_debug(&(const NextHop) { .id = id }, "Kernel removed unknown", m);
+
+ if (req)
+ nexthop_enter_removed(req->userdata);
- r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group);
- if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
return 0;
- } else if (r >= 0) {
- r = nexthop_update_group(tmp, raw_group, raw_group_size);
- if (r < 0)
- return 0;
}
- if (tmp->family != AF_UNSPEC) {
- r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw);
- if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
+ /* If we did not know the nexthop, then save it. */
+ if (!nexthop) {
+ r = nexthop_add_new(m, id, &nexthop);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to add received nexthop, ignoring: %m");
return 0;
}
- }
- r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
- return 0;
+ is_new = true;
}
- tmp->blackhole = r;
- r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
- if (r == -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
- return 0;
- } else if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
- return 0;
- } else if (tmp->id == 0) {
- log_link_warning(link, "rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
- return 0;
+ /* Also update information that cannot be obtained through netlink notification. */
+ if (req && req->waiting_reply) {
+ NextHop *n = ASSERT_PTR(req->userdata);
+
+ nexthop->source = n->source;
}
- /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not
- * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */
- if (!nexthop_bound_to_link(tmp))
- link = NULL;
+ r = sd_rtnl_message_get_family(message, &nexthop->family);
+ if (r < 0)
+ log_debug_errno(r, "rtnl: could not get nexthop family, ignoring: %m");
- tmp->ifindex = link ? link->ifindex : 0;
+ r = sd_rtnl_message_nexthop_get_protocol(message, &nexthop->protocol);
+ if (r < 0)
+ log_debug_errno(r, "rtnl: could not get nexthop protocol, ignoring: %m");
- (void) nexthop_get_by_id(m, tmp->id, &nexthop);
+ r = sd_rtnl_message_nexthop_get_flags(message, &nexthop->flags);
+ if (r < 0)
+ log_debug_errno(r, "rtnl: could not get nexthop flags, ignoring: %m");
- switch (type) {
- case RTM_NEWNEXTHOP:
- if (nexthop) {
- nexthop->flags = tmp->flags;
- nexthop_enter_configured(nexthop);
- log_nexthop_debug(tmp, "Received remembered", m);
- } else {
- nexthop_enter_configured(tmp);
- log_nexthop_debug(tmp, "Remembering", m);
-
- r = nexthop_add(m, tmp);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m");
- return 0;
- }
+ r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group);
+ if (r == -ENODATA)
+ nexthop->group = hashmap_free_free(nexthop->group);
+ else if (r < 0)
+ log_debug_errno(r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
+ else
+ (void) nexthop_update_group(nexthop, raw_group, raw_group_size);
+
+ if (nexthop->family != AF_UNSPEC) {
+ r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw);
+ if (r == -ENODATA)
+ nexthop->gw = IN_ADDR_NULL;
+ else if (r < 0)
+ log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
+ }
- TAKE_PTR(tmp);
- }
+ r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE);
+ if (r < 0)
+ log_debug_errno(r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
+ else
+ nexthop->blackhole = r;
- break;
- case RTM_DELNEXTHOP:
- if (nexthop) {
- nexthop_enter_removed(nexthop);
- if (nexthop->state == 0) {
- log_nexthop_debug(nexthop, "Forgetting", m);
- nexthop_free(nexthop);
- } else
- log_nexthop_debug(nexthop, "Removed", m);
- } else
- log_nexthop_debug(tmp, "Kernel removed unknown", m);
- break;
+ r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
+ if (r == -ENODATA)
+ nexthop->ifindex = 0;
+ else if (r < 0)
+ log_debug_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+ else if (ifindex > INT32_MAX)
+ log_debug_errno(r, "rtnl: received invalid NHA_OIF attribute, ignoring: %m");
+ else
+ nexthop->ifindex = (int) ifindex;
- default:
- assert_not_reached();
- }
+ /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not
+ * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */
+ if (!nexthop_bound_to_link(nexthop))
+ nexthop->ifindex = 0;
+
+ nexthop_enter_configured(nexthop);
+ if (req)
+ nexthop_enter_configured(req->userdata);
+ log_nexthop_debug(nexthop, is_new ? "Remembering" : "Received remembered", m);
return 1;
}