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