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