netfilter: nfnetlink: allow to detect if ctnetlink listeners exist
authorFlorian Westphal <fw@strlen.de>
Mon, 25 Apr 2022 13:15:41 +0000 (15:15 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 13 May 2022 16:56:28 +0000 (18:56 +0200)
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: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netns/conntrack.h
net/netfilter/nfnetlink.c

index a71cfd4e2f212e608fb0f8a30061cab8dfb1794d..0677cd3de03444c4cbdc24cfda7c40d94057e0c4 100644 (file)
@@ -95,6 +95,7 @@ struct nf_ip_net {
 
 struct netns_ct {
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
+       bool ctnetlink_has_listener;
        bool ecache_dwork_pending;
 #endif
        u8                      sysctl_log_invalid; /* Log invalid packets */
index 7e2c8dd01408f6b165d65c6e3aa4715b42824ed5..ad3bbe34ca8829aa95a0dba1e08fbbe07539fe6c 100644 (file)
@@ -45,6 +45,7 @@ MODULE_DESCRIPTION("Netfilter messages via netlink socket");
 static unsigned int nfnetlink_pernet_id __read_mostly;
 
 struct nfnl_net {
+       unsigned int ctnetlink_listeners;
        struct sock *nfnl;
 };
 
@@ -654,7 +655,6 @@ static void nfnetlink_rcv(struct sk_buff *skb)
                netlink_rcv_skb(skb, nfnetlink_rcv_msg);
 }
 
-#ifdef CONFIG_MODULES
 static int nfnetlink_bind(struct net *net, int group)
 {
        const struct nfnetlink_subsystem *ss;
@@ -670,9 +670,44 @@ static int nfnetlink_bind(struct net *net, int group)
        rcu_read_unlock();
        if (!ss)
                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;
 }
+
+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
+}
 
 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 = {
                .groups = NFNLGRP_MAX,
                .input  = nfnetlink_rcv,
-#ifdef CONFIG_MODULES
                .bind   = nfnetlink_bind,
-#endif
+               .unbind = nfnetlink_unbind,
        };
 
        nfnlnet->nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg);