1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/sysctl.h>
4 #include <net/lwtunnel.h>
5 #include <net/netfilter/nf_hooks_lwtunnel.h>
6 #include <linux/netfilter.h>
7
8 #include "nf_internals.h"
9
nf_hooks_lwtunnel_get(void)10 static inline int nf_hooks_lwtunnel_get(void)
11 {
12 if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
13 return 1;
14 else
15 return 0;
16 }
17
nf_hooks_lwtunnel_set(int enable)18 static inline int nf_hooks_lwtunnel_set(int enable)
19 {
20 if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) {
21 if (!enable)
22 return -EBUSY;
23 } else if (enable) {
24 static_branch_enable(&nf_hooks_lwtunnel_enabled);
25 }
26
27 return 0;
28 }
29
30 #ifdef CONFIG_SYSCTL
nf_hooks_lwtunnel_sysctl_handler(const struct ctl_table * table,int write,void * buffer,size_t * lenp,loff_t * ppos)31 int nf_hooks_lwtunnel_sysctl_handler(const struct ctl_table *table, int write,
32 void *buffer, size_t *lenp, loff_t *ppos)
33 {
34 int proc_nf_hooks_lwtunnel_enabled = 0;
35 struct ctl_table tmp = {
36 .procname = table->procname,
37 .data = &proc_nf_hooks_lwtunnel_enabled,
38 .maxlen = sizeof(int),
39 .mode = table->mode,
40 .extra1 = SYSCTL_ZERO,
41 .extra2 = SYSCTL_ONE,
42 };
43 int ret;
44
45 if (!write)
46 proc_nf_hooks_lwtunnel_enabled = nf_hooks_lwtunnel_get();
47
48 ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
49
50 if (write && ret == 0)
51 ret = nf_hooks_lwtunnel_set(proc_nf_hooks_lwtunnel_enabled);
52
53 return ret;
54 }
55 EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_sysctl_handler);
56
57 static struct ctl_table nf_lwtunnel_sysctl_table[] = {
58 {
59 .procname = "nf_hooks_lwtunnel",
60 .data = NULL,
61 .maxlen = sizeof(int),
62 .mode = 0644,
63 .proc_handler = nf_hooks_lwtunnel_sysctl_handler,
64 },
65 };
66
nf_lwtunnel_net_init(struct net * net)67 static int __net_init nf_lwtunnel_net_init(struct net *net)
68 {
69 struct ctl_table_header *hdr;
70 struct ctl_table *table;
71
72 table = nf_lwtunnel_sysctl_table;
73 if (!net_eq(net, &init_net)) {
74 table = kmemdup(nf_lwtunnel_sysctl_table,
75 sizeof(nf_lwtunnel_sysctl_table),
76 GFP_KERNEL);
77 if (!table)
78 goto err_alloc;
79 }
80
81 hdr = register_net_sysctl_sz(net, "net/netfilter", table,
82 ARRAY_SIZE(nf_lwtunnel_sysctl_table));
83 if (!hdr)
84 goto err_reg;
85
86 net->nf.nf_lwtnl_dir_header = hdr;
87
88 return 0;
89 err_reg:
90 if (!net_eq(net, &init_net))
91 kfree(table);
92 err_alloc:
93 return -ENOMEM;
94 }
95
nf_lwtunnel_net_exit(struct net * net)96 static void __net_exit nf_lwtunnel_net_exit(struct net *net)
97 {
98 const struct ctl_table *table;
99
100 table = net->nf.nf_lwtnl_dir_header->ctl_table_arg;
101 unregister_net_sysctl_table(net->nf.nf_lwtnl_dir_header);
102 if (!net_eq(net, &init_net))
103 kfree(table);
104 }
105
106 static struct pernet_operations nf_lwtunnel_net_ops = {
107 .init = nf_lwtunnel_net_init,
108 .exit = nf_lwtunnel_net_exit,
109 };
110
netfilter_lwtunnel_init(void)111 int __init netfilter_lwtunnel_init(void)
112 {
113 return register_pernet_subsys(&nf_lwtunnel_net_ops);
114 }
115
netfilter_lwtunnel_fini(void)116 void netfilter_lwtunnel_fini(void)
117 {
118 unregister_pernet_subsys(&nf_lwtunnel_net_ops);
119 }
120 #else
netfilter_lwtunnel_init(void)121 int __init netfilter_lwtunnel_init(void) { return 0; }
netfilter_lwtunnel_fini(void)122 void netfilter_lwtunnel_fini(void) {}
123 #endif /* CONFIG_SYSCTL */
124