diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 2f9f7e9676..369f5d50e4 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -107,5 +107,7 @@ int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, O void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m); +size_t netlink_get_reply_callback_count(sd_netlink *nl); + /* TODO: to be exported later */ int sd_netlink_sendv(sd_netlink *nl, sd_netlink_message **messages, size_t msgcnt, uint32_t **ret_serial); diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 898f11d1ab..b6730b71dc 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -458,6 +458,12 @@ static int timeout_compare(const void *a, const void *b) { return CMP(x->timeout, y->timeout); } +size_t netlink_get_reply_callback_count(sd_netlink *nl) { + assert(nl); + + return hashmap_size(nl->reply_callbacks); +} + int sd_netlink_call_async( sd_netlink *nl, sd_netlink_slot **ret_slot, @@ -477,7 +483,7 @@ int sd_netlink_call_async( assert_return(!netlink_pid_changed(nl), -ECHILD); if (hashmap_size(nl->reply_callbacks) >= REPLY_CALLBACKS_MAX) - return -ERANGE; + return -EXFULL; r = hashmap_ensure_allocated(&nl->reply_callbacks, &trivial_hash_ops); if (r < 0) diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 90caefbc72..1695aacf76 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -7,6 +7,8 @@ #include "networkd-queue.h" #include "string-table.h" +#define REPLY_CALLBACK_COUNT_THRESHOLD 128 + static Request *request_free(Request *req) { if (!req) return NULL; @@ -220,6 +222,13 @@ int manager_process_requests(sd_event_source *s, void *userdata) { if (req->waiting_reply) continue; /* Waiting for netlink reply. */ + /* Typically, requests send netlink message asynchronously. If there are many requests + * queued, then this event may make reply callback queue in sd-netlink full. */ + if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD || + netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD || + fw_ctx_get_reply_callback_count(manager->fw_ctx) >= REPLY_CALLBACK_COUNT_THRESHOLD) + return 0; + r = req->process(req, link, req->userdata); if (r == 0) continue; diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 764ef5a018..e96b24a72d 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -8,6 +8,7 @@ #include "firewall-util.h" #include "firewall-util-private.h" #include "log.h" +#include "netlink-util.h" #include "string-table.h" static const char * const firewall_backend_table[_FW_BACKEND_MAX] = { @@ -90,6 +91,13 @@ FirewallContext *fw_ctx_free(FirewallContext *ctx) { return mfree(ctx); } +size_t fw_ctx_get_reply_callback_count(FirewallContext *ctx) { + if (!ctx || !ctx->nfnl) + return 0; + + return netlink_get_reply_callback_count(ctx->nfnl); +} + int fw_add_masquerade( FirewallContext **ctx, bool add, diff --git a/src/shared/firewall-util.h b/src/shared/firewall-util.h index 1e25a82a54..14e35bec8d 100644 --- a/src/shared/firewall-util.h +++ b/src/shared/firewall-util.h @@ -15,6 +15,8 @@ FirewallContext *fw_ctx_free(FirewallContext *ctx); DEFINE_TRIVIAL_CLEANUP_FUNC(FirewallContext *, fw_ctx_free); +size_t fw_ctx_get_reply_callback_count(FirewallContext *ctx); + int fw_add_masquerade( FirewallContext **ctx, bool add,