Commit | Line | Data |
---|---|---|
b27f7bb5 JS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | #include <linux/bpf.h> | |
4 | #include <linux/filter.h> | |
5 | #include <net/net_namespace.h> | |
6 | ||
7 | /* | |
8 | * Functions to manage BPF programs attached to netns | |
9 | */ | |
10 | ||
7f045a49 JS |
11 | struct bpf_netns_link { |
12 | struct bpf_link link; | |
13 | enum bpf_attach_type type; | |
14 | enum netns_bpf_attach_type netns_type; | |
15 | ||
16 | /* We don't hold a ref to net in order to auto-detach the link | |
17 | * when netns is going away. Instead we rely on pernet | |
18 | * pre_exit callback to clear this pointer. Must be accessed | |
19 | * with netns_bpf_mutex held. | |
20 | */ | |
21 | struct net *net; | |
22 | }; | |
23 | ||
b27f7bb5 JS |
24 | /* Protects updates to netns_bpf */ |
25 | DEFINE_MUTEX(netns_bpf_mutex); | |
26 | ||
7f045a49 JS |
27 | /* Must be called with netns_bpf_mutex held. */ |
28 | static void __net_exit bpf_netns_link_auto_detach(struct bpf_link *link) | |
29 | { | |
30 | struct bpf_netns_link *net_link = | |
31 | container_of(link, struct bpf_netns_link, link); | |
32 | ||
33 | net_link->net = NULL; | |
34 | } | |
35 | ||
36 | static void bpf_netns_link_release(struct bpf_link *link) | |
37 | { | |
38 | struct bpf_netns_link *net_link = | |
39 | container_of(link, struct bpf_netns_link, link); | |
40 | enum netns_bpf_attach_type type = net_link->netns_type; | |
41 | struct net *net; | |
42 | ||
43 | /* Link auto-detached by dying netns. */ | |
44 | if (!net_link->net) | |
45 | return; | |
46 | ||
47 | mutex_lock(&netns_bpf_mutex); | |
48 | ||
49 | /* Recheck after potential sleep. We can race with cleanup_net | |
50 | * here, but if we see a non-NULL struct net pointer pre_exit | |
51 | * has not happened yet and will block on netns_bpf_mutex. | |
52 | */ | |
53 | net = net_link->net; | |
54 | if (!net) | |
55 | goto out_unlock; | |
56 | ||
57 | net->bpf.links[type] = NULL; | |
58 | RCU_INIT_POINTER(net->bpf.progs[type], NULL); | |
59 | ||
60 | out_unlock: | |
61 | mutex_unlock(&netns_bpf_mutex); | |
62 | } | |
63 | ||
64 | static void bpf_netns_link_dealloc(struct bpf_link *link) | |
65 | { | |
66 | struct bpf_netns_link *net_link = | |
67 | container_of(link, struct bpf_netns_link, link); | |
68 | ||
69 | kfree(net_link); | |
70 | } | |
71 | ||
72 | static int bpf_netns_link_update_prog(struct bpf_link *link, | |
73 | struct bpf_prog *new_prog, | |
74 | struct bpf_prog *old_prog) | |
75 | { | |
76 | struct bpf_netns_link *net_link = | |
77 | container_of(link, struct bpf_netns_link, link); | |
78 | enum netns_bpf_attach_type type = net_link->netns_type; | |
79 | struct net *net; | |
80 | int ret = 0; | |
81 | ||
82 | if (old_prog && old_prog != link->prog) | |
83 | return -EPERM; | |
84 | if (new_prog->type != link->prog->type) | |
85 | return -EINVAL; | |
86 | ||
87 | mutex_lock(&netns_bpf_mutex); | |
88 | ||
89 | net = net_link->net; | |
90 | if (!net || !check_net(net)) { | |
91 | /* Link auto-detached or netns dying */ | |
92 | ret = -ENOLINK; | |
93 | goto out_unlock; | |
94 | } | |
95 | ||
96 | old_prog = xchg(&link->prog, new_prog); | |
97 | rcu_assign_pointer(net->bpf.progs[type], new_prog); | |
98 | bpf_prog_put(old_prog); | |
99 | ||
100 | out_unlock: | |
101 | mutex_unlock(&netns_bpf_mutex); | |
102 | return ret; | |
103 | } | |
104 | ||
105 | static int bpf_netns_link_fill_info(const struct bpf_link *link, | |
106 | struct bpf_link_info *info) | |
107 | { | |
108 | const struct bpf_netns_link *net_link = | |
109 | container_of(link, struct bpf_netns_link, link); | |
110 | unsigned int inum = 0; | |
111 | struct net *net; | |
112 | ||
113 | mutex_lock(&netns_bpf_mutex); | |
114 | net = net_link->net; | |
115 | if (net && check_net(net)) | |
116 | inum = net->ns.inum; | |
117 | mutex_unlock(&netns_bpf_mutex); | |
118 | ||
119 | info->netns.netns_ino = inum; | |
120 | info->netns.attach_type = net_link->type; | |
121 | return 0; | |
122 | } | |
123 | ||
124 | static void bpf_netns_link_show_fdinfo(const struct bpf_link *link, | |
125 | struct seq_file *seq) | |
126 | { | |
127 | struct bpf_link_info info = {}; | |
128 | ||
129 | bpf_netns_link_fill_info(link, &info); | |
130 | seq_printf(seq, | |
131 | "netns_ino:\t%u\n" | |
132 | "attach_type:\t%u\n", | |
133 | info.netns.netns_ino, | |
134 | info.netns.attach_type); | |
135 | } | |
136 | ||
137 | static const struct bpf_link_ops bpf_netns_link_ops = { | |
138 | .release = bpf_netns_link_release, | |
139 | .dealloc = bpf_netns_link_dealloc, | |
140 | .update_prog = bpf_netns_link_update_prog, | |
141 | .fill_link_info = bpf_netns_link_fill_info, | |
142 | .show_fdinfo = bpf_netns_link_show_fdinfo, | |
143 | }; | |
144 | ||
b27f7bb5 JS |
145 | int netns_bpf_prog_query(const union bpf_attr *attr, |
146 | union bpf_attr __user *uattr) | |
147 | { | |
148 | __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids); | |
149 | u32 prog_id, prog_cnt = 0, flags = 0; | |
150 | enum netns_bpf_attach_type type; | |
151 | struct bpf_prog *attached; | |
152 | struct net *net; | |
153 | ||
154 | if (attr->query.query_flags) | |
155 | return -EINVAL; | |
156 | ||
157 | type = to_netns_bpf_attach_type(attr->query.attach_type); | |
158 | if (type < 0) | |
159 | return -EINVAL; | |
160 | ||
161 | net = get_net_ns_by_fd(attr->query.target_fd); | |
162 | if (IS_ERR(net)) | |
163 | return PTR_ERR(net); | |
164 | ||
165 | rcu_read_lock(); | |
166 | attached = rcu_dereference(net->bpf.progs[type]); | |
167 | if (attached) { | |
168 | prog_cnt = 1; | |
169 | prog_id = attached->aux->id; | |
170 | } | |
171 | rcu_read_unlock(); | |
172 | ||
173 | put_net(net); | |
174 | ||
175 | if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) | |
176 | return -EFAULT; | |
177 | if (copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt))) | |
178 | return -EFAULT; | |
179 | ||
180 | if (!attr->query.prog_cnt || !prog_ids || !prog_cnt) | |
181 | return 0; | |
182 | ||
183 | if (copy_to_user(prog_ids, &prog_id, sizeof(u32))) | |
184 | return -EFAULT; | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | int netns_bpf_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog) | |
190 | { | |
191 | enum netns_bpf_attach_type type; | |
192 | struct net *net; | |
193 | int ret; | |
194 | ||
195 | type = to_netns_bpf_attach_type(attr->attach_type); | |
196 | if (type < 0) | |
197 | return -EINVAL; | |
198 | ||
199 | net = current->nsproxy->net_ns; | |
200 | mutex_lock(&netns_bpf_mutex); | |
7f045a49 JS |
201 | |
202 | /* Attaching prog directly is not compatible with links */ | |
203 | if (net->bpf.links[type]) { | |
204 | ret = -EEXIST; | |
205 | goto out_unlock; | |
206 | } | |
207 | ||
b27f7bb5 JS |
208 | switch (type) { |
209 | case NETNS_BPF_FLOW_DISSECTOR: | |
210 | ret = flow_dissector_bpf_prog_attach(net, prog); | |
211 | break; | |
212 | default: | |
213 | ret = -EINVAL; | |
214 | break; | |
215 | } | |
7f045a49 | 216 | out_unlock: |
b27f7bb5 JS |
217 | mutex_unlock(&netns_bpf_mutex); |
218 | ||
219 | return ret; | |
220 | } | |
221 | ||
222 | /* Must be called with netns_bpf_mutex held. */ | |
223 | static int __netns_bpf_prog_detach(struct net *net, | |
224 | enum netns_bpf_attach_type type) | |
225 | { | |
226 | struct bpf_prog *attached; | |
227 | ||
7f045a49 JS |
228 | /* Progs attached via links cannot be detached */ |
229 | if (net->bpf.links[type]) | |
230 | return -EINVAL; | |
231 | ||
b27f7bb5 JS |
232 | attached = rcu_dereference_protected(net->bpf.progs[type], |
233 | lockdep_is_held(&netns_bpf_mutex)); | |
234 | if (!attached) | |
235 | return -ENOENT; | |
236 | RCU_INIT_POINTER(net->bpf.progs[type], NULL); | |
237 | bpf_prog_put(attached); | |
238 | return 0; | |
239 | } | |
240 | ||
241 | int netns_bpf_prog_detach(const union bpf_attr *attr) | |
242 | { | |
243 | enum netns_bpf_attach_type type; | |
244 | int ret; | |
245 | ||
246 | type = to_netns_bpf_attach_type(attr->attach_type); | |
247 | if (type < 0) | |
248 | return -EINVAL; | |
249 | ||
250 | mutex_lock(&netns_bpf_mutex); | |
251 | ret = __netns_bpf_prog_detach(current->nsproxy->net_ns, type); | |
252 | mutex_unlock(&netns_bpf_mutex); | |
253 | ||
254 | return ret; | |
255 | } | |
256 | ||
7f045a49 JS |
257 | static int netns_bpf_link_attach(struct net *net, struct bpf_link *link, |
258 | enum netns_bpf_attach_type type) | |
259 | { | |
260 | struct bpf_prog *prog; | |
261 | int err; | |
262 | ||
263 | mutex_lock(&netns_bpf_mutex); | |
264 | ||
265 | /* Allow attaching only one prog or link for now */ | |
266 | if (net->bpf.links[type]) { | |
267 | err = -E2BIG; | |
268 | goto out_unlock; | |
269 | } | |
270 | /* Links are not compatible with attaching prog directly */ | |
271 | prog = rcu_dereference_protected(net->bpf.progs[type], | |
272 | lockdep_is_held(&netns_bpf_mutex)); | |
273 | if (prog) { | |
274 | err = -EEXIST; | |
275 | goto out_unlock; | |
276 | } | |
277 | ||
278 | switch (type) { | |
279 | case NETNS_BPF_FLOW_DISSECTOR: | |
280 | err = flow_dissector_bpf_prog_attach(net, link->prog); | |
281 | break; | |
282 | default: | |
283 | err = -EINVAL; | |
284 | break; | |
285 | } | |
286 | if (err) | |
287 | goto out_unlock; | |
288 | ||
289 | net->bpf.links[type] = link; | |
290 | ||
291 | out_unlock: | |
292 | mutex_unlock(&netns_bpf_mutex); | |
293 | return err; | |
294 | } | |
295 | ||
296 | int netns_bpf_link_create(const union bpf_attr *attr, struct bpf_prog *prog) | |
297 | { | |
298 | enum netns_bpf_attach_type netns_type; | |
299 | struct bpf_link_primer link_primer; | |
300 | struct bpf_netns_link *net_link; | |
301 | enum bpf_attach_type type; | |
302 | struct net *net; | |
303 | int err; | |
304 | ||
305 | if (attr->link_create.flags) | |
306 | return -EINVAL; | |
307 | ||
308 | type = attr->link_create.attach_type; | |
309 | netns_type = to_netns_bpf_attach_type(type); | |
310 | if (netns_type < 0) | |
311 | return -EINVAL; | |
312 | ||
313 | net = get_net_ns_by_fd(attr->link_create.target_fd); | |
314 | if (IS_ERR(net)) | |
315 | return PTR_ERR(net); | |
316 | ||
317 | net_link = kzalloc(sizeof(*net_link), GFP_USER); | |
318 | if (!net_link) { | |
319 | err = -ENOMEM; | |
320 | goto out_put_net; | |
321 | } | |
322 | bpf_link_init(&net_link->link, BPF_LINK_TYPE_NETNS, | |
323 | &bpf_netns_link_ops, prog); | |
324 | net_link->net = net; | |
325 | net_link->type = type; | |
326 | net_link->netns_type = netns_type; | |
327 | ||
328 | err = bpf_link_prime(&net_link->link, &link_primer); | |
329 | if (err) { | |
330 | kfree(net_link); | |
331 | goto out_put_net; | |
332 | } | |
333 | ||
334 | err = netns_bpf_link_attach(net, &net_link->link, netns_type); | |
335 | if (err) { | |
336 | bpf_link_cleanup(&link_primer); | |
337 | goto out_put_net; | |
338 | } | |
339 | ||
340 | put_net(net); | |
341 | return bpf_link_settle(&link_primer); | |
342 | ||
343 | out_put_net: | |
344 | put_net(net); | |
345 | return err; | |
346 | } | |
347 | ||
b27f7bb5 JS |
348 | static void __net_exit netns_bpf_pernet_pre_exit(struct net *net) |
349 | { | |
350 | enum netns_bpf_attach_type type; | |
7f045a49 | 351 | struct bpf_link *link; |
b27f7bb5 JS |
352 | |
353 | mutex_lock(&netns_bpf_mutex); | |
7f045a49 JS |
354 | for (type = 0; type < MAX_NETNS_BPF_ATTACH_TYPE; type++) { |
355 | link = net->bpf.links[type]; | |
356 | if (link) | |
357 | bpf_netns_link_auto_detach(link); | |
358 | else | |
359 | __netns_bpf_prog_detach(net, type); | |
360 | } | |
b27f7bb5 JS |
361 | mutex_unlock(&netns_bpf_mutex); |
362 | } | |
363 | ||
364 | static struct pernet_operations netns_bpf_pernet_ops __net_initdata = { | |
365 | .pre_exit = netns_bpf_pernet_pre_exit, | |
366 | }; | |
367 | ||
368 | static int __init netns_bpf_init(void) | |
369 | { | |
370 | return register_pernet_subsys(&netns_bpf_pernet_ops); | |
371 | } | |
372 | ||
373 | subsys_initcall(netns_bpf_init); |