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