Skip to content

Commit

Permalink
netfilter: disable defrag once its no longer needed
Browse files Browse the repository at this point in the history
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: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Florian Westphal authored and ummakynes committed Apr 26, 2021
1 parent e0bb96d commit de8c121
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 16 deletions.
3 changes: 2 additions & 1 deletion include/net/netfilter/ipv4/nf_defrag_ipv4.h
Expand Up @@ -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 */
3 changes: 2 additions & 1 deletion include/net/netfilter/ipv6/nf_defrag_ipv6.h
Expand Up @@ -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);
Expand Down
30 changes: 24 additions & 6 deletions net/ipv4/netfilter/nf_defrag_ipv4.c
Expand Up @@ -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));
Expand All @@ -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);

Expand Down
29 changes: 23 additions & 6 deletions net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
Expand Up @@ -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));
Expand All @@ -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);

Expand Down
8 changes: 6 additions & 2 deletions net/netfilter/nf_conntrack_proto.c
Expand Up @@ -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:
Expand Down
24 changes: 24 additions & 0 deletions net/netfilter/nft_tproxy.c
Expand Up @@ -263,6 +263,29 @@ static int nft_tproxy_init(const struct nft_ctx *ctx,
return 0;
}

static void nft_tproxy_destroy(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
const struct nft_tproxy *priv = nft_expr_priv(expr);

switch (priv->family) {
case NFPROTO_IPV4:
nf_defrag_ipv4_disable(ctx->net);
break;
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
case NFPROTO_IPV6:
nf_defrag_ipv6_disable(ctx->net);
break;
#endif
case NFPROTO_UNSPEC:
nf_defrag_ipv4_disable(ctx->net);
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
nf_defrag_ipv6_disable(ctx->net);
#endif
break;
}
}

static int nft_tproxy_dump(struct sk_buff *skb,
const struct nft_expr *expr)
{
Expand All @@ -288,6 +311,7 @@ static const struct nft_expr_ops nft_tproxy_ops = {
.size = NFT_EXPR_SIZE(sizeof(struct nft_tproxy)),
.eval = nft_tproxy_eval,
.init = nft_tproxy_init,
.destroy = nft_tproxy_destroy,
.dump = nft_tproxy_dump,
};

Expand Down
13 changes: 13 additions & 0 deletions net/netfilter/xt_TPROXY.c
Expand Up @@ -200,6 +200,11 @@ static int tproxy_tg6_check(const struct xt_tgchk_param *par)
pr_info_ratelimited("Can be used only with -p tcp or -p udp\n");
return -EINVAL;
}

static void tproxy_tg6_destroy(const struct xt_tgdtor_param *par)
{
nf_defrag_ipv6_disable(par->net);
}
#endif

static int tproxy_tg4_check(const struct xt_tgchk_param *par)
Expand All @@ -219,6 +224,11 @@ static int tproxy_tg4_check(const struct xt_tgchk_param *par)
return -EINVAL;
}

static void tproxy_tg4_destroy(const struct xt_tgdtor_param *par)
{
nf_defrag_ipv4_disable(par->net);
}

static struct xt_target tproxy_tg_reg[] __read_mostly = {
{
.name = "TPROXY",
Expand All @@ -228,6 +238,7 @@ static struct xt_target tproxy_tg_reg[] __read_mostly = {
.revision = 0,
.targetsize = sizeof(struct xt_tproxy_target_info),
.checkentry = tproxy_tg4_check,
.destroy = tproxy_tg4_destroy,
.hooks = 1 << NF_INET_PRE_ROUTING,
.me = THIS_MODULE,
},
Expand All @@ -239,6 +250,7 @@ static struct xt_target tproxy_tg_reg[] __read_mostly = {
.revision = 1,
.targetsize = sizeof(struct xt_tproxy_target_info_v1),
.checkentry = tproxy_tg4_check,
.destroy = tproxy_tg4_destroy,
.hooks = 1 << NF_INET_PRE_ROUTING,
.me = THIS_MODULE,
},
Expand All @@ -251,6 +263,7 @@ static struct xt_target tproxy_tg_reg[] __read_mostly = {
.revision = 1,
.targetsize = sizeof(struct xt_tproxy_target_info_v1),
.checkentry = tproxy_tg6_check,
.destroy = tproxy_tg6_destroy,
.hooks = 1 << NF_INET_PRE_ROUTING,
.me = THIS_MODULE,
},
Expand Down
14 changes: 14 additions & 0 deletions net/netfilter/xt_socket.c
Expand Up @@ -216,6 +216,14 @@ static int socket_mt_v3_check(const struct xt_mtchk_param *par)
return 0;
}

static void socket_mt_destroy(const struct xt_mtdtor_param *par)
{
if (par->family == NFPROTO_IPV4)
nf_defrag_ipv4_disable(par->net);
else if (par->family == NFPROTO_IPV6)
nf_defrag_ipv4_disable(par->net);
}

static struct xt_match socket_mt_reg[] __read_mostly = {
{
.name = "socket",
Expand All @@ -231,6 +239,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
.revision = 1,
.family = NFPROTO_IPV4,
.match = socket_mt4_v1_v2_v3,
.destroy = socket_mt_destroy,
.checkentry = socket_mt_v1_check,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.hooks = (1 << NF_INET_PRE_ROUTING) |
Expand All @@ -245,6 +254,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
.match = socket_mt6_v1_v2_v3,
.checkentry = socket_mt_v1_check,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.destroy = socket_mt_destroy,
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN),
.me = THIS_MODULE,
Expand All @@ -256,6 +266,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
.family = NFPROTO_IPV4,
.match = socket_mt4_v1_v2_v3,
.checkentry = socket_mt_v2_check,
.destroy = socket_mt_destroy,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN),
Expand All @@ -268,6 +279,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
.family = NFPROTO_IPV6,
.match = socket_mt6_v1_v2_v3,
.checkentry = socket_mt_v2_check,
.destroy = socket_mt_destroy,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN),
Expand All @@ -280,6 +292,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
.family = NFPROTO_IPV4,
.match = socket_mt4_v1_v2_v3,
.checkentry = socket_mt_v3_check,
.destroy = socket_mt_destroy,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN),
Expand All @@ -292,6 +305,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
.family = NFPROTO_IPV6,
.match = socket_mt6_v1_v2_v3,
.checkentry = socket_mt_v3_check,
.destroy = socket_mt_destroy,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN),
Expand Down

0 comments on commit de8c121

Please sign in to comment.