Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: cefa31a9d461 ("netfilter: nft_log: perform module load from nf_tables")
Fixes: a38b5b56d6f4 ("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>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
b53deef0 cc807215

+34 -3
+16 -1
net/netfilter/nft_compat.c
··· 19 19 #include <linux/netfilter_bridge/ebtables.h> 20 20 #include <linux/netfilter_arp/arp_tables.h> 21 21 #include <net/netfilter/nf_tables.h> 22 + #include <net/netfilter/nf_log.h> 22 23 23 24 /* Used for matches where *info is larger than X byte */ 24 25 #define NFT_MATCH_LARGE_THRESH 192 ··· 258 257 nft_compat_wait_for_destructors(); 259 258 260 259 ret = xt_check_target(&par, size, proto, inv); 261 - if (ret < 0) 260 + if (ret < 0) { 261 + if (ret == -ENOENT) { 262 + const char *modname = NULL; 263 + 264 + if (strcmp(target->name, "LOG") == 0) 265 + modname = "nf_log_syslog"; 266 + else if (strcmp(target->name, "NFLOG") == 0) 267 + modname = "nfnetlink_log"; 268 + 269 + if (modname && 270 + nft_request_module(ctx->net, "%s", modname) == -EAGAIN) 271 + return -EAGAIN; 272 + } 273 + 262 274 return ret; 275 + } 263 276 264 277 /* The standard target cannot be used */ 265 278 if (!target->target)
+9 -1
net/netfilter/xt_LOG.c
··· 44 44 static int log_tg_check(const struct xt_tgchk_param *par) 45 45 { 46 46 const struct xt_log_info *loginfo = par->targinfo; 47 + int ret; 47 48 48 49 if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6) 49 50 return -EINVAL; ··· 59 58 return -EINVAL; 60 59 } 61 60 62 - return nf_logger_find_get(par->family, NF_LOG_TYPE_LOG); 61 + ret = nf_logger_find_get(par->family, NF_LOG_TYPE_LOG); 62 + if (ret != 0 && !par->nft_compat) { 63 + request_module("%s", "nf_log_syslog"); 64 + 65 + ret = nf_logger_find_get(par->family, NF_LOG_TYPE_LOG); 66 + } 67 + 68 + return ret; 63 69 } 64 70 65 71 static void log_tg_destroy(const struct xt_tgdtor_param *par)
+9 -1
net/netfilter/xt_NFLOG.c
··· 42 42 static int nflog_tg_check(const struct xt_tgchk_param *par) 43 43 { 44 44 const struct xt_nflog_info *info = par->targinfo; 45 + int ret; 45 46 46 47 if (info->flags & ~XT_NFLOG_MASK) 47 48 return -EINVAL; 48 49 if (info->prefix[sizeof(info->prefix) - 1] != '\0') 49 50 return -EINVAL; 50 51 51 - return nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG); 52 + ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG); 53 + if (ret != 0 && !par->nft_compat) { 54 + request_module("%s", "nfnetlink_log"); 55 + 56 + ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG); 57 + } 58 + 59 + return ret; 52 60 } 53 61 54 62 static void nflog_tg_destroy(const struct xt_tgdtor_param *par)