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