[RFC PATCH v1 13/30] sysctl: net: use sysctl_field in IPv6 fragment sysctls
From: Alexey Gladkov
Date: Wed Aug 26 2026 - 15:48:54 EST
The IPv6 fragment sysctl tables are cloned for every non-init network
namespace only to bind the entries to that namespace's fqdir. The clone
also has to patch the low/high threshold limits through extra1 and
extra2 before registration.
Use sysctl_field descriptors for the IPv6 fragment and conntrack
fragment sysctls. The data and limit pointers are derived from the
registration context, so the tables can stay const and the per-net
allocation, pointer patching, and free paths are no longer needed.
Signed-off-by: Alexey Gladkov <legion@xxxxxxxxxx>
---
net/ipv6/netfilter/nf_conntrack_reasm.c | 77 ++++++++++---------------
net/ipv6/reassembly.c | 77 ++++++++++---------------
2 files changed, 60 insertions(+), 94 deletions(-)
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 64ab23ff559b..f9785d9bd8f3 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -43,73 +43,56 @@ static struct nft_ct_frag6_pernet *nf_frag_pernet(struct net *net)
#ifdef CONFIG_SYSCTL
-static struct ctl_table nf_ct_frag6_sysctl_table[] = {
- {
- .procname = "nf_conntrack_frag6_timeout",
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- {
- .procname = "nf_conntrack_frag6_low_thresh",
- .maxlen = sizeof(unsigned long),
- .mode = 0644,
- .proc_handler = proc_doulongvec_minmax,
- },
- {
- .procname = "nf_conntrack_frag6_high_thresh",
- .maxlen = sizeof(unsigned long),
- .mode = 0644,
- .proc_handler = proc_doulongvec_minmax,
- },
+static void *nf_ct_frag6_timeout_data(const struct sysctl_context *ctx)
+{
+ return &nf_frag_pernet(ctx->ns.net_ns)->fqdir->timeout;
+}
+
+static unsigned long *nf_ct_frag6_low_thresh_data(const struct sysctl_context *ctx)
+{
+ return &nf_frag_pernet(ctx->ns.net_ns)->fqdir->low_thresh;
+}
+
+static unsigned long *nf_ct_frag6_high_thresh_data(const struct sysctl_context *ctx)
+{
+ return &nf_frag_pernet(ctx->ns.net_ns)->fqdir->high_thresh;
+}
+
+static const struct sysctl_field nf_ct_frag6_sysctl_table[] = {
+ SYSCTL_FIELD_CUSTOM("nf_conntrack_frag6_timeout", 0644, sizeof(unsigned int),
+ nf_ct_frag6_timeout_data, proc_dointvec_jiffies),
+ SYSCTL_FIELD_ULONG_MINMAX("nf_conntrack_frag6_low_thresh", 0644,
+ nf_ct_frag6_low_thresh_data, NULL,
+ nf_ct_frag6_high_thresh_data),
+ SYSCTL_FIELD_ULONG_MINMAX("nf_conntrack_frag6_high_thresh", 0644,
+ nf_ct_frag6_high_thresh_data,
+ nf_ct_frag6_low_thresh_data, NULL),
};
static int nf_ct_frag6_sysctl_register(struct net *net)
{
+ struct sysctl_context ctx = {
+ .ns.net_ns = net,
+ };
struct nft_ct_frag6_pernet *nf_frag;
- struct ctl_table *table;
struct ctl_table_header *hdr;
- table = nf_ct_frag6_sysctl_table;
- if (!net_eq(net, &init_net)) {
- table = kmemdup(table, sizeof(nf_ct_frag6_sysctl_table),
- GFP_KERNEL);
- if (table == NULL)
- goto err_alloc;
- }
-
nf_frag = nf_frag_pernet(net);
- table[0].data = &nf_frag->fqdir->timeout;
- table[1].data = &nf_frag->fqdir->low_thresh;
- table[1].extra2 = &nf_frag->fqdir->high_thresh;
- table[2].data = &nf_frag->fqdir->high_thresh;
- table[2].extra1 = &nf_frag->fqdir->low_thresh;
-
- hdr = register_net_sysctl_sz(net, "net/netfilter", table,
- ARRAY_SIZE(nf_ct_frag6_sysctl_table));
+ hdr = register_sysctl_fields(&net->sysctls, "net/netfilter",
+ nf_ct_frag6_sysctl_table, &ctx);
if (hdr == NULL)
- goto err_reg;
+ return -ENOMEM;
nf_frag->nf_frag_frags_hdr = hdr;
return 0;
-
-err_reg:
- if (!net_eq(net, &init_net))
- kfree(table);
-err_alloc:
- return -ENOMEM;
}
static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
{
struct nft_ct_frag6_pernet *nf_frag = nf_frag_pernet(net);
- const struct ctl_table *table;
- table = nf_frag->nf_frag_frags_hdr->ctl_table_arg;
unregister_net_sysctl_table(nf_frag->nf_frag_frags_hdr);
- if (!net_eq(net, &init_net))
- kfree(table);
}
#else
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 11f9144bebbe..87bd9e1efbb3 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -424,25 +424,30 @@ static const struct inet6_protocol frag_protocol = {
#ifdef CONFIG_SYSCTL
-static struct ctl_table ip6_frags_ns_ctl_table[] = {
- {
- .procname = "ip6frag_high_thresh",
- .maxlen = sizeof(unsigned long),
- .mode = 0644,
- .proc_handler = proc_doulongvec_minmax,
- },
- {
- .procname = "ip6frag_low_thresh",
- .maxlen = sizeof(unsigned long),
- .mode = 0644,
- .proc_handler = proc_doulongvec_minmax,
- },
- {
- .procname = "ip6frag_time",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
+static unsigned long *ip6_frags_high_thresh_data(const struct sysctl_context *ctx)
+{
+ return &ctx->ns.net_ns->ipv6.fqdir->high_thresh;
+}
+
+static unsigned long *ip6_frags_low_thresh_data(const struct sysctl_context *ctx)
+{
+ return &ctx->ns.net_ns->ipv6.fqdir->low_thresh;
+}
+
+static void *ip6_frags_timeout_data(const struct sysctl_context *ctx)
+{
+ return &ctx->ns.net_ns->ipv6.fqdir->timeout;
+}
+
+static const struct sysctl_field ip6_frags_ns_ctl_table[] = {
+ SYSCTL_FIELD_ULONG_MINMAX("ip6frag_high_thresh", 0644,
+ ip6_frags_high_thresh_data,
+ ip6_frags_low_thresh_data, NULL),
+ SYSCTL_FIELD_ULONG_MINMAX("ip6frag_low_thresh", 0644,
+ ip6_frags_low_thresh_data,
+ NULL, ip6_frags_high_thresh_data),
+ SYSCTL_FIELD_CUSTOM("ip6frag_time", 0644, sizeof(int),
+ ip6_frags_timeout_data, proc_dointvec_jiffies),
};
/* secret interval has been deprecated */
@@ -459,45 +464,23 @@ static struct ctl_table ip6_frags_ctl_table[] = {
static int __net_init ip6_frags_ns_sysctl_register(struct net *net)
{
- struct ctl_table *table;
+ struct sysctl_context ctx = {
+ .ns.net_ns = net,
+ };
struct ctl_table_header *hdr;
- table = ip6_frags_ns_ctl_table;
- if (!net_eq(net, &init_net)) {
- table = kmemdup(table, sizeof(ip6_frags_ns_ctl_table), GFP_KERNEL);
- if (!table)
- goto err_alloc;
-
- }
- table[0].data = &net->ipv6.fqdir->high_thresh;
- table[0].extra1 = &net->ipv6.fqdir->low_thresh;
- table[1].data = &net->ipv6.fqdir->low_thresh;
- table[1].extra2 = &net->ipv6.fqdir->high_thresh;
- table[2].data = &net->ipv6.fqdir->timeout;
-
- hdr = register_net_sysctl_sz(net, "net/ipv6", table,
- ARRAY_SIZE(ip6_frags_ns_ctl_table));
+ hdr = register_sysctl_fields(&net->sysctls, "net/ipv6",
+ ip6_frags_ns_ctl_table, &ctx);
if (!hdr)
- goto err_reg;
+ return -ENOMEM;
net->ipv6.sysctl.frags_hdr = hdr;
return 0;
-
-err_reg:
- if (!net_eq(net, &init_net))
- kfree(table);
-err_alloc:
- return -ENOMEM;
}
static void __net_exit ip6_frags_ns_sysctl_unregister(struct net *net)
{
- const struct ctl_table *table;
-
- table = net->ipv6.sysctl.frags_hdr->ctl_table_arg;
unregister_net_sysctl_table(net->ipv6.sysctl.frags_hdr);
- if (!net_eq(net, &init_net))
- kfree(table);
}
static struct ctl_table_header *ip6_ctl_header;
--
2.55.0