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