Commit b53deef0 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso
Browse files

netfilter: log: work around missing softdep backend module



iptables/nftables has two types of log modules:

1. backend, e.g. nf_log_syslog, which implement the functionality
2. frontend, e.g. xt_LOG or nft_log, which call the functionality
   provided by backend based on nf_tables or xtables rule set.

Problem is that the request_module() call to load the backed in
nf_logger_find_get() might happen with nftables transaction mutex held
in case the call path is via nf_tables/nft_compat.

This can cause deadlocks (see 'Fixes' tags for details).

The chosen solution as to let modprobe deal with this by adding 'pre: '
soft dep tag to xt_LOG (to load the syslog backend) and xt_NFLOG (to
load nflog backend).

Eric reports that this breaks on systems with older modprobe that
doesn't support softdeps.

Another, similar issue occurs when someone either insmods xt_(NF)LOG
directly or unloads the backend module (possible if no log frontend
is in use): because the frontend module is already loaded, modprobe is
not invoked again so the softdep isn't evaluated.

Add a workaround: If nf_logger_find_get() returns -ENOENT and call
is not via nft_compat, load the backend explicitly and try again.

Else, let nft_compat ask for deferred request_module via nf_tables
infra.

Softdeps are kept in-place, so with newer modprobe the dependencies
are resolved from userspace.

Fixes: cefa31a9 ("netfilter: nft_log: perform module load from nf_tables")
Fixes: a38b5b56 ("netfilter: nf_log: add module softdeps")
Reported-and-tested-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent cc807215
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_arp/arp_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_log.h>

/* Used for matches where *info is larger than X byte */
#define NFT_MATCH_LARGE_THRESH	192
@@ -257,8 +258,22 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
	nft_compat_wait_for_destructors();

	ret = xt_check_target(&par, size, proto, inv);
	if (ret < 0)
	if (ret < 0) {
		if (ret == -ENOENT) {
			const char *modname = NULL;

			if (strcmp(target->name, "LOG") == 0)
				modname = "nf_log_syslog";
			else if (strcmp(target->name, "NFLOG") == 0)
				modname = "nfnetlink_log";

			if (modname &&
			    nft_request_module(ctx->net, "%s", modname) == -EAGAIN)
				return -EAGAIN;
		}

		return ret;
	}

	/* The standard target cannot be used */
	if (!target->target)
+9 −1
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ log_tg(struct sk_buff *skb, const struct xt_action_param *par)
static int log_tg_check(const struct xt_tgchk_param *par)
{
	const struct xt_log_info *loginfo = par->targinfo;
	int ret;

	if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6)
		return -EINVAL;
@@ -58,7 +59,14 @@ static int log_tg_check(const struct xt_tgchk_param *par)
		return -EINVAL;
	}

	return nf_logger_find_get(par->family, NF_LOG_TYPE_LOG);
	ret = nf_logger_find_get(par->family, NF_LOG_TYPE_LOG);
	if (ret != 0 && !par->nft_compat) {
		request_module("%s", "nf_log_syslog");

		ret = nf_logger_find_get(par->family, NF_LOG_TYPE_LOG);
	}

	return ret;
}

static void log_tg_destroy(const struct xt_tgdtor_param *par)
+9 −1
Original line number Diff line number Diff line
@@ -42,13 +42,21 @@ nflog_tg(struct sk_buff *skb, const struct xt_action_param *par)
static int nflog_tg_check(const struct xt_tgchk_param *par)
{
	const struct xt_nflog_info *info = par->targinfo;
	int ret;

	if (info->flags & ~XT_NFLOG_MASK)
		return -EINVAL;
	if (info->prefix[sizeof(info->prefix) - 1] != '\0')
		return -EINVAL;

	return nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
	ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
	if (ret != 0 && !par->nft_compat) {
		request_module("%s", "nfnetlink_log");

		ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
	}

	return ret;
}

static void nflog_tg_destroy(const struct xt_tgdtor_param *par)