Commit de8c1211 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso
Browse files

netfilter: disable defrag once its no longer needed



When I changed defrag hooks to no longer get registered by default I
intentionally made it so that registration can only be un-done by unloading
the nf_defrag_ipv4/6 module.

In hindsight this was too conservative; there is no reason to keep defrag
on while there is no feature dependency anymore.

Moreover, this won't work if user isn't allowed to remove nf_defrag module.

This adds the disable() functions for both ipv4 and ipv6 and calls them
from conntrack, TPROXY and the xtables socket module.

ipvs isn't converted here, it will behave as before this patch and
will need module removal.

Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent e0bb96db
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
#define _NF_DEFRAG_IPV4_H

struct net;
int nf_defrag_ipv4_enable(struct net *);
int nf_defrag_ipv4_enable(struct net *net);
void nf_defrag_ipv4_disable(struct net *net);

#endif /* _NF_DEFRAG_IPV4_H */
+2 −1
Original line number Diff line number Diff line
@@ -5,7 +5,8 @@
#include <linux/skbuff.h>
#include <linux/types.h>

int nf_defrag_ipv6_enable(struct net *);
int nf_defrag_ipv6_enable(struct net *net);
void nf_defrag_ipv6_disable(struct net *net);

int nf_ct_frag6_init(void);
void nf_ct_frag6_cleanup(void);
+24 −6
Original line number Diff line number Diff line
@@ -141,14 +141,16 @@ int nf_defrag_ipv4_enable(struct net *net)
	struct defrag4_pernet *nf_defrag = net_generic(net, defrag4_pernet_id);
	int err = 0;

	might_sleep();

	if (nf_defrag->users)
		return 0;

	mutex_lock(&defrag4_mutex);
	if (nf_defrag->users)
	if (nf_defrag->users == UINT_MAX) {
		err = -EOVERFLOW;
		goto out_unlock;
	}

	if (nf_defrag->users) {
		nf_defrag->users++;
		goto out_unlock;
	}

	err = nf_register_net_hooks(net, ipv4_defrag_ops,
				    ARRAY_SIZE(ipv4_defrag_ops));
@@ -161,6 +163,22 @@ int nf_defrag_ipv4_enable(struct net *net)
}
EXPORT_SYMBOL_GPL(nf_defrag_ipv4_enable);

void nf_defrag_ipv4_disable(struct net *net)
{
	struct defrag4_pernet *nf_defrag = net_generic(net, defrag4_pernet_id);

	mutex_lock(&defrag4_mutex);
	if (nf_defrag->users) {
		nf_defrag->users--;
		if (nf_defrag->users == 0)
			nf_unregister_net_hooks(net, ipv4_defrag_ops,
						ARRAY_SIZE(ipv4_defrag_ops));
	}

	mutex_unlock(&defrag4_mutex);
}
EXPORT_SYMBOL_GPL(nf_defrag_ipv4_disable);

module_init(nf_defrag_init);
module_exit(nf_defrag_fini);

+23 −6
Original line number Diff line number Diff line
@@ -137,14 +137,16 @@ int nf_defrag_ipv6_enable(struct net *net)
	struct nft_ct_frag6_pernet *nf_frag = net_generic(net, nf_frag_pernet_id);
	int err = 0;

	might_sleep();

	if (nf_frag->users)
		return 0;

	mutex_lock(&defrag6_mutex);
	if (nf_frag->users)
	if (nf_frag->users == UINT_MAX) {
		err = -EOVERFLOW;
		goto out_unlock;
	}

	if (nf_frag->users) {
		nf_frag->users++;
		goto out_unlock;
	}

	err = nf_register_net_hooks(net, ipv6_defrag_ops,
				    ARRAY_SIZE(ipv6_defrag_ops));
@@ -157,6 +159,21 @@ int nf_defrag_ipv6_enable(struct net *net)
}
EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable);

void nf_defrag_ipv6_disable(struct net *net)
{
	struct nft_ct_frag6_pernet *nf_frag = net_generic(net, nf_frag_pernet_id);

	mutex_lock(&defrag6_mutex);
	if (nf_frag->users) {
		nf_frag->users--;
		if (nf_frag->users == 0)
			nf_unregister_net_hooks(net, ipv6_defrag_ops,
						ARRAY_SIZE(ipv6_defrag_ops));
	}
	mutex_unlock(&defrag6_mutex);
}
EXPORT_SYMBOL_GPL(nf_defrag_ipv6_disable);

module_init(nf_defrag_init);
module_exit(nf_defrag_fini);

+6 −2
Original line number Diff line number Diff line
@@ -536,15 +536,19 @@ static void nf_ct_netns_do_put(struct net *net, u8 nfproto)
	mutex_lock(&nf_ct_proto_mutex);
	switch (nfproto) {
	case NFPROTO_IPV4:
		if (cnet->users4 && (--cnet->users4 == 0))
		if (cnet->users4 && (--cnet->users4 == 0)) {
			nf_unregister_net_hooks(net, ipv4_conntrack_ops,
						ARRAY_SIZE(ipv4_conntrack_ops));
			nf_defrag_ipv4_disable(net);
		}
		break;
#if IS_ENABLED(CONFIG_IPV6)
	case NFPROTO_IPV6:
		if (cnet->users6 && (--cnet->users6 == 0))
		if (cnet->users6 && (--cnet->users6 == 0)) {
			nf_unregister_net_hooks(net, ipv6_conntrack_ops,
						ARRAY_SIZE(ipv6_conntrack_ops));
			nf_defrag_ipv6_disable(net);
		}
		break;
#endif
	case NFPROTO_BRIDGE:
Loading