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 <stdlib.h>
12 : : #include <string.h>
13 : : #include <sys/types.h>
14 : :
15 : : #include <bpfilter/chain.h>
16 : : #include <bpfilter/counter.h>
17 : : #include <bpfilter/dump.h>
18 : : #include <bpfilter/front.h>
19 : : #include <bpfilter/helper.h>
20 : : #include <bpfilter/hook.h>
21 : : #include <bpfilter/io.h>
22 : : #include <bpfilter/list.h>
23 : : #include <bpfilter/logger.h>
24 : : #include <bpfilter/ns.h>
25 : : #include <bpfilter/pack.h>
26 : :
27 : : #include "cgen/dump.h"
28 : : #include "cgen/prog/link.h"
29 : : #include "cgen/program.h"
30 : : #include "ctx.h"
31 : : #include "opts.h"
32 : :
33 : 114 : static int _bf_cgen_get_chain_pindir_fd(const char *name)
34 : : {
35 : 114 : _cleanup_close_ int bf_fd = -1;
36 : 114 : _cleanup_close_ int chain_fd = -1;
37 : :
38 : : assert(name);
39 : :
40 : 114 : bf_fd = bf_ctx_get_pindir_fd();
41 [ + - ]: 114 : if (bf_fd < 0)
42 : : return bf_fd;
43 : :
44 : 114 : chain_fd = bf_opendir_at(bf_fd, name, true);
45 [ - + ]: 114 : if (chain_fd < 0)
46 : : return chain_fd;
47 : :
48 : 114 : return TAKE_FD(chain_fd);
49 : : }
50 : :
51 : 60 : int bf_cgen_new(struct bf_cgen **cgen, enum bf_front front,
52 : : struct bf_chain **chain)
53 : : {
54 : : assert(cgen);
55 : : assert(chain);
56 : :
57 : 60 : *cgen = malloc(sizeof(struct bf_cgen));
58 [ + - ]: 60 : if (!*cgen)
59 : : return -ENOMEM;
60 : :
61 : 60 : (*cgen)->front = front;
62 : 60 : (*cgen)->program = NULL;
63 : : (*cgen)->chain = NULL;
64 : 60 : (*cgen)->chain = TAKE_PTR(*chain);
65 : :
66 : 60 : return 0;
67 : : }
68 : :
69 : 3 : int bf_cgen_new_from_pack(struct bf_cgen **cgen, bf_rpack_node_t node)
70 : : {
71 : 3 : _free_bf_cgen_ struct bf_cgen *_cgen = NULL;
72 : : bf_rpack_node_t child;
73 : : int r;
74 : :
75 : : assert(cgen);
76 : :
77 : 3 : _cgen = malloc(sizeof(*_cgen));
78 [ + - ]: 3 : if (!_cgen)
79 : : return -ENOMEM;
80 : :
81 : 3 : _cgen->program = NULL;
82 : :
83 [ - + - + : 3 : r = bf_rpack_kv_enum(node, "front", &_cgen->front, 0, _BF_FRONT_MAX);
- - ]
84 : : if (r)
85 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_cgen.front");
86 : :
87 : 3 : r = bf_rpack_kv_obj(node, "chain", &child);
88 [ - + ]: 3 : if (r)
89 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_cgen.chain");
90 : :
91 : 3 : r = bf_chain_new_from_pack(&_cgen->chain, child);
92 [ - + ]: 3 : if (r)
93 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_cgen.chain");
94 : :
95 : 3 : r = bf_rpack_kv_node(node, "program", &child);
96 [ - + ]: 3 : if (r)
97 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_cgen.program");
98 [ + - ]: 3 : if (!bf_rpack_is_nil(child)) {
99 : 3 : _cleanup_close_ int dir_fd = -1;
100 : :
101 [ - + ]: 3 : if ((dir_fd = _bf_cgen_get_chain_pindir_fd(_cgen->chain->name)) < 0) {
102 [ # # ]: 0 : return bf_err_r(dir_fd,
103 : : "failed to open chain pin directory for '%s'",
104 : : _cgen->chain->name);
105 : : }
106 : :
107 : 3 : r = bf_program_new_from_pack(&_cgen->program, _cgen->chain, dir_fd,
108 : : child);
109 [ + - ]: 3 : if (r)
110 : : return r;
111 : : }
112 : :
113 : 3 : *cgen = TAKE_PTR(_cgen);
114 : :
115 : 3 : return 0;
116 : : }
117 : :
118 : 127 : void bf_cgen_free(struct bf_cgen **cgen)
119 : : {
120 : 64 : _cleanup_close_ int pin_fd = -1;
121 : :
122 : : assert(cgen);
123 : :
124 [ + + ]: 127 : if (!*cgen)
125 : : return;
126 : :
127 : : /* Perform a non-recursive removal of the chain's pin directory: if
128 : : * the chain hasn't been pinned (e.g. due to a failure), the pin directory
129 : : * will be empty and will be removed. If the chain is valid and pinned, then
130 : : * the removal of the pin directory will fail, but that's alright. */
131 [ + - + - ]: 63 : if (bf_opts_persist() && (pin_fd = bf_ctx_get_pindir_fd()) >= 0)
132 : 63 : bf_rmdir_at(pin_fd, (*cgen)->chain->name, false);
133 : :
134 : 63 : bf_program_free(&(*cgen)->program);
135 : 63 : bf_chain_free(&(*cgen)->chain);
136 : :
137 : 63 : free(*cgen);
138 : 63 : *cgen = NULL;
139 : : }
140 : :
141 : 169 : int bf_cgen_pack(const struct bf_cgen *cgen, bf_wpack_t *pack)
142 : : {
143 : : assert(cgen);
144 : : assert(pack);
145 : :
146 : 169 : bf_wpack_kv_enum(pack, "front", cgen->front);
147 : :
148 : 169 : bf_wpack_open_object(pack, "chain");
149 : 169 : bf_chain_pack(cgen->chain, pack);
150 : 169 : bf_wpack_close_object(pack);
151 : :
152 [ + - ]: 169 : if (cgen->program) {
153 : 169 : bf_wpack_open_object(pack, "program");
154 : 169 : bf_program_pack(cgen->program, pack);
155 : 169 : bf_wpack_close_object(pack);
156 : : } else {
157 : : bf_wpack_kv_nil(pack, "program");
158 : : }
159 : :
160 [ - + ]: 169 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
161 : : }
162 : :
163 : 15 : void bf_cgen_dump(const struct bf_cgen *cgen, prefix_t *prefix)
164 : : {
165 : : assert(cgen);
166 : : assert(prefix);
167 : :
168 [ + - ]: 15 : DUMP(prefix, "struct bf_cgen at %p", cgen);
169 : :
170 : 15 : bf_dump_prefix_push(prefix);
171 [ + - ]: 15 : DUMP(prefix, "front: %s", bf_front_to_str(cgen->front));
172 : :
173 : : // Chain
174 [ + - ]: 15 : DUMP(prefix, "chain: struct bf_chain *");
175 : 15 : bf_dump_prefix_push(prefix);
176 : 15 : bf_chain_dump(cgen->chain, bf_dump_prefix_last(prefix));
177 : 15 : bf_dump_prefix_pop(prefix);
178 : :
179 : : // Programs
180 [ + + ]: 15 : if (cgen->program) {
181 [ + - ]: 3 : DUMP(bf_dump_prefix_last(prefix), "program: struct bf_program *");
182 : 3 : bf_dump_prefix_push(prefix);
183 : 3 : bf_program_dump(cgen->program, bf_dump_prefix_last(prefix));
184 : 3 : bf_dump_prefix_pop(prefix);
185 : : } else {
186 [ + - ]: 12 : DUMP(bf_dump_prefix_last(prefix), "program: (struct bf_program *)NULL");
187 : : }
188 : :
189 : 15 : bf_dump_prefix_pop(prefix);
190 : 15 : }
191 : :
192 : 32 : int bf_cgen_get_counter(const struct bf_cgen *cgen,
193 : : enum bf_counter_type counter_idx,
194 : : struct bf_counter *counter)
195 : : {
196 : : assert(cgen);
197 : : assert(counter);
198 : :
199 : : /* There are two more counter than rules. The special counters must
200 : : * be accessed via the specific values, to avoid confusion. */
201 [ + + ]: 32 : enum bf_counter_type rule_count = bf_list_size(&cgen->chain->rules);
202 [ + + ]: 32 : if (counter_idx == BF_COUNTER_POLICY) {
203 : : counter_idx = rule_count;
204 [ + + ]: 19 : } else if (counter_idx == BF_COUNTER_ERRORS) {
205 : 13 : counter_idx = rule_count + 1;
206 [ + - ]: 6 : } else if (counter_idx < 0 || counter_idx >= rule_count) {
207 : : return -EINVAL;
208 : : }
209 : :
210 : 32 : return bf_program_get_counter(cgen->program, counter_idx, counter);
211 : : }
212 : :
213 : 48 : int bf_cgen_set(struct bf_cgen *cgen, const struct bf_ns *ns,
214 : : struct bf_hookopts **hookopts)
215 : : {
216 : 48 : _free_bf_program_ struct bf_program *prog = NULL;
217 : 48 : _cleanup_close_ int pindir_fd = -1;
218 : : int r;
219 : :
220 : : assert(cgen);
221 : :
222 [ + - ]: 48 : if (bf_opts_persist()) {
223 : 48 : pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
224 [ + - ]: 48 : if (pindir_fd < 0)
225 : : return pindir_fd;
226 : : }
227 : :
228 : 48 : r = bf_program_new(&prog, cgen->chain);
229 [ + - ]: 48 : if (r < 0)
230 : : return r;
231 : :
232 : 48 : r = bf_program_generate(prog);
233 [ - + ]: 48 : if (r < 0)
234 [ # # ]: 0 : return bf_err_r(r, "failed to generate bf_program");
235 : :
236 : 48 : r = bf_program_load(prog);
237 [ - + ]: 48 : if (r < 0)
238 [ # # ]: 0 : return bf_err_r(r, "failed to load the chain");
239 : :
240 [ + + ]: 48 : if (hookopts) {
241 : 19 : r = bf_ns_set(ns, bf_ctx_get_ns());
242 [ - + ]: 19 : if (r)
243 [ # # ]: 0 : return bf_err_r(r, "failed to switch to the client's namespaces");
244 : :
245 : 19 : r = bf_program_attach(prog, hookopts);
246 [ + + ]: 19 : if (r < 0)
247 [ + - ]: 2 : return bf_err_r(r, "failed to load and attach the chain");
248 : :
249 [ - + ]: 17 : if (bf_ns_set(bf_ctx_get_ns(), ns))
250 [ # # ]: 0 : bf_abort("failed to restore previous namespaces, aborting");
251 : : }
252 : :
253 [ + - ]: 46 : if (bf_opts_persist()) {
254 : 46 : r = bf_program_pin(prog, pindir_fd);
255 [ + - ]: 46 : if (r)
256 : : return r;
257 : : }
258 : :
259 : 46 : cgen->program = TAKE_PTR(prog);
260 : :
261 : 46 : return r;
262 : : }
263 : :
264 : 12 : int bf_cgen_load(struct bf_cgen *cgen)
265 : : {
266 : 12 : _free_bf_program_ struct bf_program *prog = NULL;
267 : 12 : _cleanup_close_ int pindir_fd = -1;
268 : : int r;
269 : :
270 : : assert(cgen);
271 : :
272 [ + - ]: 12 : if (bf_opts_persist()) {
273 : 12 : pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
274 [ + - ]: 12 : if (pindir_fd < 0)
275 : : return pindir_fd;
276 : : }
277 : :
278 : 12 : r = bf_program_new(&prog, cgen->chain);
279 [ + - ]: 12 : if (r < 0)
280 : : return r;
281 : :
282 : 12 : r = bf_program_generate(prog);
283 [ - + ]: 12 : if (r < 0)
284 [ # # ]: 0 : return bf_err_r(r, "failed to generate bf_program");
285 : :
286 : 12 : r = bf_program_load(prog);
287 [ - + ]: 12 : if (r < 0)
288 [ # # ]: 0 : return bf_err_r(r, "failed to load the chain");
289 : :
290 [ + - ]: 12 : if (bf_opts_persist()) {
291 : 12 : r = bf_program_pin(prog, pindir_fd);
292 [ + - ]: 12 : if (r)
293 : : return r;
294 : : }
295 : :
296 [ + - ]: 12 : bf_info("load %s", cgen->chain->name);
297 : 12 : bf_cgen_dump(cgen, EMPTY_PREFIX);
298 : :
299 : 12 : cgen->program = TAKE_PTR(prog);
300 : :
301 : 12 : return r;
302 : : }
303 : :
304 : 9 : int bf_cgen_attach(struct bf_cgen *cgen, const struct bf_ns *ns,
305 : : struct bf_hookopts **hookopts)
306 : : {
307 : 9 : _cleanup_close_ int pindir_fd = -1;
308 : : int r;
309 : :
310 : : assert(cgen);
311 : : assert(ns);
312 : : assert(hookopts);
313 : :
314 [ + - ]: 9 : bf_info("attaching %s to %s", cgen->chain->name,
315 : : bf_hook_to_str(cgen->chain->hook));
316 : 9 : bf_hookopts_dump(*hookopts, EMPTY_PREFIX);
317 : :
318 [ + - ]: 9 : if (bf_opts_persist()) {
319 : 9 : pindir_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
320 [ + - ]: 9 : if (pindir_fd < 0)
321 : : return pindir_fd;
322 : : }
323 : :
324 : 9 : r = bf_ns_set(ns, bf_ctx_get_ns());
325 [ - + ]: 9 : if (r)
326 [ # # ]: 0 : return bf_err_r(r, "failed to switch to the client's namespaces");
327 : :
328 : 9 : r = bf_program_attach(cgen->program, hookopts);
329 [ + + ]: 9 : if (r < 0)
330 [ + - ]: 2 : return bf_err_r(r, "failed to attach chain '%s'", cgen->chain->name);
331 : :
332 [ - + ]: 7 : if (bf_ns_set(bf_ctx_get_ns(), ns))
333 [ # # ]: 0 : bf_abort("failed to restore previous namespaces, aborting");
334 : :
335 [ + - ]: 7 : if (bf_opts_persist()) {
336 : 7 : r = bf_link_pin(cgen->program->link, pindir_fd);
337 [ - + ]: 7 : if (r) {
338 : 0 : bf_program_detach(cgen->program);
339 : 0 : return r;
340 : : }
341 : : }
342 : :
343 : : return r;
344 : : }
345 : :
346 : 4 : int bf_cgen_update(struct bf_cgen *cgen, struct bf_chain **new_chain)
347 : : {
348 : 4 : _free_bf_program_ struct bf_program *new_prog = NULL;
349 : 4 : _cleanup_close_ int pindir_fd = -1;
350 : : struct bf_program *old_prog;
351 : : int r;
352 : :
353 : : assert(cgen);
354 : : assert(new_chain);
355 : :
356 : 4 : old_prog = cgen->program;
357 : :
358 [ + - ]: 4 : if (bf_opts_persist()) {
359 : 4 : pindir_fd = _bf_cgen_get_chain_pindir_fd((*new_chain)->name);
360 [ + - ]: 4 : if (pindir_fd < 0)
361 : : return pindir_fd;
362 : : }
363 : :
364 : 4 : r = bf_program_new(&new_prog, *new_chain);
365 [ - + ]: 4 : if (r < 0)
366 [ # # ]: 0 : return bf_err_r(r, "failed to create a new bf_program");
367 : :
368 : 4 : r = bf_program_generate(new_prog);
369 [ - + ]: 4 : if (r < 0) {
370 [ # # ]: 0 : return bf_err_r(r,
371 : : "failed to generate the bytecode for a new bf_program");
372 : : }
373 : :
374 : 4 : r = bf_program_load(new_prog);
375 [ - + ]: 4 : if (r)
376 [ # # ]: 0 : return bf_err_r(r, "failed to load new program");
377 : :
378 [ + - ]: 4 : if (bf_opts_persist())
379 : 4 : bf_program_unpin(old_prog, pindir_fd);
380 : :
381 : 4 : r = bf_link_update(old_prog->link, cgen->chain->hook,
382 : 4 : new_prog->runtime.prog_fd);
383 [ - + ]: 4 : if (r) {
384 [ # # ]: 0 : bf_err_r(r, "failed to update bf_link object with new program");
385 [ # # # # ]: 0 : if (bf_opts_persist() && bf_program_pin(old_prog, pindir_fd) < 0)
386 [ # # ]: 0 : bf_err("failed to repin old program, ignoring");
387 : 0 : return r;
388 : : }
389 : :
390 : : // We updated the old link, we need to store it in the new program
391 : 4 : bf_swap(new_prog->link, old_prog->link);
392 : :
393 [ + - ]: 4 : if (bf_opts_persist()) {
394 : 4 : r = bf_program_pin(new_prog, pindir_fd);
395 [ - + ]: 4 : if (r)
396 [ # # ]: 0 : bf_warn_r(r, "failed to pin new prog, ignoring");
397 : : }
398 : :
399 : 4 : bf_swap(cgen->program, new_prog);
400 : :
401 : 4 : bf_chain_free(&cgen->chain);
402 : 4 : cgen->chain = TAKE_PTR(*new_chain);
403 : :
404 : 4 : return 0;
405 : : }
406 : :
407 : 0 : void bf_cgen_detach(struct bf_cgen *cgen)
408 : : {
409 : : assert(cgen);
410 : :
411 : 0 : bf_program_detach(cgen->program);
412 : 0 : }
413 : :
414 : 38 : void bf_cgen_unload(struct bf_cgen *cgen)
415 : : {
416 : 38 : _cleanup_close_ int chain_fd = -1;
417 : :
418 : : assert(cgen);
419 : :
420 : 38 : chain_fd = _bf_cgen_get_chain_pindir_fd(cgen->chain->name);
421 [ - + ]: 38 : if (chain_fd < 0) {
422 [ # # ]: 0 : bf_err_r(chain_fd, "failed to open pin directory for '%s'",
423 : : cgen->chain->name);
424 : : return;
425 : : }
426 : :
427 : : // The chain's pin directory will be removed in bf_cgen_free()
428 : 38 : bf_program_unpin(cgen->program, chain_fd);
429 : 38 : bf_program_unload(cgen->program);
430 : : }
431 : :
432 : 13 : int bf_cgen_get_counters(const struct bf_cgen *cgen, bf_list *counters)
433 : : {
434 : 13 : bf_list _counters = bf_list_default_from(*counters);
435 : : int r;
436 : :
437 : : assert(cgen);
438 : : assert(counters);
439 : :
440 : : /* Iterate over all the rules, then the policy counter (size(rules)) and
441 : : * the errors counters (sizeof(rules) + 1)*/
442 [ + + ]: 45 : for (size_t i = 0; i < bf_list_size(&cgen->chain->rules) + 2; ++i) {
443 : 0 : _free_bf_counter_ struct bf_counter *counter = NULL;
444 : 32 : ssize_t idx = (ssize_t)i;
445 : :
446 [ + + ]: 32 : if (i == bf_list_size(&cgen->chain->rules))
447 : : idx = BF_COUNTER_POLICY;
448 [ + + ]: 19 : else if (i == bf_list_size(&cgen->chain->rules) + 1)
449 : : idx = BF_COUNTER_ERRORS;
450 : :
451 : 32 : r = bf_counter_new(&counter, 0, 0);
452 [ + - ]: 32 : if (r)
453 : : return r;
454 : :
455 : 32 : r = bf_cgen_get_counter(cgen, idx, counter);
456 [ + - ]: 32 : if (r)
457 : : return r;
458 : :
459 : 32 : r = bf_list_add_tail(&_counters, counter);
460 [ + - ]: 32 : if (r)
461 : : return r;
462 : :
463 : 32 : TAKE_PTR(counter);
464 : : }
465 : :
466 : 13 : *counters = bf_list_move(_counters);
467 : :
468 : 13 : return 0;
469 : : }
|