Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-only
2 : /*
3 : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/cgen/prog/link.h"
7 :
8 : #include <linux/bpf.h>
9 :
10 : #include <errno.h>
11 : #include <fcntl.h>
12 : #include <stdio.h>
13 : #include <stdlib.h>
14 : #include <string.h>
15 : #include <unistd.h>
16 :
17 : #include "core/bpf.h"
18 : #include "core/dump.h"
19 : #include "core/flavor.h"
20 : #include "core/helper.h"
21 : #include "core/hook.h"
22 : #include "core/logger.h"
23 : #include "core/marsh.h"
24 :
25 4 : int bf_link_new(struct bf_link **link, const char *name)
26 : {
27 4 : _free_bf_link_ struct bf_link *_link = NULL;
28 :
29 4 : bf_assert(link && name);
30 4 : bf_assert(name[0] != '\0');
31 :
32 4 : _link = malloc(sizeof(*_link));
33 4 : if (!_link)
34 : return -ENOMEM;
35 :
36 4 : bf_strncpy(_link->name, BPF_OBJ_NAME_LEN, name);
37 :
38 4 : _link->hookopts = NULL;
39 4 : _link->fd = -1;
40 :
41 4 : *link = TAKE_PTR(_link);
42 :
43 4 : return 0;
44 : }
45 :
46 0 : int bf_link_new_from_marsh(struct bf_link **link, int dir_fd,
47 : const struct bf_marsh *marsh)
48 : {
49 0 : _free_bf_link_ struct bf_link *_link = NULL;
50 0 : _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
51 : struct bf_marsh *child = NULL;
52 : int r;
53 :
54 0 : bf_assert(link && marsh);
55 :
56 0 : _link = malloc(sizeof(*_link));
57 0 : if (!_link)
58 : return -ENOMEM;
59 :
60 0 : _link->hookopts = NULL;
61 0 : _link->fd = -1;
62 :
63 0 : if (!(child = bf_marsh_next_child(marsh, child)))
64 : return -EINVAL;
65 0 : memcpy(&_link->name, child->data, BPF_OBJ_NAME_LEN);
66 :
67 0 : if (!(child = bf_marsh_next_child(marsh, child)))
68 : return -EINVAL;
69 0 : if (!bf_marsh_is_empty(child)) {
70 0 : r = bf_hookopts_new_from_marsh(&_link->hookopts, child);
71 0 : if (r)
72 : return r;
73 : }
74 :
75 0 : if (dir_fd != -1) {
76 0 : r = bf_bpf_obj_get(_link->name, dir_fd, &_link->fd);
77 0 : if (r) {
78 0 : return bf_err_r(r, "failed to open pinned BPF link '%s'",
79 : _link->name);
80 : }
81 : }
82 :
83 0 : *link = TAKE_PTR(_link);
84 :
85 0 : return 0;
86 : }
87 :
88 8 : void bf_link_free(struct bf_link **link)
89 : {
90 8 : bf_assert(link);
91 :
92 8 : if (!*link)
93 : return;
94 :
95 4 : bf_hookopts_free(&(*link)->hookopts);
96 4 : closep(&(*link)->fd);
97 : freep((void *)link);
98 : }
99 :
100 0 : int bf_link_marsh(const struct bf_link *link, struct bf_marsh **marsh)
101 : {
102 0 : _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
103 : int r;
104 :
105 0 : bf_assert(link && marsh);
106 :
107 0 : r = bf_marsh_new(&_marsh, NULL, 0);
108 0 : if (r)
109 : return r;
110 :
111 0 : r = bf_marsh_add_child_raw(&_marsh, &link->name, BPF_OBJ_NAME_LEN);
112 0 : if (r)
113 : return r;
114 :
115 : // Serialize link.hookopts
116 0 : if (link->hookopts) {
117 0 : _cleanup_bf_marsh_ struct bf_marsh *hookopts_elem = NULL;
118 :
119 0 : r = bf_hookopts_marsh(link->hookopts, &hookopts_elem);
120 0 : if (r < 0)
121 : return r;
122 :
123 0 : r = bf_marsh_add_child_obj(&_marsh, hookopts_elem);
124 0 : if (r < 0)
125 : return r;
126 : } else {
127 0 : r = bf_marsh_add_child_raw(&_marsh, NULL, 0);
128 0 : if (r)
129 : return r;
130 : }
131 :
132 0 : *marsh = TAKE_PTR(_marsh);
133 :
134 0 : return 0;
135 : }
136 :
137 0 : void bf_link_dump(const struct bf_link *link, prefix_t *prefix)
138 : {
139 0 : bf_assert(link && prefix);
140 :
141 0 : DUMP(prefix, "struct bf_link at %p", link);
142 :
143 0 : bf_dump_prefix_push(prefix);
144 0 : DUMP(prefix, "name: %s", link->name);
145 :
146 0 : if (link->hookopts) {
147 0 : DUMP(prefix, "hookopts: struct bf_hookopts *");
148 0 : bf_dump_prefix_push(prefix);
149 0 : bf_hookopts_dump(link->hookopts, prefix);
150 0 : bf_dump_prefix_pop(prefix);
151 : } else {
152 0 : DUMP(prefix, "hookopts: struct bf_hookopts * (NULL)");
153 : }
154 :
155 0 : DUMP(bf_dump_prefix_last(prefix), "fd: %d", link->fd);
156 0 : bf_dump_prefix_pop(prefix);
157 0 : }
158 :
159 0 : static int _bf_link_attach_xdp(struct bf_link *link, enum bf_hook hook,
160 : const struct bf_hookopts *hookopts, int prog_fd)
161 : {
162 : union bpf_attr attr;
163 : int r;
164 :
165 : UNUSED(hook);
166 :
167 0 : memset(&attr, 0, sizeof(attr));
168 :
169 0 : attr.link_create.prog_fd = prog_fd;
170 0 : attr.link_create.target_fd = hookopts->ifindex;
171 0 : attr.link_create.attach_type = BPF_XDP;
172 0 : attr.link_create.flags = BF_XDP_MODE_SKB;
173 :
174 0 : r = bf_bpf(BPF_LINK_CREATE, &attr);
175 0 : if (r < 0)
176 : return r;
177 :
178 0 : link->fd = r;
179 :
180 0 : return 0;
181 : }
182 :
183 0 : static int _bf_link_attach_tc(struct bf_link *link, enum bf_hook hook,
184 : const struct bf_hookopts *hookopts, int prog_fd)
185 : {
186 : union bpf_attr attr;
187 : int r;
188 :
189 : UNUSED(hook);
190 :
191 0 : memset(&attr, 0, sizeof(attr));
192 :
193 0 : attr.link_create.prog_fd = prog_fd;
194 0 : attr.link_create.target_fd = hookopts->ifindex;
195 0 : attr.link_create.attach_type = bf_hook_to_bpf_attach_type(hook);
196 :
197 0 : r = bf_bpf(BPF_LINK_CREATE, &attr);
198 0 : if (r < 0)
199 : return r;
200 :
201 0 : link->fd = r;
202 :
203 0 : return 0;
204 : }
205 :
206 0 : static int _bf_link_attach_nf(struct bf_link *link, enum bf_hook hook,
207 : const struct bf_hookopts *hookopts, int prog_fd)
208 : {
209 : union bpf_attr attr;
210 : int r;
211 :
212 0 : memset(&attr, 0, sizeof(attr));
213 :
214 0 : attr.link_create.prog_fd = prog_fd;
215 0 : attr.link_create.attach_type = BPF_NETFILTER;
216 0 : attr.link_create.netfilter.pf = hookopts->family;
217 0 : attr.link_create.netfilter.hooknum = bf_hook_to_nf_hook(hook);
218 0 : attr.link_create.netfilter.priority = hookopts->priorities[0];
219 :
220 0 : r = bf_bpf(BPF_LINK_CREATE, &attr);
221 0 : if (r < 0)
222 : return r;
223 :
224 0 : link->fd = r;
225 :
226 0 : return 0;
227 : }
228 :
229 0 : static int _bf_link_attach_cgroup(struct bf_link *link, enum bf_hook hook,
230 : const struct bf_hookopts *hookopts,
231 : int prog_fd)
232 : {
233 0 : _cleanup_close_ int cgroup_fd = -1;
234 : union bpf_attr attr;
235 : int r;
236 :
237 0 : memset(&attr, 0, sizeof(attr));
238 :
239 0 : attr.link_create.prog_fd = prog_fd;
240 0 : attr.link_create.attach_type = bf_hook_to_bpf_attach_type(hook);
241 :
242 0 : cgroup_fd = open(hookopts->cgpath, O_DIRECTORY | O_RDONLY);
243 0 : if (cgroup_fd < 0)
244 0 : return bf_err_r(errno, "failed to open cgroup '%s'", hookopts->cgpath);
245 :
246 0 : attr.link_create.target_fd = cgroup_fd;
247 :
248 0 : r = bf_bpf(BPF_LINK_CREATE, &attr);
249 0 : if (r < 0)
250 : return r;
251 :
252 0 : link->fd = r;
253 :
254 0 : return 0;
255 : }
256 :
257 0 : int bf_link_attach(struct bf_link *link, enum bf_hook hook,
258 : struct bf_hookopts **hookopts, int prog_fd)
259 : {
260 : int r;
261 :
262 0 : bf_assert(link && hookopts);
263 :
264 0 : switch (bf_hook_to_flavor(hook)) {
265 0 : case BF_FLAVOR_XDP:
266 0 : r = _bf_link_attach_xdp(link, hook, *hookopts, prog_fd);
267 0 : break;
268 0 : case BF_FLAVOR_TC:
269 0 : r = _bf_link_attach_tc(link, hook, *hookopts, prog_fd);
270 0 : break;
271 0 : case BF_FLAVOR_CGROUP:
272 0 : r = _bf_link_attach_cgroup(link, hook, *hookopts, prog_fd);
273 0 : break;
274 0 : case BF_FLAVOR_NF:
275 0 : r = _bf_link_attach_nf(link, hook, *hookopts, prog_fd);
276 0 : break;
277 : default:
278 : return -ENOTSUP;
279 : }
280 :
281 0 : if (r)
282 : return r;
283 :
284 0 : link->hookopts = TAKE_PTR(*hookopts);
285 :
286 0 : return 0;
287 : }
288 :
289 0 : static int _bf_link_update(struct bf_link *link, enum bf_hook hook, int prog_fd)
290 : {
291 : union bpf_attr attr;
292 :
293 : UNUSED(hook);
294 :
295 0 : bf_assert(link);
296 :
297 0 : memset(&attr, 0, sizeof(attr));
298 :
299 0 : attr.link_update.link_fd = link->fd;
300 0 : attr.link_update.new_prog_fd = prog_fd;
301 :
302 0 : return bf_bpf(BPF_LINK_UPDATE, &attr);
303 : }
304 :
305 0 : static int _bf_link_update_nf(struct bf_link *link, enum bf_hook hook,
306 : int prog_fd)
307 : {
308 0 : _cleanup_close_ int new_link_fd = -1;
309 : union bpf_attr attr;
310 : int priorities[2];
311 :
312 0 : bf_assert(link);
313 :
314 0 : memset(&attr, 0, sizeof(attr));
315 :
316 0 : attr.link_create.prog_fd = prog_fd;
317 0 : attr.link_create.attach_type = BPF_NETFILTER;
318 :
319 0 : memcpy(priorities, link->hookopts->priorities, sizeof(priorities));
320 :
321 0 : attr.link_create.netfilter.pf = link->hookopts->family;
322 0 : attr.link_create.netfilter.hooknum = bf_hook_to_nf_hook(hook);
323 0 : attr.link_create.netfilter.priority = priorities[1];
324 :
325 0 : new_link_fd = bf_bpf(BPF_LINK_CREATE, &attr);
326 0 : if (new_link_fd < 0)
327 : return new_link_fd;
328 :
329 : // Swap priorities, so priorities[0] is the one currently used
330 0 : link->hookopts->priorities[0] = priorities[1];
331 0 : link->hookopts->priorities[1] = priorities[0];
332 :
333 0 : return 0;
334 : }
335 :
336 0 : int bf_link_update(struct bf_link *link, enum bf_hook hook, int prog_fd)
337 : {
338 : int r;
339 :
340 0 : bf_assert(link);
341 :
342 0 : switch (bf_hook_to_flavor(hook)) {
343 0 : case BF_FLAVOR_XDP:
344 : case BF_FLAVOR_TC:
345 : case BF_FLAVOR_CGROUP:
346 0 : r = _bf_link_update(link, hook, prog_fd);
347 0 : break;
348 0 : case BF_FLAVOR_NF:
349 0 : r = _bf_link_update_nf(link, hook, prog_fd);
350 0 : break;
351 : default:
352 : return -ENOTSUP;
353 : }
354 :
355 : return r;
356 : }
357 :
358 0 : void bf_link_detach(struct bf_link *link)
359 : {
360 : union bpf_attr attr;
361 : int r;
362 :
363 0 : bf_assert(link);
364 :
365 0 : memset(&attr, 0, sizeof(attr));
366 :
367 0 : attr.link_detach.link_fd = link->fd;
368 :
369 0 : r = bf_bpf(BPF_LINK_DETACH, &attr);
370 0 : if (r) {
371 0 : bf_warn_r(
372 : r,
373 : "call to BPF_LINK_DETACH failed, closing the file descriptor and assuming the link is destroyed");
374 : }
375 :
376 0 : bf_hookopts_free(&link->hookopts);
377 0 : closep(&link->fd);
378 0 : }
379 :
380 0 : int bf_link_pin(struct bf_link *link, int dir_fd)
381 : {
382 : int r;
383 :
384 0 : bf_assert(link);
385 0 : bf_assert(dir_fd > 0);
386 :
387 0 : r = bf_bpf_obj_pin(link->name, link->fd, dir_fd);
388 0 : if (r)
389 0 : return bf_err_r(r, "failed to pin BPF link '%s'", link->name);
390 :
391 : return 0;
392 : }
393 :
394 0 : void bf_link_unpin(struct bf_link *link, int dir_fd)
395 : {
396 : int r;
397 :
398 0 : bf_assert(link);
399 0 : bf_assert(dir_fd > 0);
400 :
401 0 : r = unlinkat(dir_fd, link->name, 0);
402 0 : if (r < 0 && errno != ENOENT) {
403 : // Do not warn on ENOENT, we want the file to be gone!
404 0 : bf_warn_r(
405 : errno,
406 : "failed to unlink BPF link '%s', assuming the link is not pinned",
407 : link->name);
408 : }
409 0 : }
|