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
10static 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
18static 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
31int 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(table: &tmp, dir: write, buffer, lenp, ppos);
49
50 if (write && ret == 0)
51 ret = nf_hooks_lwtunnel_set(enable: proc_nf_hooks_lwtunnel_enabled);
52
53 return ret;
54}
55EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_sysctl_handler);
56
57static 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
67static 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(net1: net, net2: &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, path: "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;
89err_reg:
90 if (!net_eq(net1: net, net2: &init_net))
91 kfree(objp: table);
92err_alloc:
93 return -ENOMEM;
94}
95
96static 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(header: net->nf.nf_lwtnl_dir_header);
102 if (!net_eq(net1: net, net2: &init_net))
103 kfree(objp: table);
104}
105
106static struct pernet_operations nf_lwtunnel_net_ops = {
107 .init = nf_lwtunnel_net_init,
108 .exit = nf_lwtunnel_net_exit,
109};
110
111int __init netfilter_lwtunnel_init(void)
112{
113 return register_pernet_subsys(&nf_lwtunnel_net_ops);
114}
115
116void netfilter_lwtunnel_fini(void)
117{
118 unregister_pernet_subsys(&nf_lwtunnel_net_ops);
119}
120#else
121int __init netfilter_lwtunnel_init(void) { return 0; }
122void netfilter_lwtunnel_fini(void) {}
123#endif /* CONFIG_SYSCTL */
124

source code of linux/net/netfilter/nf_hooks_lwtunnel.c