Skip to content

Commit

Permalink
netfilter: ctnetlink: make event listener tracking global
Browse files Browse the repository at this point in the history
pernet tracking doesn't work correctly because other netns might have
set NETLINK_LISTEN_ALL_NSID on its event socket.

In this case its expected that events originating in other net
namespaces are also received.

Making pernet-tracking work while also honoring NETLINK_LISTEN_ALL_NSID
requires much more intrusive changes both in netlink and nfnetlink,
f.e. adding a 'setsockopt' callback that lets nfnetlink know that the
event socket entered (or left) ALL_NSID mode.

Move to global tracking instead: if there is an event socket anywhere
on the system, all net namespaces which have conntrack enabled and
use autobind mode will allocate the ecache extension.

netlink_has_listeners() returns false only if the given group has no
subscribers in any net namespace, the 'net' argument passed to
nfnetlink_has_listeners is only used to derive the protocol (nfnetlink),
it has no other effect.

For proper NETLINK_LISTEN_ALL_NSID-aware pernet tracking of event
listeners a new netlink_has_net_listeners() is also needed.

Fixes: 90d1daa ("netfilter: conntrack: add nf_conntrack_events autodetect mode")
Reported-by: Bryce Kahle <bryce.kahle@datadoghq.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Florian Westphal authored and Pablo Neira Ayuso committed Feb 21, 2023
1 parent 05c07c0 commit fdf6491
Show file tree
Hide file tree
Showing 5 changed files with 14 additions and 6 deletions.
5 changes: 5 additions & 0 deletions include/linux/netfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,4 +488,9 @@ extern const struct nfnl_ct_hook __rcu *nfnl_ct_hook;
*/
DECLARE_PER_CPU(bool, nf_skb_duplicated);

/**
* Contains bitmask of ctnetlink event subscribers, if any.
* Can't be pernet due to NETLINK_LISTEN_ALL_NSID setsockopt flag.
*/
extern u8 nf_ctnetlink_has_listener;
#endif /*__LINUX_NETFILTER_H*/
1 change: 0 additions & 1 deletion include/net/netns/conntrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ struct nf_ip_net {

struct netns_ct {
#ifdef CONFIG_NF_CONNTRACK_EVENTS
u8 ctnetlink_has_listener;
bool ecache_dwork_pending;
#endif
u8 sysctl_log_invalid; /* Log invalid packets */
Expand Down
3 changes: 3 additions & 0 deletions net/netfilter/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,9 @@ const struct nf_ct_hook __rcu *nf_ct_hook __read_mostly;
EXPORT_SYMBOL_GPL(nf_ct_hook);

#if IS_ENABLED(CONFIG_NF_CONNTRACK)
u8 nf_ctnetlink_has_listener;
EXPORT_SYMBOL_GPL(nf_ctnetlink_has_listener);

const struct nf_nat_hook __rcu *nf_nat_hook __read_mostly;
EXPORT_SYMBOL_GPL(nf_nat_hook);

Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/nf_conntrack_ecache.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ bool nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp
break;
return true;
case 2: /* autodetect: no event listener, don't allocate extension. */
if (!READ_ONCE(net->ct.ctnetlink_has_listener))
if (!READ_ONCE(nf_ctnetlink_has_listener))
return true;
fallthrough;
case 1:
Expand Down
9 changes: 5 additions & 4 deletions net/netfilter/nfnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include <net/netlink.h>
#include <net/netns/generic.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>

MODULE_LICENSE("GPL");
Expand Down Expand Up @@ -685,12 +686,12 @@ static void nfnetlink_bind_event(struct net *net, unsigned int group)
group_bit = (1 << group);

spin_lock(&nfnl_grp_active_lock);
v = READ_ONCE(net->ct.ctnetlink_has_listener);
v = READ_ONCE(nf_ctnetlink_has_listener);
if ((v & group_bit) == 0) {
v |= group_bit;

/* read concurrently without nfnl_grp_active_lock held. */
WRITE_ONCE(net->ct.ctnetlink_has_listener, v);
WRITE_ONCE(nf_ctnetlink_has_listener, v);
}

spin_unlock(&nfnl_grp_active_lock);
Expand Down Expand Up @@ -744,12 +745,12 @@ static void nfnetlink_unbind(struct net *net, int group)

spin_lock(&nfnl_grp_active_lock);
if (!nfnetlink_has_listeners(net, group)) {
u8 v = READ_ONCE(net->ct.ctnetlink_has_listener);
u8 v = READ_ONCE(nf_ctnetlink_has_listener);

v &= ~group_bit;

/* read concurrently without nfnl_grp_active_lock held. */
WRITE_ONCE(net->ct.ctnetlink_has_listener, v);
WRITE_ONCE(nf_ctnetlink_has_listener, v);
}
spin_unlock(&nfnl_grp_active_lock);
#endif
Expand Down

0 comments on commit fdf6491

Please sign in to comment.