Branch data 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 "cgen/cgen.h"
7 : :
8 : : #include <errno.h>
9 : : #include <stddef.h>
10 : : #include <stdint.h>
11 : : #include <stdio.h>
12 : : #include <stdlib.h>
13 : : #include <string.h>
14 : : #include <sys/types.h>
15 : : #include <unistd.h>
16 : :
17 : : #include <bpfilter/bpf.h>
18 : : #include <bpfilter/chain.h>
19 : : #include <bpfilter/counter.h>
20 : : #include <bpfilter/dump.h>
21 : : #include <bpfilter/helper.h>
22 : : #include <bpfilter/hook.h>
23 : : #include <bpfilter/io.h>
24 : : #include <bpfilter/list.h>
25 : : #include <bpfilter/logger.h>
26 : : #include <bpfilter/ns.h>
27 : : #include <bpfilter/pack.h>
28 : :
29 : : #include "cgen/dump.h"
30 : : #include "cgen/handle.h"
31 : : #include "cgen/prog/link.h"
32 : : #include "cgen/prog/map.h"
33 : : #include "cgen/program.h"
34 : : #include "ctx.h"
35 : :
36 : : #define _BF_PROG_NAME "bf_prog"
37 : : #define _BF_CTX_PIN_NAME "bf_ctx"
38 : : #define _BF_CTX_TMP_PIN_NAME "bf_ctx_tmp"
39 : :
40 : 175 : static int _bf_cgen_get_chain_pindir_fd(const char *name)
41 : : {
42 : 175 : _cleanup_close_ int bf_fd = -1;
43 : 175 : _cleanup_close_ int chain_fd = -1;
44 : :
45 : : assert(name);
46 : :
47 : 175 : bf_fd = bf_ctx_get_pindir_fd();
48 [ + - ]: 175 : if (bf_fd < 0)
49 : : return bf_fd;
50 : :
51 : 175 : chain_fd = bf_opendir_at(bf_fd, name, true);
52 [ - + ]: 175 : if (chain_fd < 0)
53 : : return chain_fd;
54 : :
55 : 175 : return TAKE_FD(chain_fd);
56 : : }
57 : :
58 : : /**
59 : : * @brief Persist the codegen state to a BPF context map in bpffs.
60 : : *
61 : : * Serializes the cgen, creates a `BPF_MAP_TYPE_ARRAY` map with 1 entry
62 : : * containing the serialized data, pins it as `bf_ctx_tmp`, then atomically
63 : : * renames to `bf_ctx`. The map fd is closed after pinning - this is a
64 : : * one-shot operation.
65 : : *
66 : : * @param cgen Codegen to persist. Can't be NULL.
67 : : * @param dir_fd File descriptor of the chain's bpffs pin directory. Must be
68 : : * valid.
69 : : * @return 0 on success, or negative errno value on failure.
70 : : */
71 : 117 : static int _bf_cgen_persist(const struct bf_cgen *cgen, int dir_fd)
72 : : {
73 : 117 : _free_bf_wpack_ bf_wpack_t *pack = NULL;
74 : 117 : _free_bf_map_ struct bf_map *map = NULL;
75 : : const void *data;
76 : : size_t data_len;
77 : 117 : uint32_t key = 0;
78 : : int r;
79 : :
80 : : assert(cgen);
81 : :
82 : 117 : r = bf_wpack_new(&pack);
83 [ - + ]: 117 : if (r)
84 [ # # ]: 0 : return bf_err_r(r, "failed to create wpack for bf_cgen");
85 : :
86 : 117 : r = bf_cgen_pack(cgen, pack);
87 [ - + ]: 117 : if (r)
88 [ # # ]: 0 : return bf_err_r(r, "failed to pack bf_cgen");
89 : :
90 : 117 : r = bf_wpack_get_data(pack, &data, &data_len);
91 [ - + ]: 117 : if (r)
92 [ # # ]: 0 : return bf_err_r(r, "failed to get data from bf_cgen wpack");
93 : :
94 : 117 : r = bf_map_new(&map, _BF_CTX_PIN_NAME, BF_MAP_TYPE_CTX, sizeof(uint32_t),
95 : : data_len, 1);
96 [ - + ]: 117 : if (r)
97 [ # # ]: 0 : return bf_err_r(r, "failed to create context map");
98 : :
99 : 117 : r = bf_map_set_elem(map, &key, (void *)data);
100 [ - + ]: 117 : if (r)
101 [ # # ]: 0 : return bf_err_r(r, "failed to write context to map");
102 : :
103 : : // Remove stale temporary pin if present.
104 : 117 : unlinkat(dir_fd, _BF_CTX_TMP_PIN_NAME, 0);
105 : :
106 : 117 : r = bf_bpf_obj_pin(_BF_CTX_TMP_PIN_NAME, map->fd, dir_fd);
107 [ - + ]: 117 : if (r)
108 [ # # ]: 0 : return bf_err_r(r, "failed to pin context map");
109 : :
110 : 117 : r = renameat(dir_fd, _BF_CTX_TMP_PIN_NAME, dir_fd, _BF_CTX_PIN_NAME);
111 [ - + ]: 117 : if (r) {
112 : 0 : r = -errno;
113 : 0 : unlinkat(dir_fd, _BF_CTX_TMP_PIN_NAME, 0);
114 [ # # ]: 0 : return bf_err_r(r, "failed to atomically replace context map pin");
115 : : }
116 : :
117 : : return 0;
118 : : }
119 : :
120 : 5 : static int _bf_cgen_new_from_pack(struct bf_cgen **cgen, bf_rpack_node_t node)
121 : : {
122 : 5 : _free_bf_cgen_ struct bf_cgen *_cgen = NULL;
123 : 5 : _cleanup_close_ int dir_fd = -1;
124 : : bf_rpack_node_t child;
125 : : int r;
126 : :
127 : : assert(cgen);
128 : :
129 : 5 : _cgen = calloc(1, sizeof(*_cgen));
130 [ + - ]: 5 : if (!_cgen)
131 : : return -ENOMEM;
132 : :
133 : 5 : r = bf_rpack_kv_obj(node, "chain", &child);
134 [ - + ]: 5 : if (r)
135 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_cgen.chain");
136 : :
137 : 5 : r = bf_chain_new_from_pack(&_cgen->chain, child);
138 [ - + ]: 5 : if (r)
139 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_cgen.chain");
140 : :
141 : 5 : r = bf_rpack_kv_node(node, "handle", &child);
142 [ - + ]: 5 : if (r)
143 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_cgen.handle");
144 : :
145 : 5 : dir_fd = _bf_cgen_get_chain_pindir_fd(_cgen->chain->name);
146 [ - + ]: 5 : if (dir_fd < 0) {
147 [ # # ]: 0 : return bf_err_r(dir_fd, "failed to open chain pin directory for '%s'",
148 : : _cgen->chain->name);
149 : : }
150 : :
151 : 5 : r = bf_handle_new_from_pack(&_cgen->handle, dir_fd, child);
152 [ + - ]: 5 : if (r)
153 : : return r;
154 : :
155 : 5 : *cgen = TAKE_PTR(_cgen);
156 : :
157 : 5 : return 0;
158 : : }
159 : :
160 : 5 : int bf_cgen_new_from_dir_fd(struct bf_cgen **cgen, int dir_fd)
161 : : {
162 : 5 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
163 : 5 : _cleanup_close_ int map_fd = -1;
164 : : _cleanup_free_ void *data = NULL;
165 : : struct bpf_map_info info;
166 : 5 : uint32_t key = 0;
167 : : int r;
168 : :
169 : : assert(cgen);
170 : : assert(dir_fd >= 0);
171 : :
172 : 5 : r = bf_bpf_obj_get(_BF_CTX_PIN_NAME, dir_fd, &map_fd);
173 [ - + ]: 5 : if (r < 0)
174 [ # # ]: 0 : return bf_err_r(r, "failed to open pinned context map");
175 : :
176 : 5 : r = bf_bpf_map_get_info(map_fd, &info);
177 [ - + ]: 5 : if (r)
178 [ # # ]: 0 : return bf_err_r(r, "failed to get context map info");
179 : :
180 [ - + ]: 5 : if (info.value_size == 0)
181 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid serialized context size");
182 : :
183 : 5 : data = malloc(info.value_size);
184 [ + - ]: 5 : if (!data)
185 : : return -ENOMEM;
186 : :
187 : 5 : r = bf_bpf_map_lookup_elem(map_fd, &key, data);
188 [ - + ]: 5 : if (r)
189 [ # # ]: 0 : return bf_err_r(r, "failed to read context from map");
190 : :
191 : 5 : r = bf_rpack_new(&pack, data, info.value_size);
192 [ - + ]: 5 : if (r)
193 [ # # ]: 0 : return bf_err_r(r, "failed to create rpack for bf_cgen");
194 : :
195 : 5 : r = _bf_cgen_new_from_pack(cgen, bf_rpack_root(pack));
196 [ - + ]: 5 : if (r)
197 [ # # ]: 0 : return bf_err_r(r, "failed to deserialize cgen from context map");
198 : :
199 : : return 0;
200 : : }
201 : :
202 : 86 : int bf_cgen_new(struct bf_cgen **cgen, struct bf_chain **chain)
203 : : {
204 : 86 : _free_bf_cgen_ struct bf_cgen *_cgen = NULL;
205 : : int r;
206 : :
207 : : assert(cgen);
208 : : assert(chain);
209 : :
210 : 86 : _cgen = calloc(1, sizeof(*_cgen));
211 [ + - ]: 86 : if (!_cgen)
212 : : return -ENOMEM;
213 : :
214 : 86 : _cgen->chain = TAKE_PTR(*chain);
215 : :
216 : 86 : r = bf_handle_new(&_cgen->handle, _BF_PROG_NAME);
217 [ + - ]: 86 : if (r)
218 : : return r;
219 : :
220 : 86 : *cgen = TAKE_PTR(_cgen);
221 : :
222 : 86 : return 0;
223 : : }
224 : :
225 : 283 : void bf_cgen_free(struct bf_cgen **cgen)
226 : : {
227 : 192 : _cleanup_close_ int pin_fd = -1;
228 : :
229 : : assert(cgen);
230 : :
231 [ + + ]: 283 : if (!*cgen)
232 : : return;
233 : :
234 : : /* Perform a non-recursive removal of the chain's pin directory: if
235 : : * the chain hasn't been pinned (e.g. due to a failure), the pin directory
236 : : * will be empty and will be removed. If the chain is valid and pinned, then
237 : : * the removal of the pin directory will fail, but that's alright. */
238 [ + - + - ]: 91 : if (!bf_ctx_is_transient() && (pin_fd = bf_ctx_get_pindir_fd()) >= 0)
239 : 91 : bf_rmdir_at(pin_fd, (*cgen)->chain->name, false);
240 : :
241 : 91 : bf_handle_free(&(*cgen)->handle);
242 : 91 : bf_chain_free(&(*cgen)->chain);
243 : :
244 : 91 : free(*cgen);
245 : 91 : *cgen = NULL;
246 : : }
247 : :
248 : 117 : int bf_cgen_pack(const struct bf_cgen *cgen, bf_wpack_t *pack)
249 : : {
250 : : assert(cgen);
251 : : assert(pack);
252 : :
253 : 117 : bf_wpack_open_object(pack, "chain");
254 : 117 : bf_chain_pack(cgen->chain, pack);
255 : 117 : bf_wpack_close_object(pack);
256 : :
257 : 117 : bf_wpack_open_object(pack, "handle");
258 : 117 : bf_handle_pack(cgen->handle, pack);
259 : 117 : bf_wpack_close_object(pack);
260 : :
261 [ - + ]: 117 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
262 : : }
263 : :
264 : 17 : void bf_cgen_dump(const struct bf_cgen *cgen, prefix_t *prefix)
265 : : {
266 : : assert(cgen);
267 : : assert(prefix);
268 : :
269 [ + - ]: 17 : DUMP(prefix, "struct bf_cgen at %p", cgen);
270 : :
271 : 17 : bf_dump_prefix_push(prefix);
272 : :
273 : : // Chain
274 [ + - ]: 17 : DUMP(prefix, "chain: struct bf_chain *");
275 : 17 : bf_dump_prefix_push(prefix);
276 : 17 : bf_chain_dump(cgen->chain, bf_dump_prefix_last(prefix));
277 : 17 : bf_dump_prefix_pop(prefix);
278 : :
279 [ + - ]: 17 : DUMP(bf_dump_prefix_last(prefix), "handle: struct bf_handle *");
280 : 17 : bf_dump_prefix_push(prefix);
281 : 17 : bf_handle_dump(cgen->handle, bf_dump_prefix_last(prefix));
282 : 17 : bf_dump_prefix_pop(prefix);
283 : :
284 : 17 : bf_dump_prefix_pop(prefix);
285 : 17 : }
286 : :
287 : 116 : int bf_cgen_get_counter(const struct bf_cgen *cgen,
288 : : enum bf_counter_type counter_idx,
289 : : struct bf_counter *counter)
290 : : {
291 : : assert(cgen);
292 : : assert(counter);
293 : :
294 : : /* There are two more counter than rules. The special counters must
295 : : * be accessed via the specific values, to avoid confusion. */
296 [ + + ]: 116 : enum bf_counter_type rule_count = bf_list_size(&cgen->chain->rules);
297 [ + + ]: 116 : if (counter_idx == BF_COUNTER_POLICY) {
298 : : counter_idx = rule_count;
299 [ + + ]: 76 : } else if (counter_idx == BF_COUNTER_ERRORS) {
300 : 40 : counter_idx = rule_count + 1;
301 [ + - ]: 36 : } else if (counter_idx < 0 || counter_idx >= rule_count) {
302 : : return -EINVAL;
303 : : }
304 : :
305 : 116 : return bf_handle_get_counter(cgen->handle, counter_idx, counter);
306 : : }
307 : :
308 : 74 : int bf_cgen_set(struct bf_cgen *cgen, const struct bf_ns *ns,
309 : : struct bf_hookopts **hookopts)
310 : : {
311 : 74 : _free_bf_program_ struct bf_program *prog = NULL;
312 : 74 : _cleanup_close_ int pindir_fd = -1;
313 : 74 : bool persist = !bf_ctx_is_transient();
314 : : int r;
315 : :
316 : : assert(cgen);
317 : :
318 [ + - ]: 74 : if (persist) {
319 : 74 : pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
320 [ + - ]: 74 : if (pindir_fd < 0)
321 : : return pindir_fd;
322 : : }
323 : :
324 : 74 : r = bf_program_new(&prog, cgen->chain, cgen->handle);
325 [ + - ]: 74 : if (r < 0)
326 : : return r;
327 : :
328 : 74 : r = bf_program_generate(prog);
329 [ + + ]: 74 : if (r < 0)
330 [ + - ]: 3 : return bf_err_r(r, "failed to generate bf_program");
331 : :
332 : 71 : r = bf_program_load(prog);
333 [ - + ]: 71 : if (r < 0)
334 [ # # ]: 0 : return bf_err_r(r, "failed to load the chain");
335 : :
336 [ + + ]: 71 : if (hookopts) {
337 : 40 : r = bf_ns_set(ns, bf_ctx_get_ns());
338 [ - + ]: 40 : if (r)
339 [ # # ]: 0 : return bf_err_r(r, "failed to switch to the client's namespaces");
340 : :
341 : 40 : r = bf_handle_attach(cgen->handle, cgen->chain->hook, hookopts);
342 [ + + ]: 40 : if (r < 0)
343 [ + - ]: 2 : return bf_err_r(r, "failed to load and attach the chain");
344 : :
345 [ - + ]: 38 : if (bf_ns_set(bf_ctx_get_ns(), ns))
346 [ # # ]: 0 : bf_abort("failed to restore previous namespaces, aborting");
347 : : }
348 : :
349 [ + - ]: 69 : if (persist) {
350 : 69 : r = bf_handle_pin(cgen->handle, pindir_fd);
351 [ + - ]: 69 : if (r)
352 : : return r;
353 : :
354 : 69 : r = _bf_cgen_persist(cgen, pindir_fd);
355 [ + - ]: 69 : if (r) {
356 : 0 : bf_handle_unpin(cgen->handle, pindir_fd);
357 [ # # ]: 0 : return bf_err_r(r, "failed to persist cgen for '%s'",
358 : : cgen->chain->name);
359 : : }
360 : : }
361 : :
362 : : return 0;
363 : : }
364 : :
365 : 12 : int bf_cgen_load(struct bf_cgen *cgen)
366 : : {
367 : 12 : _free_bf_program_ struct bf_program *prog = NULL;
368 : 12 : _cleanup_close_ int pindir_fd = -1;
369 : 12 : bool persist = !bf_ctx_is_transient();
370 : : int r;
371 : :
372 : : assert(cgen);
373 : :
374 [ + - ]: 12 : if (persist) {
375 : 12 : pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
376 [ + - ]: 12 : if (pindir_fd < 0)
377 : : return pindir_fd;
378 : : }
379 : :
380 : 12 : r = bf_program_new(&prog, cgen->chain, cgen->handle);
381 [ + - ]: 12 : if (r < 0)
382 : : return r;
383 : :
384 : 12 : r = bf_program_generate(prog);
385 [ - + ]: 12 : if (r < 0)
386 [ # # ]: 0 : return bf_err_r(r, "failed to generate bf_program");
387 : :
388 : 12 : r = bf_program_load(prog);
389 [ - + ]: 12 : if (r < 0)
390 [ # # ]: 0 : return bf_err_r(r, "failed to load the chain");
391 : :
392 [ + - ]: 12 : if (persist) {
393 : 12 : r = bf_handle_pin(cgen->handle, pindir_fd);
394 [ + - ]: 12 : if (r)
395 : : return r;
396 : :
397 : 12 : r = _bf_cgen_persist(cgen, pindir_fd);
398 [ - + ]: 12 : if (r) {
399 : 0 : bf_handle_unpin(cgen->handle, pindir_fd);
400 [ # # ]: 0 : return bf_err_r(r, "failed to persist cgen for '%s'",
401 : : cgen->chain->name);
402 : : }
403 : : }
404 : :
405 [ + - ]: 12 : bf_info("load %s", cgen->chain->name);
406 : 12 : bf_cgen_dump(cgen, EMPTY_PREFIX);
407 : :
408 : 12 : return 0;
409 : : }
410 : :
411 : 10 : int bf_cgen_attach(struct bf_cgen *cgen, const struct bf_ns *ns,
412 : : struct bf_hookopts **hookopts)
413 : : {
414 : 10 : _cleanup_close_ int pindir_fd = -1;
415 : 10 : bool persist = !bf_ctx_is_transient();
416 : : int r;
417 : :
418 : : assert(cgen);
419 : : assert(ns);
420 : : assert(hookopts);
421 : :
422 [ + - ]: 10 : bf_info("attaching %s to %s", cgen->chain->name,
423 : : bf_hook_to_str(cgen->chain->hook));
424 : 10 : bf_hookopts_dump(*hookopts, EMPTY_PREFIX);
425 : :
426 [ + - ]: 10 : if (persist) {
427 : 10 : pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
428 [ + - ]: 10 : if (pindir_fd < 0)
429 : : return pindir_fd;
430 : : }
431 : :
432 : 10 : r = bf_ns_set(ns, bf_ctx_get_ns());
433 [ - + ]: 10 : if (r)
434 [ # # ]: 0 : return bf_err_r(r, "failed to switch to the client's namespaces");
435 : :
436 : 10 : r = bf_handle_attach(cgen->handle, cgen->chain->hook, hookopts);
437 [ + + ]: 10 : if (r < 0)
438 [ + - ]: 2 : return bf_err_r(r, "failed to attach chain '%s'", cgen->chain->name);
439 : :
440 [ - + ]: 8 : if (bf_ns_set(bf_ctx_get_ns(), ns))
441 [ # # ]: 0 : bf_abort("failed to restore previous namespaces, aborting");
442 : :
443 [ + - ]: 8 : if (persist) {
444 : 8 : r = bf_link_pin(cgen->handle->link, pindir_fd);
445 [ - + ]: 8 : if (r) {
446 : 0 : bf_handle_detach(cgen->handle);
447 : 0 : return r;
448 : : }
449 : :
450 : 8 : r = _bf_cgen_persist(cgen, pindir_fd);
451 [ - + ]: 8 : if (r) {
452 : 0 : bf_link_unpin(cgen->handle->link, pindir_fd);
453 : 0 : bf_handle_detach(cgen->handle);
454 [ # # ]: 0 : return bf_err_r(r, "failed to persist cgen for '%s'",
455 : : cgen->chain->name);
456 : : }
457 : : }
458 : :
459 : : return r;
460 : : }
461 : :
462 : : /**
463 : : * @brief Transfer all counters from old handle to new handle.
464 : : *
465 : : * Copies counter values 1:1 for all rule counters plus policy and error
466 : : * counters. The old and new chains must have the same number of rules.
467 : : * Both handles must be loaded.
468 : : *
469 : : * @param old_handle Handle with the source counter map. Can't be NULL.
470 : : * @param new_handle Handle with the destination counter map. Can't be NULL.
471 : : * @param n_rules Number of rules in the chain.
472 : : * @return 0 on success, or a negative errno value on failure.
473 : : */
474 : 8 : static int _bf_cgen_transfer_counters(const struct bf_handle *old_handle,
475 : : struct bf_handle *new_handle,
476 : : size_t n_rules)
477 : : {
478 : : int r;
479 : :
480 : : assert(old_handle);
481 : : assert(new_handle);
482 : :
483 [ + - - + ]: 8 : if (!old_handle->cmap || !new_handle->cmap)
484 [ # # ]: 0 : return bf_err_r(-ENOENT, "missing counter map for counter transfer");
485 : :
486 : : // n_rules entries for rules, +1 for policy, +1 for errors.
487 [ + + ]: 34 : for (uint32_t i = 0; i < n_rules + 2; ++i) {
488 : : struct bf_counter counter;
489 : :
490 : 26 : r = bf_handle_get_counter(old_handle, i, &counter);
491 [ - + ]: 26 : if (r)
492 [ # # ]: 0 : return bf_err_r(r, "failed to read counter %u", i);
493 : :
494 [ + + + - ]: 26 : if (!counter.count && !counter.size)
495 : 21 : continue;
496 : :
497 : 5 : r = bf_map_set_elem(new_handle->cmap, &i, &counter);
498 [ - + ]: 5 : if (r)
499 [ # # ]: 0 : return bf_err_r(r, "failed to write counter %u", i);
500 : : }
501 : :
502 : 8 : return 0;
503 : : }
504 : :
505 : 14 : int bf_cgen_update(struct bf_cgen *cgen, struct bf_chain **new_chain,
506 : : uint32_t flags)
507 : : {
508 : 14 : _free_bf_program_ struct bf_program *new_prog = NULL;
509 : 14 : _free_bf_handle_ struct bf_handle *new_handle = NULL;
510 : 14 : _cleanup_close_ int pindir_fd = -1;
511 : 14 : bool persist = !bf_ctx_is_transient();
512 : : struct bf_handle *old_handle;
513 : : int r;
514 : :
515 : : assert(cgen);
516 : : assert(new_chain);
517 : :
518 [ - + ]: 14 : if (flags & ~BF_FLAGS_MASK(_BF_CGEN_UPDATE_MAX))
519 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown update flags: 0x%x", flags);
520 : :
521 : 14 : old_handle = cgen->handle;
522 : :
523 [ + - ]: 14 : if (persist) {
524 : 14 : pindir_fd = _bf_cgen_get_chain_pindir_fd((*new_chain)->name);
525 [ + - ]: 14 : if (pindir_fd < 0)
526 : : return pindir_fd;
527 : : }
528 : :
529 : 14 : r = bf_handle_new(&new_handle, _BF_PROG_NAME);
530 [ + - ]: 14 : if (r)
531 : : return r;
532 : :
533 : 14 : r = bf_program_new(&new_prog, *new_chain, new_handle);
534 [ - + ]: 14 : if (r < 0)
535 [ # # ]: 0 : return bf_err_r(r, "failed to create a new bf_program");
536 : :
537 : 14 : r = bf_program_generate(new_prog);
538 [ - + ]: 14 : if (r < 0) {
539 [ # # ]: 0 : return bf_err_r(r,
540 : : "failed to generate the bytecode for a new bf_program");
541 : : }
542 : :
543 : 14 : r = bf_program_load(new_prog);
544 [ - + ]: 14 : if (r)
545 [ # # ]: 0 : return bf_err_r(r, "failed to load new program");
546 : :
547 [ + + ]: 14 : if (flags & BF_FLAG(BF_CGEN_UPDATE_PRESERVE_COUNTERS)) {
548 [ - + ]: 8 : if (bf_list_size(&cgen->chain->rules) !=
549 [ - + ]: 8 : bf_list_size(&(*new_chain)->rules)) {
550 [ # # ]: 0 : return bf_err_r(-EINVAL,
551 : : "rule count mismatch for counter transfer");
552 : : }
553 : :
554 : 8 : r = _bf_cgen_transfer_counters(old_handle, new_handle,
555 : : bf_list_size(&(*new_chain)->rules));
556 [ - + ]: 8 : if (r)
557 [ # # ]: 0 : return bf_err_r(r, "failed to transfer counters");
558 : : }
559 : :
560 [ + - ]: 14 : if (persist)
561 : 14 : bf_handle_unpin(old_handle, pindir_fd);
562 : :
563 [ + + ]: 14 : if (old_handle->link) {
564 : 12 : r = bf_link_update(old_handle->link, new_handle->prog_fd);
565 [ - + ]: 12 : if (r) {
566 [ # # ]: 0 : bf_err_r(r, "failed to update bf_link object with new program");
567 [ # # # # ]: 0 : if (persist && bf_handle_pin(old_handle, pindir_fd) < 0)
568 [ # # ]: 0 : bf_err("failed to repin old handle, ignoring");
569 : 0 : return r;
570 : : }
571 : :
572 : : // We updated the old link, we need to store it in the new handle
573 : 12 : bf_swap(new_handle->link, old_handle->link);
574 : : }
575 : :
576 : 14 : bf_swap(cgen->handle, new_handle);
577 : :
578 [ + - ]: 14 : if (persist) {
579 : 14 : r = bf_handle_pin(cgen->handle, pindir_fd);
580 [ - + ]: 14 : if (r)
581 [ # # ]: 0 : return bf_err_r(r, "failed to pin new handle");
582 : :
583 : 14 : r = _bf_cgen_persist(cgen, pindir_fd);
584 [ - + ]: 14 : if (r) {
585 : 0 : bf_handle_unpin(cgen->handle, pindir_fd);
586 [ # # ]: 0 : return bf_err_r(r, "failed to persist cgen for '%s'",
587 : : cgen->chain->name);
588 : : }
589 : : }
590 : :
591 : 14 : bf_chain_free(&cgen->chain);
592 : 14 : cgen->chain = TAKE_PTR(*new_chain);
593 : :
594 [ + - ]: 14 : if (persist) {
595 : 14 : r = _bf_cgen_persist(cgen, pindir_fd);
596 [ + - ]: 14 : if (r) {
597 : 0 : bf_handle_unpin(cgen->handle, pindir_fd);
598 [ # # ]: 0 : return bf_err_r(r, "failed to persist cgen for '%s'",
599 : : cgen->chain->name);
600 : : }
601 : : }
602 : :
603 : : return 0;
604 : : }
605 : :
606 : 0 : void bf_cgen_detach(struct bf_cgen *cgen)
607 : : {
608 : : assert(cgen);
609 : :
610 : 0 : bf_handle_detach(cgen->handle);
611 : 0 : }
612 : :
613 : 60 : void bf_cgen_unload(struct bf_cgen *cgen)
614 : : {
615 : 60 : _cleanup_close_ int chain_fd = -1;
616 : :
617 : : assert(cgen);
618 : :
619 : 60 : chain_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
620 [ - + ]: 60 : if (chain_fd < 0) {
621 [ # # ]: 0 : bf_err_r(chain_fd, "failed to open pin directory for '%s'",
622 : : cgen->chain->name);
623 : : return;
624 : : }
625 : :
626 : : // The chain's pin directory will be removed in bf_cgen_free()
627 : 60 : unlinkat(chain_fd, _BF_CTX_PIN_NAME, 0);
628 : 60 : bf_handle_unpin(cgen->handle, chain_fd);
629 : 60 : bf_handle_unload(cgen->handle);
630 : : }
631 : :
632 : 40 : int bf_cgen_get_counters(const struct bf_cgen *cgen, bf_list *counters)
633 : : {
634 : : bf_list _counters;
635 : : int r;
636 : :
637 : : assert(cgen);
638 : : assert(counters);
639 : :
640 : 40 : _counters = bf_list_default_from(*counters);
641 : :
642 : : /* Iterate over all the rules, then the policy counter (size(rules)) and
643 : : * the errors counters (sizeof(rules) + 1)*/
644 [ + + ]: 156 : for (size_t i = 0; i < bf_list_size(&cgen->chain->rules) + 2; ++i) {
645 : 0 : _free_bf_counter_ struct bf_counter *counter = NULL;
646 : 116 : ssize_t idx = (ssize_t)i;
647 : :
648 [ + + ]: 116 : if (i == bf_list_size(&cgen->chain->rules))
649 : : idx = BF_COUNTER_POLICY;
650 [ + + ]: 76 : else if (i == bf_list_size(&cgen->chain->rules) + 1)
651 : : idx = BF_COUNTER_ERRORS;
652 : :
653 : 116 : r = bf_counter_new(&counter, 0, 0);
654 [ + - ]: 116 : if (r)
655 : : return r;
656 : :
657 : 116 : r = bf_cgen_get_counter(cgen, idx, counter);
658 [ + - ]: 116 : if (r)
659 : : return r;
660 : :
661 : 116 : r = bf_list_add_tail(&_counters, counter);
662 [ + - ]: 116 : if (r)
663 : : return r;
664 : :
665 : 116 : TAKE_PTR(counter);
666 : : }
667 : :
668 : 40 : *counters = bf_list_move(_counters);
669 : :
670 : 40 : return 0;
671 : : }
|