mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 06:01:57 +00:00
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:cefa31a9d4
("netfilter: nft_log: perform module load from nf_tables") Fixes:a38b5b56d6
("netfilter: nf_log: add module softdeps") Reported-and-tested-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
cc8072153a
commit
b53deef054
@ -19,6 +19,7 @@
|
|||||||
#include <linux/netfilter_bridge/ebtables.h>
|
#include <linux/netfilter_bridge/ebtables.h>
|
||||||
#include <linux/netfilter_arp/arp_tables.h>
|
#include <linux/netfilter_arp/arp_tables.h>
|
||||||
#include <net/netfilter/nf_tables.h>
|
#include <net/netfilter/nf_tables.h>
|
||||||
|
#include <net/netfilter/nf_log.h>
|
||||||
|
|
||||||
/* Used for matches where *info is larger than X byte */
|
/* Used for matches where *info is larger than X byte */
|
||||||
#define NFT_MATCH_LARGE_THRESH 192
|
#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();
|
nft_compat_wait_for_destructors();
|
||||||
|
|
||||||
ret = xt_check_target(&par, size, proto, inv);
|
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;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* The standard target cannot be used */
|
/* The standard target cannot be used */
|
||||||
if (!target->target)
|
if (!target->target)
|
||||||
|
@ -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)
|
static int log_tg_check(const struct xt_tgchk_param *par)
|
||||||
{
|
{
|
||||||
const struct xt_log_info *loginfo = par->targinfo;
|
const struct xt_log_info *loginfo = par->targinfo;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6)
|
if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -58,7 +59,14 @@ static int log_tg_check(const struct xt_tgchk_param *par)
|
|||||||
return -EINVAL;
|
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)
|
static void log_tg_destroy(const struct xt_tgdtor_param *par)
|
||||||
|
@ -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)
|
static int nflog_tg_check(const struct xt_tgchk_param *par)
|
||||||
{
|
{
|
||||||
const struct xt_nflog_info *info = par->targinfo;
|
const struct xt_nflog_info *info = par->targinfo;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (info->flags & ~XT_NFLOG_MASK)
|
if (info->flags & ~XT_NFLOG_MASK)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (info->prefix[sizeof(info->prefix) - 1] != '\0')
|
if (info->prefix[sizeof(info->prefix) - 1] != '\0')
|
||||||
return -EINVAL;
|
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)
|
static void nflog_tg_destroy(const struct xt_tgdtor_param *par)
|
||||||
|
Loading…
Reference in New Issue
Block a user