Commit 2794cdb0 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: nfnetlink: allow to detect if ctnetlink listeners exist

At this time, every new conntrack gets the 'event cache extension'
enabled for it.

This is because the 'net.netfilter.nf_conntrack_events' sysctl defaults
to 1.

Changing the default to 0 means that commands that rely on the event
notification extension, e.g. 'conntrack -E' or conntrackd, stop working.

We COULD detect if there is a listener by means of
'nfnetlink_has_listeners()' and only add the extension if this is true.

The downside is a dependency from conntrack module to nfnetlink module.

This adds a different way: inc/dec a counter whenever a ctnetlink group
is being (un)subscribed and toggle a flag in struct net.

Next patches will take advantage of this and will only add the event
extension if the flag is set.
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 8169ff58
...@@ -95,6 +95,7 @@ struct nf_ip_net { ...@@ -95,6 +95,7 @@ struct nf_ip_net {
struct netns_ct { struct netns_ct {
#ifdef CONFIG_NF_CONNTRACK_EVENTS #ifdef CONFIG_NF_CONNTRACK_EVENTS
bool ctnetlink_has_listener;
bool ecache_dwork_pending; bool ecache_dwork_pending;
#endif #endif
u8 sysctl_log_invalid; /* Log invalid packets */ u8 sysctl_log_invalid; /* Log invalid packets */
......
...@@ -45,6 +45,7 @@ MODULE_DESCRIPTION("Netfilter messages via netlink socket"); ...@@ -45,6 +45,7 @@ MODULE_DESCRIPTION("Netfilter messages via netlink socket");
static unsigned int nfnetlink_pernet_id __read_mostly; static unsigned int nfnetlink_pernet_id __read_mostly;
struct nfnl_net { struct nfnl_net {
unsigned int ctnetlink_listeners;
struct sock *nfnl; struct sock *nfnl;
}; };
...@@ -654,7 +655,6 @@ static void nfnetlink_rcv(struct sk_buff *skb) ...@@ -654,7 +655,6 @@ static void nfnetlink_rcv(struct sk_buff *skb)
netlink_rcv_skb(skb, nfnetlink_rcv_msg); netlink_rcv_skb(skb, nfnetlink_rcv_msg);
} }
#ifdef CONFIG_MODULES
static int nfnetlink_bind(struct net *net, int group) static int nfnetlink_bind(struct net *net, int group)
{ {
const struct nfnetlink_subsystem *ss; const struct nfnetlink_subsystem *ss;
...@@ -670,9 +670,44 @@ static int nfnetlink_bind(struct net *net, int group) ...@@ -670,9 +670,44 @@ static int nfnetlink_bind(struct net *net, int group)
rcu_read_unlock(); rcu_read_unlock();
if (!ss) if (!ss)
request_module_nowait("nfnetlink-subsys-%d", type); request_module_nowait("nfnetlink-subsys-%d", type);
#ifdef CONFIG_NF_CONNTRACK_EVENTS
if (type == NFNL_SUBSYS_CTNETLINK) {
struct nfnl_net *nfnlnet = nfnl_pernet(net);
nfnl_lock(NFNL_SUBSYS_CTNETLINK);
if (WARN_ON_ONCE(nfnlnet->ctnetlink_listeners == UINT_MAX)) {
nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
return -EOVERFLOW;
}
nfnlnet->ctnetlink_listeners++;
if (nfnlnet->ctnetlink_listeners == 1)
WRITE_ONCE(net->ct.ctnetlink_has_listener, true);
nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
}
#endif
return 0; return 0;
} }
static void nfnetlink_unbind(struct net *net, int group)
{
#ifdef CONFIG_NF_CONNTRACK_EVENTS
int type = nfnl_group2type[group];
if (type == NFNL_SUBSYS_CTNETLINK) {
struct nfnl_net *nfnlnet = nfnl_pernet(net);
nfnl_lock(NFNL_SUBSYS_CTNETLINK);
WARN_ON_ONCE(nfnlnet->ctnetlink_listeners == 0);
nfnlnet->ctnetlink_listeners--;
if (nfnlnet->ctnetlink_listeners == 0)
WRITE_ONCE(net->ct.ctnetlink_has_listener, false);
nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
}
#endif #endif
}
static int __net_init nfnetlink_net_init(struct net *net) static int __net_init nfnetlink_net_init(struct net *net)
{ {
...@@ -680,9 +715,8 @@ static int __net_init nfnetlink_net_init(struct net *net) ...@@ -680,9 +715,8 @@ static int __net_init nfnetlink_net_init(struct net *net)
struct netlink_kernel_cfg cfg = { struct netlink_kernel_cfg cfg = {
.groups = NFNLGRP_MAX, .groups = NFNLGRP_MAX,
.input = nfnetlink_rcv, .input = nfnetlink_rcv,
#ifdef CONFIG_MODULES
.bind = nfnetlink_bind, .bind = nfnetlink_bind,
#endif .unbind = nfnetlink_unbind,
}; };
nfnlnet->nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg); nfnlnet->nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment