1
1
// SPDX-License-Identifier: GPL-2.0
2
2
#include <linux/bpf.h>
3
3
#include <linux/filter.h>
4
+ #include <linux/kmod.h>
5
+ #include <linux/module.h>
4
6
#include <linux/netfilter.h>
5
7
6
8
#include <net/netfilter/nf_bpf_link.h>
@@ -23,20 +25,100 @@ struct bpf_nf_link {
23
25
struct nf_hook_ops hook_ops ;
24
26
struct net * net ;
25
27
u32 dead ;
28
+ const struct nf_defrag_hook * defrag_hook ;
26
29
};
27
30
31
+ static const struct nf_defrag_hook *
32
+ get_proto_defrag_hook (struct bpf_nf_link * link ,
33
+ const struct nf_defrag_hook __rcu * global_hook ,
34
+ const char * mod )
35
+ {
36
+ const struct nf_defrag_hook * hook ;
37
+ int err ;
38
+
39
+ /* RCU protects us from races against module unloading */
40
+ rcu_read_lock ();
41
+ hook = rcu_dereference (global_hook );
42
+ if (!hook ) {
43
+ rcu_read_unlock ();
44
+ err = request_module (mod );
45
+ if (err )
46
+ return ERR_PTR (err < 0 ? err : - EINVAL );
47
+
48
+ rcu_read_lock ();
49
+ hook = rcu_dereference (global_hook );
50
+ }
51
+
52
+ if (hook && try_module_get (hook -> owner )) {
53
+ /* Once we have a refcnt on the module, we no longer need RCU */
54
+ hook = rcu_pointer_handoff (hook );
55
+ } else {
56
+ WARN_ONCE (!hook , "%s has bad registration" , mod );
57
+ hook = ERR_PTR (- ENOENT );
58
+ }
59
+ rcu_read_unlock ();
60
+
61
+ if (!IS_ERR (hook )) {
62
+ err = hook -> enable (link -> net );
63
+ if (err ) {
64
+ module_put (hook -> owner );
65
+ hook = ERR_PTR (err );
66
+ }
67
+ }
68
+
69
+ return hook ;
70
+ }
71
+
72
+ static int bpf_nf_enable_defrag (struct bpf_nf_link * link )
73
+ {
74
+ const struct nf_defrag_hook __maybe_unused * hook ;
75
+
76
+ switch (link -> hook_ops .pf ) {
77
+ #if IS_ENABLED (CONFIG_NF_DEFRAG_IPV4 )
78
+ case NFPROTO_IPV4 :
79
+ hook = get_proto_defrag_hook (link , nf_defrag_v4_hook , "nf_defrag_ipv4" );
80
+ if (IS_ERR (hook ))
81
+ return PTR_ERR (hook );
82
+
83
+ link -> defrag_hook = hook ;
84
+ return 0 ;
85
+ #endif
86
+ #if IS_ENABLED (CONFIG_NF_DEFRAG_IPV6 )
87
+ case NFPROTO_IPV6 :
88
+ hook = get_proto_defrag_hook (link , nf_defrag_v6_hook , "nf_defrag_ipv6" );
89
+ if (IS_ERR (hook ))
90
+ return PTR_ERR (hook );
91
+
92
+ link -> defrag_hook = hook ;
93
+ return 0 ;
94
+ #endif
95
+ default :
96
+ return - EAFNOSUPPORT ;
97
+ }
98
+ }
99
+
100
+ static void bpf_nf_disable_defrag (struct bpf_nf_link * link )
101
+ {
102
+ const struct nf_defrag_hook * hook = link -> defrag_hook ;
103
+
104
+ if (!hook )
105
+ return ;
106
+ hook -> disable (link -> net );
107
+ module_put (hook -> owner );
108
+ }
109
+
28
110
static void bpf_nf_link_release (struct bpf_link * link )
29
111
{
30
112
struct bpf_nf_link * nf_link = container_of (link , struct bpf_nf_link , link );
31
113
32
114
if (nf_link -> dead )
33
115
return ;
34
116
35
- /* prevent hook-not-found warning splat from netfilter core when
36
- * .detach was already called
37
- */
38
- if (!cmpxchg (& nf_link -> dead , 0 , 1 ))
117
+ /* do not double release in case .detach was already called */
118
+ if (!cmpxchg (& nf_link -> dead , 0 , 1 )) {
39
119
nf_unregister_net_hook (nf_link -> net , & nf_link -> hook_ops );
120
+ bpf_nf_disable_defrag (nf_link );
121
+ }
40
122
}
41
123
42
124
static void bpf_nf_link_dealloc (struct bpf_link * link )
@@ -92,6 +174,8 @@ static const struct bpf_link_ops bpf_nf_link_lops = {
92
174
93
175
static int bpf_nf_check_pf_and_hooks (const union bpf_attr * attr )
94
176
{
177
+ int prio ;
178
+
95
179
switch (attr -> link_create .netfilter .pf ) {
96
180
case NFPROTO_IPV4 :
97
181
case NFPROTO_IPV6 :
@@ -102,19 +186,18 @@ static int bpf_nf_check_pf_and_hooks(const union bpf_attr *attr)
102
186
return - EAFNOSUPPORT ;
103
187
}
104
188
105
- if (attr -> link_create .netfilter .flags )
189
+ if (attr -> link_create .netfilter .flags & ~ BPF_F_NETFILTER_IP_DEFRAG )
106
190
return - EOPNOTSUPP ;
107
191
108
- /* make sure conntrack confirm is always last.
109
- *
110
- * In the future, if userspace can e.g. request defrag, then
111
- * "defrag_requested && prio before NF_IP_PRI_CONNTRACK_DEFRAG"
112
- * should fail.
113
- */
114
- switch (attr -> link_create .netfilter .priority ) {
115
- case NF_IP_PRI_FIRST : return - ERANGE ; /* sabotage_in and other warts */
116
- case NF_IP_PRI_LAST : return - ERANGE ; /* e.g. conntrack confirm */
117
- }
192
+ /* make sure conntrack confirm is always last */
193
+ prio = attr -> link_create .netfilter .priority ;
194
+ if (prio == NF_IP_PRI_FIRST )
195
+ return - ERANGE ; /* sabotage_in and other warts */
196
+ else if (prio == NF_IP_PRI_LAST )
197
+ return - ERANGE ; /* e.g. conntrack confirm */
198
+ else if ((attr -> link_create .netfilter .flags & BPF_F_NETFILTER_IP_DEFRAG ) &&
199
+ prio <= NF_IP_PRI_CONNTRACK_DEFRAG )
200
+ return - ERANGE ; /* cannot use defrag if prog runs before nf_defrag */
118
201
119
202
return 0 ;
120
203
}
@@ -149,15 +232,25 @@ int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
149
232
150
233
link -> net = net ;
151
234
link -> dead = false;
235
+ link -> defrag_hook = NULL ;
152
236
153
237
err = bpf_link_prime (& link -> link , & link_primer );
154
238
if (err ) {
155
239
kfree (link );
156
240
return err ;
157
241
}
158
242
243
+ if (attr -> link_create .netfilter .flags & BPF_F_NETFILTER_IP_DEFRAG ) {
244
+ err = bpf_nf_enable_defrag (link );
245
+ if (err ) {
246
+ bpf_link_cleanup (& link_primer );
247
+ return err ;
248
+ }
249
+ }
250
+
159
251
err = nf_register_net_hook (net , & link -> hook_ops );
160
252
if (err ) {
253
+ bpf_nf_disable_defrag (link );
161
254
bpf_link_cleanup (& link_primer );
162
255
return err ;
163
256
}
0 commit comments