Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/cgen/program.h"
7 :
8 : #include <linux/bpf.h>
9 : #include <linux/bpf_common.h>
10 : #include <linux/limits.h>
11 :
12 : #include <errno.h>
13 : #include <fcntl.h>
14 : #include <limits.h>
15 : #include <stddef.h>
16 : #include <stdint.h>
17 : #include <stdio.h>
18 : #include <stdlib.h>
19 : #include <string.h>
20 : #include <unistd.h>
21 :
22 : #include "bpfilter/cgen/cgroup.h"
23 : #include "bpfilter/cgen/dump.h"
24 : #include "bpfilter/cgen/fixup.h"
25 : #include "bpfilter/cgen/jmp.h"
26 : #include "bpfilter/cgen/matcher/ip4.h"
27 : #include "bpfilter/cgen/matcher/ip6.h"
28 : #include "bpfilter/cgen/matcher/meta.h"
29 : #include "bpfilter/cgen/matcher/set.h"
30 : #include "bpfilter/cgen/matcher/tcp.h"
31 : #include "bpfilter/cgen/matcher/udp.h"
32 : #include "bpfilter/cgen/nf.h"
33 : #include "bpfilter/cgen/printer.h"
34 : #include "bpfilter/cgen/prog/link.h"
35 : #include "bpfilter/cgen/prog/map.h"
36 : #include "bpfilter/cgen/stub.h"
37 : #include "bpfilter/cgen/tc.h"
38 : #include "bpfilter/cgen/xdp.h"
39 : #include "bpfilter/ctx.h"
40 : #include "core/bpf.h"
41 : #include "core/btf.h"
42 : #include "core/chain.h"
43 : #include "core/counter.h"
44 : #include "core/dump.h"
45 : #include "core/flavor.h"
46 : #include "core/front.h"
47 : #include "core/helper.h"
48 : #include "core/hook.h"
49 : #include "core/io.h"
50 : #include "core/list.h"
51 : #include "core/logger.h"
52 : #include "core/marsh.h"
53 : #include "core/matcher.h"
54 : #include "core/opts.h"
55 : #include "core/rule.h"
56 : #include "core/set.h"
57 : #include "core/verdict.h"
58 :
59 : #include "external/filter.h"
60 : #include "external/murmur3.h"
61 :
62 : #define _BF_PROGRAM_DEFAULT_IMG_SIZE (1 << 6)
63 :
64 16 : static const struct bf_flavor_ops *bf_flavor_ops_get(enum bf_hook hook)
65 : {
66 : static const struct bf_flavor_ops *flavor_ops[] = {
67 : [BF_HOOK_XDP] = &bf_flavor_ops_xdp,
68 : [BF_HOOK_TC_INGRESS] = &bf_flavor_ops_tc,
69 : [BF_HOOK_NF_PRE_ROUTING] = &bf_flavor_ops_nf,
70 : [BF_HOOK_NF_LOCAL_IN] = &bf_flavor_ops_nf,
71 : [BF_HOOK_NF_FORWARD] = &bf_flavor_ops_nf,
72 : [BF_HOOK_CGROUP_INGRESS] = &bf_flavor_ops_cgroup,
73 : [BF_HOOK_CGROUP_EGRESS] = &bf_flavor_ops_cgroup,
74 : [BF_HOOK_NF_LOCAL_OUT] = &bf_flavor_ops_nf,
75 : [BF_HOOK_NF_POST_ROUTING] = &bf_flavor_ops_nf,
76 : [BF_HOOK_TC_EGRESS] = &bf_flavor_ops_tc,
77 : };
78 :
79 16 : bf_assert(0 <= hook && hook < _BF_HOOK_MAX);
80 : static_assert(ARRAY_SIZE(flavor_ops) == _BF_HOOK_MAX,
81 : "missing entries in flavors array");
82 :
83 14 : return flavor_ops[hook];
84 : }
85 :
86 : /**
87 : * Generate the program's identifier.
88 : *
89 : * A program's ID is a way to idenfity it: there shouldn't be two programs
90 : * with the same ID. This ID will be used when we need to avoid name clashes,
91 : * such as when the BPF objects are pinned to the system.
92 : *
93 : * The ID is composed of 3 parts:
94 : * - The @c bf prefix, as the ID will be used as a prefix for the BPF objects
95 : * name.
96 : * - A hook idenfitier
97 : * - A hash of the unique property of the hook: multiple programs can be
98 : * generated for a given hook, so we hash the data that make this specific
99 : * program unique amongst other programs attached to the same hook.
100 : *
101 : * @param program Program to generate the ID for. Can't be NULL.
102 : * @return 0 on success, or a negative errno value on error.
103 : */
104 4 : static int _bf_program_genid(struct bf_program *program)
105 : {
106 : static const char *flavor_keys[] = {
107 : [BF_HOOK_XDP] = "xdp",
108 : [BF_HOOK_TC_INGRESS] = "tci",
109 : [BF_HOOK_NF_PRE_ROUTING] = "nfp",
110 : [BF_HOOK_NF_LOCAL_IN] = "nfi",
111 : [BF_HOOK_CGROUP_INGRESS] = "cgi",
112 : [BF_HOOK_CGROUP_EGRESS] = "cge",
113 : [BF_HOOK_NF_FORWARD] = "nff",
114 : [BF_HOOK_NF_LOCAL_OUT] = "nfo",
115 : [BF_HOOK_NF_POST_ROUTING] = "nfr",
116 : [BF_HOOK_TC_EGRESS] = "tce",
117 : };
118 : static_assert(ARRAY_SIZE(flavor_keys) == _BF_HOOK_MAX,
119 : "missing entries in flavor_keys array");
120 :
121 : char buf[PATH_MAX];
122 : uint32_t hash;
123 :
124 4 : bf_assert(program);
125 :
126 : // If the chain has a name, use it as ID
127 4 : if (program->runtime.chain->hook_opts.used_opts & (1 << BF_HOOK_OPT_NAME)) {
128 0 : (void)snprintf(program->id, BF_PROG_ID_LEN,
129 0 : program->runtime.chain->hook_opts.name);
130 0 : return 0;
131 : }
132 :
133 4 : switch (program->hook) {
134 3 : case BF_HOOK_XDP:
135 : case BF_HOOK_TC_INGRESS:
136 : case BF_HOOK_TC_EGRESS:
137 3 : (void)snprintf(buf, PATH_MAX, "%s_%08x", flavor_keys[program->hook],
138 3 : program->runtime.chain->hook_opts.ifindex);
139 3 : break;
140 1 : case BF_HOOK_NF_PRE_ROUTING:
141 : case BF_HOOK_NF_LOCAL_IN:
142 : case BF_HOOK_NF_FORWARD:
143 : case BF_HOOK_NF_LOCAL_OUT:
144 : case BF_HOOK_NF_POST_ROUTING:
145 1 : (void)snprintf(buf, PATH_MAX, "%s", flavor_keys[program->hook]);
146 1 : break;
147 0 : case BF_HOOK_CGROUP_INGRESS:
148 : case BF_HOOK_CGROUP_EGRESS:
149 0 : (void)snprintf(buf, PATH_MAX, "%s_%s", flavor_keys[program->hook],
150 0 : program->runtime.chain->hook_opts.cgroup);
151 0 : break;
152 0 : default:
153 0 : return bf_err_r(-ENOTSUP, "hook %d is not supported", program->hook);
154 : }
155 :
156 4 : murmur3_x86_32(buf, (int)strlen(buf), 0, &hash);
157 :
158 4 : (void)snprintf(program->id, BF_PROG_ID_LEN, "bf_%3s_%04x",
159 4 : flavor_keys[program->hook], (uint16_t)(hash & 0xff));
160 :
161 4 : return 0;
162 : }
163 :
164 4 : int bf_program_new(struct bf_program **program, enum bf_hook hook,
165 : enum bf_front front, const struct bf_chain *chain)
166 : {
167 4 : _cleanup_bf_program_ struct bf_program *_program = NULL;
168 : char name[BPF_OBJ_NAME_LEN];
169 : uint32_t set_idx = 0;
170 : int r;
171 :
172 4 : bf_assert(chain);
173 :
174 4 : _program = calloc(1, sizeof(*_program));
175 4 : if (!_program)
176 : return -ENOMEM;
177 :
178 4 : _program->hook = hook;
179 4 : _program->front = front;
180 4 : _program->runtime.ops = bf_flavor_ops_get(hook);
181 4 : _program->runtime.chain = chain;
182 :
183 4 : r = _bf_program_genid(_program);
184 4 : if (r) {
185 0 : return bf_err_r(r,
186 : "failed to generate ID for bf_program attached to %s",
187 : bf_hook_to_str(hook));
188 : }
189 :
190 4 : (void)snprintf(_program->prog_name, BPF_OBJ_NAME_LEN, "%s_prg",
191 4 : _program->id);
192 :
193 4 : (void)snprintf(name, BPF_OBJ_NAME_LEN, "%s_cmp", _program->id);
194 4 : r = bf_map_new(&_program->cmap, name, BF_MAP_TYPE_COUNTERS,
195 : BF_MAP_BPF_TYPE_ARRAY, sizeof(uint32_t),
196 : sizeof(struct bf_counter), 1);
197 4 : if (r < 0)
198 0 : return bf_err_r(r, "failed to create the counters bf_map object");
199 :
200 4 : (void)snprintf(name, BPF_OBJ_NAME_LEN, "%s_pmp", _program->id);
201 4 : r = bf_map_new(&_program->pmap, name, BF_MAP_TYPE_PRINTER,
202 : BF_MAP_BPF_TYPE_ARRAY, sizeof(uint32_t),
203 : BF_MAP_VALUE_SIZE_UNKNOWN, 1);
204 4 : if (r < 0)
205 0 : return bf_err_r(r, "failed to create the printer bf_map object");
206 :
207 4 : _program->sets = bf_map_list();
208 8 : bf_list_foreach (&chain->sets, set_node) {
209 0 : struct bf_set *set = bf_list_node_get_data(set_node);
210 0 : _cleanup_bf_map_ struct bf_map *map = NULL;
211 :
212 0 : (void)snprintf(name, BPF_OBJ_NAME_LEN, "%s_s%02x", _program->id,
213 0 : (uint8_t)set_idx++);
214 0 : r = bf_map_new(&map, name, BF_MAP_TYPE_SET, BF_MAP_BPF_TYPE_HASH,
215 0 : set->elem_size, 1, bf_list_size(&set->elems));
216 0 : if (r < 0)
217 : return r;
218 :
219 0 : r = bf_list_add_tail(&_program->sets, map);
220 0 : if (r < 0)
221 : return r;
222 0 : TAKE_PTR(map);
223 : };
224 :
225 4 : _program->links = bf_link_list();
226 :
227 4 : r = bf_printer_new(&_program->printer);
228 4 : if (r)
229 : return r;
230 :
231 4 : bf_list_init(&_program->fixups,
232 4 : (bf_list_ops[]) {{.free = (bf_list_ops_free)bf_fixup_free}});
233 :
234 4 : _program->runtime.prog_fd = -1;
235 :
236 4 : *program = TAKE_PTR(_program);
237 :
238 4 : return 0;
239 : }
240 :
241 14 : void bf_program_free(struct bf_program **program)
242 : {
243 14 : if (!*program)
244 : return;
245 :
246 4 : bf_list_clean(&(*program)->fixups);
247 4 : free((*program)->img);
248 :
249 : /* Close the file descriptors if they are still open. If --transient is
250 : * used, then the file descriptors are already closed (as
251 : * bf_program_unload() has been called). Otherwise, bf_program_unload()
252 : * won't be called, but the programs are pinned, so they can be closed
253 : * safely. */
254 4 : closep(&(*program)->runtime.prog_fd);
255 :
256 4 : bf_map_free(&(*program)->cmap);
257 4 : bf_map_free(&(*program)->pmap);
258 4 : bf_list_clean(&(*program)->sets);
259 4 : bf_list_clean(&(*program)->links);
260 4 : bf_printer_free(&(*program)->printer);
261 :
262 4 : free(*program);
263 4 : *program = NULL;
264 : }
265 :
266 0 : int bf_program_marsh(const struct bf_program *program, struct bf_marsh **marsh)
267 : {
268 0 : _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
269 : int r;
270 :
271 0 : bf_assert(program);
272 0 : bf_assert(marsh);
273 :
274 0 : r = bf_marsh_new(&_marsh, NULL, 0);
275 0 : if (r < 0)
276 : return r;
277 :
278 0 : r |= bf_marsh_add_child_raw(&_marsh, &program->hook, sizeof(program->hook));
279 0 : r |= bf_marsh_add_child_raw(&_marsh, &program->front,
280 : sizeof(program->front));
281 0 : if (r)
282 : return r;
283 :
284 : {
285 : // Serialize bf_program.counters
286 0 : _cleanup_bf_marsh_ struct bf_marsh *counters_elem = NULL;
287 :
288 0 : r = bf_map_marsh(program->cmap, &counters_elem);
289 0 : if (r < 0)
290 : return r;
291 :
292 0 : r = bf_marsh_add_child_obj(&_marsh, counters_elem);
293 0 : if (r < 0)
294 : return r;
295 : }
296 :
297 : {
298 : // Serialize bf_program.pmap
299 0 : _cleanup_bf_marsh_ struct bf_marsh *pmap_elem = NULL;
300 :
301 0 : r = bf_map_marsh(program->pmap, &pmap_elem);
302 0 : if (r < 0)
303 : return r;
304 :
305 0 : r = bf_marsh_add_child_obj(&_marsh, pmap_elem);
306 0 : if (r < 0)
307 : return r;
308 : }
309 :
310 : {
311 : // Serialize bf_program.sets
312 0 : _cleanup_bf_marsh_ struct bf_marsh *sets_elem = NULL;
313 :
314 0 : r = bf_list_marsh(&program->sets, &sets_elem);
315 0 : if (r < 0)
316 : return r;
317 :
318 0 : r = bf_marsh_add_child_obj(&_marsh, sets_elem);
319 0 : if (r < 0) {
320 0 : return bf_err_r(
321 : r,
322 : "failed to insert serialized sets into bf_program serialized data");
323 : }
324 : }
325 :
326 : {
327 : // Serialize bf_program.links
328 0 : _cleanup_bf_marsh_ struct bf_marsh *links_elem = NULL;
329 :
330 0 : r = bf_list_marsh(&program->links, &links_elem);
331 0 : if (r)
332 : return r;
333 :
334 0 : r = bf_marsh_add_child_obj(&_marsh, links_elem);
335 0 : if (r) {
336 0 : return bf_err_r(
337 : r,
338 : "failed to insert serialized links into bf_program serialized data");
339 : }
340 : }
341 :
342 : {
343 : // Serialise bf_program.printer
344 0 : _cleanup_bf_marsh_ struct bf_marsh *child = NULL;
345 :
346 0 : r = bf_printer_marsh(program->printer, &child);
347 0 : if (r)
348 0 : return bf_err_r(r, "failed to marsh bf_printer object");
349 :
350 0 : r = bf_marsh_add_child_obj(&_marsh, child);
351 0 : if (r)
352 0 : return bf_err_r(r, "failed to append object to marsh");
353 : }
354 :
355 0 : r |= bf_marsh_add_child_raw(&_marsh, &program->num_counters,
356 : sizeof(program->num_counters));
357 0 : r |= bf_marsh_add_child_raw(&_marsh, program->img,
358 0 : program->img_size * sizeof(struct bpf_insn));
359 0 : if (r)
360 0 : return bf_err_r(r, "Failed to serialize program");
361 :
362 0 : *marsh = TAKE_PTR(_marsh);
363 :
364 0 : return 0;
365 : }
366 :
367 0 : int bf_program_unmarsh(const struct bf_marsh *marsh,
368 : struct bf_program **program,
369 : const struct bf_chain *chain)
370 : {
371 : enum bf_hook hook;
372 : enum bf_front front;
373 : char dir[PATH_MAX];
374 : _cleanup_close_ int pindir_fd = -1;
375 0 : _cleanup_bf_program_ struct bf_program *_program = NULL;
376 : struct bf_marsh *child = NULL;
377 : int r;
378 :
379 0 : bf_assert(marsh);
380 0 : bf_assert(program);
381 :
382 0 : if (!(child = bf_marsh_next_child(marsh, NULL)))
383 : return -EINVAL;
384 0 : memcpy(&hook, child->data, sizeof(hook));
385 :
386 0 : if (!(child = bf_marsh_next_child(marsh, child)))
387 : return -EINVAL;
388 0 : memcpy(&front, child->data, sizeof(front));
389 :
390 0 : r = bf_program_new(&_program, hook, front, chain);
391 0 : if (r < 0)
392 : return r;
393 :
394 0 : (void)snprintf(dir, PATH_MAX, "%s/%s", BF_PIN_DIR, _program->id);
395 0 : pindir_fd = open(dir, O_DIRECTORY, 0);
396 0 : if (pindir_fd < 0) {
397 0 : return bf_err_r(errno, "failed to open bf_program pin directory %s",
398 : dir);
399 : }
400 :
401 0 : if (!(child = bf_marsh_next_child(marsh, child)))
402 : return -EINVAL;
403 0 : bf_map_free(&_program->cmap);
404 0 : r = bf_map_new_from_marsh(&_program->cmap, pindir_fd, child);
405 0 : if (r < 0)
406 : return r;
407 :
408 0 : if (!(child = bf_marsh_next_child(marsh, child)))
409 : return -EINVAL;
410 0 : bf_map_free(&_program->pmap);
411 0 : r = bf_map_new_from_marsh(&_program->pmap, pindir_fd, child);
412 0 : if (r < 0)
413 : return r;
414 :
415 : /** @todo Avoid creating and filling the list in @ref bf_program_new before
416 : * trashing it all here. Eventually, this function will be replaced with
417 : * @c bf_program_new_from_marsh and this issue could be solved by **not**
418 : * relying on @ref bf_program_new to allocate an initialize @p _program . */
419 0 : bf_list_clean(&_program->sets);
420 0 : _program->sets = bf_map_list();
421 :
422 0 : if (!(child = bf_marsh_next_child(marsh, child)))
423 : return -EINVAL;
424 : {
425 : // Unmarsh bf_program.sets
426 : struct bf_marsh *set_elem = NULL;
427 :
428 0 : while ((set_elem = bf_marsh_next_child(child, set_elem))) {
429 0 : _cleanup_bf_map_ struct bf_map *map = NULL;
430 :
431 0 : r = bf_map_new_from_marsh(&map, pindir_fd, set_elem);
432 0 : if (r < 0)
433 : return r;
434 :
435 0 : r = bf_list_add_tail(&_program->sets, map);
436 0 : if (r < 0)
437 : return r;
438 :
439 0 : TAKE_PTR(map);
440 : }
441 : }
442 :
443 : // Unmarsh bf_program.links
444 0 : if (!(child = bf_marsh_next_child(marsh, child)))
445 : return -EINVAL;
446 : {
447 : struct bf_marsh *link_elem = NULL;
448 :
449 0 : while ((link_elem = bf_marsh_next_child(child, link_elem))) {
450 0 : _cleanup_bf_link_ struct bf_link *link = NULL;
451 :
452 0 : r = bf_link_new_from_marsh(&link, pindir_fd, link_elem);
453 0 : if (r)
454 : return r;
455 :
456 0 : r = bf_list_add_tail(&_program->links, link);
457 0 : if (r)
458 : return r;
459 :
460 0 : TAKE_PTR(link);
461 : }
462 : }
463 :
464 : // Unmarsh bf_program.printer
465 0 : child = bf_marsh_next_child(marsh, child);
466 0 : if (!child)
467 0 : return bf_err_r(-EINVAL, "failed to find valid child");
468 :
469 0 : bf_printer_free(&_program->printer);
470 0 : r = bf_printer_new_from_marsh(&_program->printer, child);
471 0 : if (r)
472 0 : return bf_err_r(r, "failed to restore bf_printer object");
473 :
474 0 : if (!(child = bf_marsh_next_child(marsh, child)))
475 : return -EINVAL;
476 0 : memcpy(&_program->num_counters, child->data,
477 : sizeof(_program->num_counters));
478 :
479 0 : if (!(child = bf_marsh_next_child(marsh, child)))
480 : return -EINVAL;
481 0 : _program->img = bf_memdup(child->data, child->data_len);
482 0 : _program->img_size = child->data_len / sizeof(struct bpf_insn);
483 0 : _program->img_cap = child->data_len / sizeof(struct bpf_insn);
484 :
485 0 : if (bf_marsh_next_child(marsh, child))
486 0 : bf_warn("codegen marsh has more children than expected");
487 :
488 0 : r = bf_bpf_obj_get(_program->prog_name, pindir_fd,
489 0 : &_program->runtime.prog_fd);
490 0 : if (r < 0)
491 0 : return bf_err_r(r, "failed to get prog fd");
492 :
493 0 : *program = TAKE_PTR(_program);
494 :
495 0 : return 0;
496 : }
497 :
498 0 : void bf_program_dump(const struct bf_program *program, prefix_t *prefix)
499 : {
500 0 : bf_assert(program);
501 0 : bf_assert(prefix);
502 :
503 0 : DUMP(prefix, "struct bf_program at %p", program);
504 :
505 0 : bf_dump_prefix_push(prefix);
506 :
507 0 : DUMP(prefix, "id: %s", program->id);
508 0 : DUMP(prefix, "hook: %s", bf_hook_to_str(program->hook));
509 0 : DUMP(prefix, "front: %s", bf_front_to_str(program->front));
510 0 : DUMP(prefix, "num_counters: %lu", program->num_counters);
511 0 : DUMP(prefix, "prog_name: %s", program->prog_name);
512 :
513 0 : DUMP(prefix, "cmap: struct bf_map *");
514 0 : bf_dump_prefix_push(prefix);
515 0 : bf_map_dump(program->cmap, bf_dump_prefix_last(prefix));
516 0 : bf_dump_prefix_pop(prefix);
517 :
518 0 : DUMP(prefix, "pmap: struct bf_map *");
519 0 : bf_dump_prefix_push(prefix);
520 0 : bf_map_dump(program->pmap, bf_dump_prefix_last(prefix));
521 0 : bf_dump_prefix_pop(prefix);
522 :
523 0 : DUMP(prefix, "sets: bf_list<bf_map>[%lu]", bf_list_size(&program->sets));
524 0 : bf_dump_prefix_push(prefix);
525 0 : bf_list_foreach (&program->sets, map_node) {
526 0 : struct bf_map *map = bf_list_node_get_data(map_node);
527 :
528 0 : if (bf_list_is_tail(&program->sets, map_node))
529 0 : bf_dump_prefix_last(prefix);
530 :
531 0 : bf_map_dump(map, prefix);
532 : }
533 0 : bf_dump_prefix_pop(prefix);
534 :
535 0 : DUMP(prefix, "links: bf_list<bf_link>[%lu]", bf_list_size(&program->links));
536 0 : bf_dump_prefix_push(prefix);
537 0 : bf_list_foreach (&program->links, link_node) {
538 0 : struct bf_link *link = bf_list_node_get_data(link_node);
539 :
540 0 : if (bf_list_is_tail(&program->links, link_node))
541 0 : bf_dump_prefix_last(prefix);
542 :
543 0 : bf_link_dump(link, prefix);
544 : }
545 0 : bf_dump_prefix_pop(prefix);
546 :
547 0 : DUMP(prefix, "printer: struct bf_printer *");
548 0 : bf_dump_prefix_push(prefix);
549 0 : bf_printer_dump(program->printer, prefix);
550 0 : bf_dump_prefix_pop(prefix);
551 :
552 0 : DUMP(prefix, "img: %p", program->img);
553 0 : DUMP(prefix, "img_size: %lu", program->img_size);
554 0 : DUMP(prefix, "img_cap: %lu", program->img_cap);
555 :
556 0 : DUMP(prefix, "fixups: bf_list<struct bf_fixup>[%lu]",
557 : bf_list_size(&program->fixups));
558 0 : bf_dump_prefix_push(prefix);
559 0 : bf_list_foreach (&program->fixups, fixup_node) {
560 0 : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
561 :
562 0 : if (bf_list_is_tail(&program->fixups, fixup_node))
563 0 : bf_dump_prefix_last(prefix);
564 :
565 0 : bf_fixup_dump(fixup, prefix);
566 : }
567 0 : bf_dump_prefix_pop(prefix);
568 :
569 0 : DUMP(bf_dump_prefix_last(prefix), "runtime: <anonymous>");
570 0 : bf_dump_prefix_push(prefix);
571 0 : DUMP(prefix, "prog_fd: %d", program->runtime.prog_fd);
572 0 : DUMP(bf_dump_prefix_last(prefix), "ops: %p", program->runtime.ops);
573 0 : bf_dump_prefix_pop(prefix);
574 :
575 0 : bf_dump_prefix_pop(prefix);
576 0 : }
577 :
578 0 : static inline size_t _bf_round_next_power_of_2(size_t value)
579 : {
580 0 : value--;
581 0 : value |= value >> 1;
582 0 : value |= value >> 2;
583 0 : value |= value >> 4;
584 0 : value |= value >> 8;
585 0 : value |= value >> 16;
586 :
587 0 : return ++value;
588 : }
589 :
590 4 : int bf_program_grow_img(struct bf_program *program)
591 : {
592 : size_t new_cap = _BF_PROGRAM_DEFAULT_IMG_SIZE;
593 : int r;
594 :
595 4 : bf_assert(program);
596 :
597 4 : if (program->img)
598 0 : new_cap = _bf_round_next_power_of_2(program->img_cap << 1);
599 :
600 4 : r = bf_realloc((void **)&program->img, new_cap * sizeof(struct bpf_insn));
601 4 : if (r < 0) {
602 0 : return bf_err_r(r, "failed to grow program img from %lu to %lu insn",
603 : program->img_cap, new_cap);
604 : }
605 :
606 4 : program->img_cap = new_cap;
607 :
608 4 : return 0;
609 : }
610 :
611 0 : static void _bf_program_fixup_insn(struct bpf_insn *insn,
612 : enum bf_fixup_insn type, int32_t value)
613 : {
614 0 : switch (type) {
615 0 : case BF_FIXUP_INSN_OFF:
616 0 : bf_assert(!insn->off);
617 0 : bf_assert(value < SHRT_MAX);
618 0 : insn->off = (int16_t)value;
619 0 : break;
620 0 : case BF_FIXUP_INSN_IMM:
621 0 : bf_assert(!insn->imm);
622 0 : insn->imm = value;
623 0 : break;
624 0 : default:
625 0 : bf_abort(
626 : "unsupported fixup instruction type, this should not happen: %d",
627 : type);
628 : break;
629 : }
630 0 : }
631 :
632 0 : static int _bf_program_fixup(struct bf_program *program,
633 : enum bf_fixup_type type)
634 : {
635 0 : bf_assert(program);
636 0 : bf_assert(type >= 0 && type < _BF_FIXUP_TYPE_MAX);
637 :
638 0 : bf_list_foreach (&program->fixups, fixup_node) {
639 : enum bf_fixup_insn insn_type = _BF_FIXUP_INSN_MAX;
640 : int32_t value;
641 : size_t offset;
642 0 : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
643 0 : struct bpf_insn *insn = &program->img[fixup->insn];
644 : struct bf_map *map;
645 :
646 0 : if (type != fixup->type)
647 0 : continue;
648 :
649 0 : switch (type) {
650 0 : case BF_FIXUP_TYPE_JMP_NEXT_RULE:
651 : insn_type = BF_FIXUP_INSN_OFF;
652 0 : value = (int)(program->img_size - fixup->insn - 1U);
653 0 : break;
654 0 : case BF_FIXUP_TYPE_COUNTERS_MAP_FD:
655 : insn_type = BF_FIXUP_INSN_IMM;
656 0 : value = program->cmap->fd;
657 0 : break;
658 0 : case BF_FIXUP_TYPE_PRINTER_MAP_FD:
659 : insn_type = BF_FIXUP_INSN_IMM;
660 0 : value = program->pmap->fd;
661 0 : break;
662 0 : case BF_FIXUP_TYPE_SET_MAP_FD:
663 0 : map = bf_list_get_at(&program->sets, insn->imm);
664 0 : if (!map) {
665 0 : return bf_err_r(-ENOENT, "can't find set map at index %d",
666 : insn->imm);
667 : }
668 : insn_type = BF_FIXUP_INSN_IMM;
669 0 : value = map->fd;
670 0 : break;
671 0 : case BF_FIXUP_TYPE_FUNC_CALL:
672 : insn_type = BF_FIXUP_INSN_IMM;
673 0 : offset = program->functions_location[fixup->attr.function] -
674 : fixup->insn - 1;
675 0 : bf_assert(offset < INT_MAX);
676 0 : value = (int32_t)offset;
677 0 : break;
678 0 : default:
679 0 : bf_abort("unsupported fixup type, this should not happen: %d",
680 : type);
681 : break;
682 : }
683 :
684 0 : _bf_program_fixup_insn(insn, insn_type, value);
685 0 : bf_list_delete(&program->fixups, fixup_node);
686 : }
687 :
688 : return 0;
689 : }
690 :
691 0 : static int _bf_program_generate_rule(struct bf_program *program,
692 : struct bf_rule *rule)
693 : {
694 : int r;
695 :
696 0 : bf_assert(program);
697 0 : bf_assert(rule);
698 :
699 0 : bf_list_foreach (&rule->matchers, matcher_node) {
700 0 : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
701 :
702 0 : switch (matcher->type) {
703 0 : case BF_MATCHER_META_IFINDEX:
704 : case BF_MATCHER_META_L3_PROTO:
705 : case BF_MATCHER_META_L4_PROTO:
706 : case BF_MATCHER_META_SPORT:
707 : case BF_MATCHER_META_DPORT:
708 0 : r = bf_matcher_generate_meta(program, matcher);
709 0 : if (r)
710 : return r;
711 : break;
712 0 : case BF_MATCHER_IP4_SRC_ADDR:
713 : case BF_MATCHER_IP4_DST_ADDR:
714 : case BF_MATCHER_IP4_PROTO:
715 0 : r = bf_matcher_generate_ip4(program, matcher);
716 0 : if (r)
717 : return r;
718 : break;
719 0 : case BF_MATCHER_IP6_SADDR:
720 : case BF_MATCHER_IP6_DADDR:
721 0 : r = bf_matcher_generate_ip6(program, matcher);
722 0 : if (r)
723 : return r;
724 : break;
725 0 : case BF_MATCHER_TCP_SPORT:
726 : case BF_MATCHER_TCP_DPORT:
727 : case BF_MATCHER_TCP_FLAGS:
728 0 : r = bf_matcher_generate_tcp(program, matcher);
729 0 : if (r)
730 : return r;
731 : break;
732 0 : case BF_MATCHER_UDP_SPORT:
733 : case BF_MATCHER_UDP_DPORT:
734 0 : r = bf_matcher_generate_udp(program, matcher);
735 0 : if (r)
736 : return r;
737 : break;
738 0 : case BF_MATCHER_SET_SRCIP6PORT:
739 : case BF_MATCHER_SET_SRCIP6:
740 0 : r = bf_matcher_generate_set(program, matcher);
741 0 : if (r)
742 : return r;
743 : break;
744 0 : default:
745 0 : return bf_err_r(-EINVAL, "unknown matcher type %d", matcher->type);
746 : };
747 : }
748 :
749 0 : if (rule->counters) {
750 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_1, rule->index));
751 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10,
752 : BF_PROG_CTX_OFF(pkt_size)));
753 0 : EMIT_FIXUP_CALL(program, BF_FIXUP_FUNC_UPDATE_COUNTERS);
754 : }
755 :
756 0 : switch (rule->verdict) {
757 0 : case BF_VERDICT_ACCEPT:
758 : case BF_VERDICT_DROP:
759 0 : EMIT(program,
760 : BPF_MOV64_IMM(BPF_REG_0,
761 : program->runtime.ops->get_verdict(rule->verdict)));
762 0 : EMIT(program, BPF_EXIT_INSN());
763 0 : break;
764 : case BF_VERDICT_CONTINUE:
765 : // Fall through to next rule or default chain policy.
766 : break;
767 0 : default:
768 0 : bf_abort("unsupported verdict, this should not happen: %d",
769 : rule->verdict);
770 : break;
771 : }
772 :
773 0 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_JMP_NEXT_RULE);
774 0 : if (r)
775 0 : return bf_err_r(r, "failed to generate next rule fixups");
776 :
777 : return 0;
778 : }
779 :
780 : /**
781 : * Generate the BPF function to update a rule's counters.
782 : *
783 : * This function defines a new function **in** the generated BPF program to
784 : * be called during packet processing.
785 : *
786 : * Parameters:
787 : * - @c r1 : index of the rule to update the counters for.
788 : * - @c r2 : size of the packet.
789 : * Returns:
790 : * 0 on success, non-zero on error.
791 : *
792 : * @param program Program to emit the function into. Can not be NULL.
793 : * @return 0 on success, or negative errno value on error.
794 : */
795 0 : static int _bf_program_generate_update_counters(struct bf_program *program)
796 : {
797 : // Move the counters key in scratch[0..4] and the packet size in scratch[8..15]
798 0 : EMIT(program,
799 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1, BF_PROG_SCR_OFF(0)));
800 0 : EMIT(program,
801 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_SCR_OFF(8)));
802 :
803 : // Call bpf_map_lookup_elem()
804 0 : EMIT_LOAD_COUNTERS_FD_FIXUP(program, BPF_REG_1);
805 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10));
806 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, BF_PROG_SCR_OFF(0)));
807 0 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem));
808 :
809 : // If the counters doesn't exist, return from the function
810 : {
811 0 : _cleanup_bf_jmpctx_ struct bf_jmpctx _ =
812 0 : bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0));
813 :
814 0 : if (bf_opts_is_verbose(BF_VERBOSE_BPF))
815 0 : EMIT_PRINT(program, "failed to fetch the rule's counters");
816 :
817 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_0, 1));
818 0 : EMIT(program, BPF_EXIT_INSN());
819 : }
820 :
821 : // Increment the packets count by 1.
822 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
823 : offsetof(struct bf_counter, packets)));
824 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1));
825 0 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1,
826 : offsetof(struct bf_counter, packets)));
827 :
828 : // Increase the total byte by the size of the packet.
829 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
830 : offsetof(struct bf_counter, bytes)));
831 0 : EMIT(program,
832 : BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, BF_PROG_SCR_OFF(8)));
833 0 : EMIT(program, BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2));
834 0 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1,
835 : offsetof(struct bf_counter, bytes)));
836 :
837 : // On success, return 0
838 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_0, 0));
839 0 : EMIT(program, BPF_EXIT_INSN());
840 :
841 0 : return 0;
842 : }
843 :
844 0 : static int _bf_program_generate_functions(struct bf_program *program)
845 : {
846 : int r;
847 :
848 0 : bf_assert(program);
849 :
850 0 : bf_list_foreach (&program->fixups, fixup_node) {
851 0 : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
852 0 : size_t off = program->img_size;
853 :
854 0 : if (fixup->type != BF_FIXUP_TYPE_FUNC_CALL)
855 0 : continue;
856 :
857 0 : bf_assert(fixup->attr.function >= 0 &&
858 : fixup->attr.function < _BF_FIXUP_FUNC_MAX);
859 :
860 : // Only generate each function once
861 0 : if (program->functions_location[fixup->attr.function])
862 0 : continue;
863 :
864 0 : switch (fixup->attr.function) {
865 0 : case BF_FIXUP_FUNC_UPDATE_COUNTERS:
866 0 : r = _bf_program_generate_update_counters(program);
867 0 : if (r)
868 : return r;
869 : break;
870 0 : default:
871 0 : bf_abort("unsupported fixup function, this should not happen: %d",
872 : fixup->attr.function);
873 : break;
874 : }
875 :
876 0 : program->functions_location[fixup->attr.function] = off;
877 : }
878 :
879 : return 0;
880 : }
881 :
882 41 : int bf_program_emit(struct bf_program *program, struct bpf_insn insn)
883 : {
884 : int r;
885 :
886 41 : bf_assert(program);
887 :
888 41 : if (program->img_size == program->img_cap) {
889 3 : r = bf_program_grow_img(program);
890 3 : if (r)
891 : return r;
892 : }
893 :
894 41 : program->img[program->img_size++] = insn;
895 :
896 41 : return 0;
897 : }
898 :
899 0 : int bf_program_emit_kfunc_call(struct bf_program *program, const char *name)
900 : {
901 : int r;
902 :
903 0 : bf_assert(program);
904 0 : bf_assert(name);
905 :
906 0 : r = bf_btf_get_id(name);
907 0 : if (r < 0)
908 : return r;
909 :
910 0 : EMIT(program, ((struct bpf_insn) {.code = BPF_JMP | BPF_CALL,
911 : .dst_reg = 0,
912 : .src_reg = BPF_PSEUDO_KFUNC_CALL,
913 : .off = 0,
914 : .imm = r}));
915 :
916 0 : return 0;
917 : }
918 :
919 0 : int bf_program_emit_fixup(struct bf_program *program, enum bf_fixup_type type,
920 : struct bpf_insn insn, const union bf_fixup_attr *attr)
921 : {
922 0 : _cleanup_bf_fixup_ struct bf_fixup *fixup = NULL;
923 : int r;
924 :
925 0 : bf_assert(program);
926 :
927 0 : if (program->img_size == program->img_cap) {
928 0 : r = bf_program_grow_img(program);
929 0 : if (r)
930 : return r;
931 : }
932 :
933 0 : r = bf_fixup_new(&fixup, type, program->img_size, attr);
934 0 : if (r)
935 : return r;
936 :
937 0 : r = bf_list_add_tail(&program->fixups, fixup);
938 0 : if (r)
939 : return r;
940 :
941 0 : TAKE_PTR(fixup);
942 :
943 : /* This call could fail and return an error, in which case it is not
944 : * properly handled. However, this shouldn't be an issue as we previously
945 : * test whether enough room is available in cgen.img, which is currently
946 : * the only reason for EMIT() to fail. */
947 0 : EMIT(program, insn);
948 :
949 : return 0;
950 : }
951 :
952 2 : int bf_program_emit_fixup_call(struct bf_program *program,
953 : enum bf_fixup_func function)
954 : {
955 1 : _cleanup_bf_fixup_ struct bf_fixup *fixup = NULL;
956 : int r;
957 :
958 2 : bf_assert(program);
959 :
960 1 : if (program->img_size == program->img_cap) {
961 1 : r = bf_program_grow_img(program);
962 1 : if (r)
963 : return r;
964 : }
965 :
966 1 : r = bf_fixup_new(&fixup, BF_FIXUP_TYPE_FUNC_CALL, program->img_size, NULL);
967 1 : if (r)
968 : return r;
969 :
970 1 : fixup->attr.function = function;
971 :
972 1 : r = bf_list_add_tail(&program->fixups, fixup);
973 1 : if (r)
974 : return r;
975 :
976 1 : TAKE_PTR(fixup);
977 :
978 : /* This call could fail and return an error, in which case it is not
979 : * properly handled. However, this shouldn't be an issue as we previously
980 : * test whether enough room is available in cgen.img, which is currently
981 : * the only reason for EMIT() to fail. */
982 1 : EMIT(program, BPF_CALL_REL(0));
983 :
984 1 : return 0;
985 : }
986 :
987 0 : int bf_program_generate(struct bf_program *program)
988 : {
989 0 : const struct bf_chain *chain = program->runtime.chain;
990 : int r;
991 :
992 0 : bf_info("generating program for %s::%s", bf_front_to_str(program->front),
993 : bf_hook_to_str(program->hook));
994 :
995 : /* Add 1 to the number of counters for the policy counter, and 1
996 : * for the first reserved error slot. This must be done ahead of
997 : * generation, as we will index into the error counters. */
998 0 : program->num_counters = bf_list_size(&chain->rules) + 2;
999 :
1000 : // Save the program's argument into the context.
1001 0 : EMIT(program,
1002 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
1003 :
1004 : // Reset the protocol ID registers
1005 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_7, 0));
1006 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_8, 0));
1007 :
1008 0 : r = program->runtime.ops->gen_inline_prologue(program);
1009 0 : if (r)
1010 : return r;
1011 :
1012 0 : bf_list_foreach (&chain->rules, rule_node) {
1013 0 : r = _bf_program_generate_rule(program,
1014 0 : bf_list_node_get_data(rule_node));
1015 0 : if (r)
1016 : return r;
1017 : }
1018 :
1019 0 : r = program->runtime.ops->gen_inline_epilogue(program);
1020 0 : if (r)
1021 : return r;
1022 :
1023 : // Call the update counters function
1024 0 : EMIT(program, BPF_MOV32_IMM(BPF_REG_1, bf_list_size(&chain->rules)));
1025 0 : EMIT(program,
1026 : BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, BF_PROG_CTX_OFF(pkt_size)));
1027 0 : EMIT_FIXUP_CALL(program, BF_FIXUP_FUNC_UPDATE_COUNTERS);
1028 :
1029 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_0, program->runtime.ops->get_verdict(
1030 : chain->policy)));
1031 0 : EMIT(program, BPF_EXIT_INSN());
1032 :
1033 0 : r = _bf_program_generate_functions(program);
1034 0 : if (r)
1035 : return r;
1036 :
1037 0 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_FUNC_CALL);
1038 0 : if (r)
1039 0 : return bf_err_r(r, "failed to generate function call fixups");
1040 :
1041 : return 0;
1042 : }
1043 :
1044 : static void _bf_program_unpin(const struct bf_program *program,
1045 : const char *dir);
1046 :
1047 : /**
1048 : * Pin the BPF objects that should survive the daemon's lifetime.
1049 : *
1050 : * If any of the BPF objects can't be pinned, unpin all of them to ensure
1051 : * there will be no leftovers.
1052 : *
1053 : * @param program Program containing the objects to pin. Can't be NULL.
1054 : * @return 0 on success, or negative erron value on failure.
1055 : */
1056 0 : static int _bf_program_pin(const struct bf_program *program)
1057 : {
1058 : _cleanup_close_ int pindir_fd = -1;
1059 : char dir[PATH_MAX];
1060 : int r;
1061 :
1062 0 : bf_assert(program);
1063 :
1064 0 : r = bf_ensure_dir(BF_PIN_DIR);
1065 0 : if (r)
1066 0 : return bf_err_r(r, "failed to ensure BPF objects pin directory exists");
1067 :
1068 0 : (void)snprintf(dir, PATH_MAX, "%s/%s", BF_PIN_DIR, program->id);
1069 :
1070 0 : r = bf_ensure_dir(dir);
1071 0 : if (r)
1072 0 : return bf_err_r(r, "failed to validate pin directory %s", dir);
1073 :
1074 0 : pindir_fd = open(dir, O_DIRECTORY, 0);
1075 0 : if (pindir_fd < 0) {
1076 0 : return bf_err_r(errno, "failed to open bf_program pin directory %s",
1077 : dir);
1078 : }
1079 :
1080 0 : r = bf_bpf_obj_pin(program->prog_name, program->runtime.prog_fd, pindir_fd);
1081 0 : if (r < 0) {
1082 0 : bf_err_r(r, "failed to pin program '%s' in %s", program->prog_name,
1083 : BF_PIN_DIR);
1084 0 : goto err_prog_pin;
1085 : }
1086 :
1087 0 : r = bf_map_pin(program->cmap, pindir_fd);
1088 0 : if (r < 0)
1089 0 : goto err_cmap_pin;
1090 :
1091 0 : r = bf_map_pin(program->pmap, pindir_fd);
1092 0 : if (r < 0)
1093 0 : goto err_pmap_pin;
1094 :
1095 0 : bf_list_foreach (&program->sets, set_node) {
1096 0 : r = bf_map_pin(bf_list_node_get_data(set_node), pindir_fd);
1097 0 : if (r < 0)
1098 0 : goto err_set_pin;
1099 : }
1100 :
1101 0 : bf_list_foreach (&program->links, link_node) {
1102 0 : r = bf_link_pin(bf_list_node_get_data(link_node), pindir_fd);
1103 0 : if (r < 0)
1104 0 : goto err_link_pin;
1105 : }
1106 :
1107 : return 0;
1108 :
1109 : err_link_pin:
1110 0 : bf_list_foreach (&program->links, link_node)
1111 0 : bf_link_unpin(bf_list_node_get_data(link_node), pindir_fd);
1112 0 : err_set_pin:
1113 0 : bf_list_foreach (&program->sets, set_node)
1114 0 : bf_map_unpin(bf_list_node_get_data(set_node), pindir_fd);
1115 0 : bf_map_unpin(program->pmap, pindir_fd);
1116 0 : err_pmap_pin:
1117 0 : bf_map_unpin(program->cmap, pindir_fd);
1118 0 : err_cmap_pin:
1119 0 : unlinkat(pindir_fd, program->prog_name, 0);
1120 0 : err_prog_pin:
1121 : closep(&pindir_fd);
1122 : return r;
1123 : }
1124 :
1125 : /**
1126 : * Unpin the BPF objects owned by a program.
1127 : *
1128 : * If the @p program object is deleted, the BPF object will disappear from
1129 : * the system.
1130 : *
1131 : * @param program Program containing the objects to unpin. Can't be NULL.
1132 : * @param dir If non NULL, override the directory to unpin the BPF objects
1133 : * from.
1134 : */
1135 0 : static void _bf_program_unpin(const struct bf_program *program, const char *dir)
1136 : {
1137 : char _dir[PATH_MAX];
1138 : int pindir_fd;
1139 : int r;
1140 :
1141 0 : bf_assert(program);
1142 :
1143 0 : if (dir)
1144 0 : (void)snprintf(_dir, PATH_MAX, "%s", dir);
1145 : else
1146 0 : (void)snprintf(_dir, PATH_MAX, "%s/%s", BF_PIN_DIR, program->id);
1147 :
1148 0 : pindir_fd = open(_dir, O_DIRECTORY, 0);
1149 0 : if (pindir_fd < 0) {
1150 0 : bf_warn_r(
1151 : errno,
1152 : "failed to open bf_program pin directory %s, assuming BPF objects are unpinned",
1153 : dir);
1154 0 : return;
1155 : }
1156 :
1157 0 : unlinkat(pindir_fd, program->prog_name, 0);
1158 0 : bf_map_unpin(program->pmap, pindir_fd);
1159 0 : bf_map_unpin(program->cmap, pindir_fd);
1160 0 : bf_list_foreach (&program->sets, set_node)
1161 0 : bf_map_unpin(bf_list_node_get_data(set_node), pindir_fd);
1162 0 : bf_list_foreach (&program->links, link_node)
1163 0 : bf_link_unpin(bf_list_node_get_data(link_node), pindir_fd);
1164 :
1165 : closep(&pindir_fd);
1166 0 : r = rmdir(_dir);
1167 0 : if (r) {
1168 0 : bf_warn_r(r, "failed to remove bf_program pin directory %s, ignoring",
1169 : _dir);
1170 : }
1171 : }
1172 :
1173 0 : static int _bf_program_load_printer_map(struct bf_program *program)
1174 : {
1175 0 : _cleanup_free_ void *pstr = NULL;
1176 : size_t pstr_len;
1177 0 : uint32_t key = 0;
1178 : int r;
1179 :
1180 0 : bf_assert(program);
1181 :
1182 0 : r = bf_printer_assemble(program->printer, &pstr, &pstr_len);
1183 0 : if (r)
1184 0 : return bf_err_r(r, "failed to assemble printer map string");
1185 :
1186 0 : r = bf_map_set_value_size(program->pmap, pstr_len);
1187 0 : if (r < 0)
1188 : return r;
1189 :
1190 0 : r = bf_map_create(program->pmap, 0);
1191 0 : if (r < 0)
1192 : return r;
1193 :
1194 0 : r = bf_map_set_elem(program->pmap, &key, pstr);
1195 0 : if (r)
1196 : return r;
1197 :
1198 0 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_PRINTER_MAP_FD);
1199 0 : if (r) {
1200 0 : bf_map_destroy(program->pmap);
1201 0 : return bf_err_r(r, "failed to fixup printer map FD");
1202 : }
1203 :
1204 : return 0;
1205 : }
1206 :
1207 0 : static int _bf_program_load_counters_map(struct bf_program *program)
1208 : {
1209 : _cleanup_close_ int _fd = -1;
1210 : int r;
1211 :
1212 0 : bf_assert(program);
1213 :
1214 0 : r = bf_map_set_n_elems(program->cmap, program->num_counters);
1215 0 : if (r < 0)
1216 : return r;
1217 :
1218 0 : r = bf_map_create(program->cmap, 0);
1219 0 : if (r < 0)
1220 : return r;
1221 :
1222 0 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_COUNTERS_MAP_FD);
1223 0 : if (r < 0) {
1224 0 : bf_map_destroy(program->cmap);
1225 0 : return bf_err_r(r, "failed to fixup counters map FD");
1226 : }
1227 :
1228 : return 0;
1229 : }
1230 :
1231 0 : static int _bf_program_load_sets_maps(struct bf_program *new_prog)
1232 : {
1233 : const bf_list_node *set_node;
1234 : const bf_list_node *map_node;
1235 : int r;
1236 :
1237 0 : bf_assert(new_prog);
1238 :
1239 0 : set_node = bf_list_get_head(&new_prog->runtime.chain->sets);
1240 0 : map_node = bf_list_get_head(&new_prog->sets);
1241 :
1242 : // Fill the bf_map with the sets content
1243 0 : while (set_node && map_node) {
1244 : _cleanup_free_ uint8_t *values = NULL;
1245 : _cleanup_free_ uint8_t *keys = NULL;
1246 0 : struct bf_set *set = bf_list_node_get_data(set_node);
1247 0 : struct bf_map *map = bf_list_node_get_data(map_node);
1248 0 : size_t nelems = bf_list_size(&set->elems);
1249 0 : union bpf_attr attr = {};
1250 : size_t idx = 0;
1251 :
1252 0 : r = bf_map_create(map, 0);
1253 0 : if (r < 0) {
1254 0 : r = bf_err_r(r, "failed to create BPF map for set");
1255 0 : goto err_destroy_maps;
1256 : }
1257 :
1258 0 : values = malloc(nelems);
1259 0 : if (!values) {
1260 0 : r = bf_err_r(errno, "failed to allocate map values");
1261 0 : goto err_destroy_maps;
1262 : }
1263 :
1264 0 : keys = malloc(set->elem_size * nelems);
1265 0 : if (!keys) {
1266 0 : r = bf_err_r(errno, "failed to allocate map keys");
1267 0 : goto err_destroy_maps;
1268 : }
1269 :
1270 0 : bf_list_foreach (&set->elems, elem_node) {
1271 0 : void *elem = bf_list_node_get_data(elem_node);
1272 :
1273 0 : memcpy(keys + (idx * set->elem_size), elem, set->elem_size);
1274 0 : values[idx] = 1;
1275 0 : ++idx;
1276 : }
1277 :
1278 0 : attr.batch.map_fd = map->fd;
1279 0 : attr.batch.keys = (unsigned long long)keys;
1280 0 : attr.batch.values = (unsigned long long)values;
1281 0 : attr.batch.count = nelems;
1282 0 : attr.batch.flags = BPF_ANY;
1283 :
1284 0 : r = bf_bpf(BPF_MAP_UPDATE_BATCH, &attr);
1285 0 : if (r < 0) {
1286 0 : bf_err_r(r, "failed to add set elements to the map");
1287 0 : goto err_destroy_maps;
1288 : }
1289 :
1290 0 : set_node = bf_list_node_next(set_node);
1291 0 : map_node = bf_list_node_next(map_node);
1292 : }
1293 :
1294 0 : r = _bf_program_fixup(new_prog, BF_FIXUP_TYPE_SET_MAP_FD);
1295 0 : if (r < 0)
1296 0 : goto err_destroy_maps;
1297 :
1298 : return 0;
1299 :
1300 0 : err_destroy_maps:
1301 0 : bf_list_foreach (&new_prog->sets, map_node)
1302 0 : bf_map_destroy(bf_list_node_get_data(map_node));
1303 : return r;
1304 : }
1305 :
1306 : /**
1307 : * Callback to create a new @ref bf_link when attaching the program to the hook.
1308 : *
1309 : * See @ref bf_flavor_ops for more details.
1310 : *
1311 : * @param prog Program to create the @ref bf_link for. Can't be NULL.
1312 : * @param old_link Existing BPF link from the old program (program to replace).
1313 : * If not NULL, the new link's FD is a duplicate of the old link's FD.
1314 : * This is useful for flavors using @c BPF_LINK_UPDATE : the link remain
1315 : * the same but the program changes, the old link can then be unpinned
1316 : * and closed safely.
1317 : * @param new_link New link to create. Can't be NULL. On success, @c *link
1318 : * points to a valid @ref bf_link . On failure, @c *link remain unchanged.
1319 : * @return 0 on success, or a negative errno value on success.
1320 : */
1321 0 : static int _bf_program_get_new_link(struct bf_program *prog,
1322 : struct bf_link *old_link,
1323 : struct bf_link **new_link)
1324 : {
1325 0 : _cleanup_bf_link_ struct bf_link *_link = NULL;
1326 : char name[BPF_OBJ_NAME_LEN];
1327 : int r;
1328 :
1329 0 : bf_assert(prog && new_link);
1330 :
1331 0 : (void)snprintf(name, BPF_OBJ_NAME_LEN, "%s_lk%1ld", prog->id,
1332 0 : bf_list_size(&prog->links));
1333 :
1334 0 : r = bf_link_new(&_link, name, prog->hook);
1335 0 : if (r)
1336 0 : return bf_err_r(r, "failed to create bf_link from callback");
1337 :
1338 0 : if (old_link) {
1339 0 : r = dup(old_link->fd);
1340 0 : if (r < 0)
1341 0 : return bf_err_r(r, "failed to duplicate old link FD");
1342 :
1343 0 : _link->fd = r;
1344 : }
1345 :
1346 0 : r = bf_list_add_tail(&prog->links, _link);
1347 0 : if (r) {
1348 0 : return bf_err_r(
1349 : r, "failed to append new bf_link to program from callback");
1350 : }
1351 :
1352 0 : *new_link = TAKE_PTR(_link);
1353 :
1354 0 : return 0;
1355 : }
1356 :
1357 0 : int bf_program_load(struct bf_program *new_prog, struct bf_program *old_prog)
1358 : {
1359 : char dir[PATH_MAX];
1360 : char tmpdir[PATH_MAX];
1361 : int r;
1362 :
1363 0 : bf_assert(new_prog);
1364 :
1365 0 : r = _bf_program_load_sets_maps(new_prog);
1366 0 : if (r < 0)
1367 : return r;
1368 :
1369 0 : r = _bf_program_load_counters_map(new_prog);
1370 0 : if (r)
1371 : return r;
1372 :
1373 0 : r = _bf_program_load_printer_map(new_prog);
1374 0 : if (r)
1375 : return r;
1376 :
1377 0 : if (bf_opts_is_verbose(BF_VERBOSE_BYTECODE))
1378 0 : bf_program_dump_bytecode(new_prog);
1379 :
1380 0 : r = bf_bpf_prog_load(
1381 0 : new_prog->prog_name, bf_hook_to_bpf_prog_type(new_prog->hook),
1382 0 : new_prog->img, new_prog->img_size,
1383 : bf_hook_to_attach_type(new_prog->hook), &new_prog->runtime.prog_fd);
1384 0 : if (r)
1385 0 : return bf_err_r(r, "failed to load new bf_program");
1386 :
1387 0 : if (old_prog) {
1388 0 : (void)snprintf(dir, PATH_MAX, "%s/%s", BF_PIN_DIR, new_prog->id);
1389 0 : (void)snprintf(tmpdir, PATH_MAX, "%s/%s_tmp", BF_PIN_DIR, new_prog->id);
1390 : }
1391 :
1392 0 : if (old_prog && !bf_opts_transient() && (r = rename(dir, tmpdir)))
1393 0 : return bf_err_r(r, "failed to rename old bf_program pin directory");
1394 :
1395 0 : if (new_prog->runtime.chain->hook_opts.attach) {
1396 0 : r = new_prog->runtime.ops->attach_prog(new_prog, old_prog,
1397 : _bf_program_get_new_link);
1398 0 : if (r) {
1399 0 : bf_err_r(r, "failed to attach new program");
1400 0 : goto err_restore_old;
1401 : }
1402 : }
1403 :
1404 0 : if (!bf_opts_transient()) {
1405 0 : r = _bf_program_pin(new_prog);
1406 0 : if (r)
1407 0 : return bf_err_r(r, "failed to pin new program!");
1408 :
1409 0 : if (old_prog)
1410 0 : _bf_program_unpin(old_prog, tmpdir);
1411 : }
1412 :
1413 : return r;
1414 :
1415 : err_restore_old:
1416 : int _r = r;
1417 0 : if ((r = rename(tmpdir, dir))) {
1418 0 : bf_err_r(r,
1419 : "failed to restore old program, the daemon is in bad state!");
1420 : } else {
1421 0 : bf_info("previous program restored");
1422 : }
1423 : return _r;
1424 : }
1425 :
1426 0 : int bf_program_unload(struct bf_program *program)
1427 : {
1428 : int r;
1429 :
1430 0 : bf_assert(program);
1431 :
1432 0 : if (program->runtime.chain->hook_opts.attach) {
1433 0 : r = program->runtime.ops->detach_prog(program);
1434 0 : if (r)
1435 : return r;
1436 : }
1437 :
1438 0 : if (!bf_opts_transient())
1439 0 : _bf_program_unpin(program, NULL);
1440 :
1441 : closep(&program->runtime.prog_fd);
1442 :
1443 0 : bf_map_destroy(program->cmap);
1444 0 : bf_map_destroy(program->pmap);
1445 :
1446 0 : bf_list_foreach (&program->sets, map_node)
1447 0 : bf_map_destroy(bf_list_node_get_data(map_node));
1448 :
1449 0 : bf_list_foreach (&program->links, link_node)
1450 0 : bf_link_detach(bf_list_node_get_data(link_node));
1451 :
1452 0 : bf_dbg("unloaded %s program from %s", bf_front_to_str(program->front),
1453 : bf_hook_to_str(program->hook));
1454 :
1455 : return 0;
1456 : }
1457 :
1458 0 : int bf_program_get_counter(const struct bf_program *program,
1459 : uint32_t counter_idx, struct bf_counter *counter)
1460 : {
1461 0 : bf_assert(program);
1462 0 : bf_assert(counter);
1463 :
1464 : int r;
1465 :
1466 0 : r = bf_bpf_map_lookup_elem(program->cmap->fd, &counter_idx, counter);
1467 0 : if (r < 0)
1468 0 : return bf_err_r(errno, "failed to lookup counters map");
1469 :
1470 : return 0;
1471 : }
1472 :
1473 0 : int bf_cgen_set_counters(struct bf_program *program,
1474 : const struct bf_counter *counters)
1475 : {
1476 : UNUSED(program);
1477 : UNUSED(counters);
1478 :
1479 0 : return -ENOTSUP;
1480 : }
|