netfilter: disable defrag once its no longer needed
authorFlorian Westphal <fw@strlen.de>
Wed, 21 Apr 2021 07:45:40 +0000 (09:45 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 26 Apr 2021 01:20:07 +0000 (03:20 +0200)
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>
include/net/netfilter/ipv4/nf_defrag_ipv4.h
include/net/netfilter/ipv6/nf_defrag_ipv6.h
net/ipv4/netfilter/nf_defrag_ipv4.c
net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
net/netfilter/nf_conntrack_proto.c
net/netfilter/nft_tproxy.c
net/netfilter/xt_TPROXY.c
net/netfilter/xt_socket.c

index bcbd724cc048a207eb8d66575051ef8598b21ee5..7fda9ce9f6943fba57a09cc0159db33a87373fa4 100644 (file)
@@ -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 */
index ece923e2035b50645e6e720a5b644eb76d53463b..0fd8a4159662d094fd5f02c34c856a9bb08d2813 100644 (file)
@@ -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);
index ffdcc2b9360fe3bacfe2fec031419af6f535f11e..613432a36f0a701afc993d23053bfc03c4a8ed0a 100644 (file)
@@ -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);
 
index 402dc4ca9504fec1624178452ccfb31242bc0b67..e8a59d8bf2adf705824586f6f60b35b53d2be9e6 100644 (file)
@@ -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);
 
index 47e9319d2cf315768a76899bb403ee44ab72e534..89e5bac384d70162fca42975a8818b911f321c78 100644 (file)
@@ -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:
index 43a5a780a6d3b69bcff11b200b135fe13fdebaa6..accef672088c788bd6b7610e2fd795d276391c6e 100644 (file)
@@ -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)
 {
@@ -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,
 };
 
index 194dc03341f305703f792b8ec47a660608c2e3bb..459d0696c91a6d24bb20abe4dac45c5108b0118a 100644 (file)
@@ -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)
@@ -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",
@@ -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,
        },
@@ -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,
        },
@@ -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,
        },
index 5f973987265df57f9345f1841a542460fab0ea8a..5e6459e1160553c0a563a38b5060815e88998b4d 100644 (file)
@@ -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",
@@ -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) |
@@ -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,
@@ -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),
@@ -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),
@@ -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),
@@ -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),