Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include <errno.h>
7 :
8 : #include <bpfilter/chain.h>
9 : #include <bpfilter/counter.h>
10 : #include <bpfilter/front.h>
11 : #include <bpfilter/helper.h>
12 : #include <bpfilter/hook.h>
13 : #include <bpfilter/io.h>
14 : #include <bpfilter/list.h>
15 : #include <bpfilter/logger.h>
16 : #include <bpfilter/pack.h>
17 : #include <bpfilter/request.h>
18 : #include <bpfilter/response.h>
19 :
20 : #include "cgen/cgen.h"
21 : #include "cgen/prog/link.h"
22 : #include "cgen/prog/map.h"
23 : #include "cgen/program.h"
24 : #include "ctx.h"
25 : #include "xlate/front.h"
26 :
27 : static int _bf_cli_setup(void);
28 : static int _bf_cli_teardown(void);
29 : static int _bf_cli_request_handler(const struct bf_request *request,
30 : struct bf_response **response);
31 : static int _bf_cli_pack(bf_wpack_t *pack);
32 : static int _bf_cli_unpack(bf_rpack_node_t node);
33 :
34 : const struct bf_front_ops cli_front = {
35 : .setup = _bf_cli_setup,
36 : .teardown = _bf_cli_teardown,
37 : .request_handler = _bf_cli_request_handler,
38 : .pack = _bf_cli_pack,
39 : .unpack = _bf_cli_unpack,
40 : };
41 :
42 0 : static int _bf_cli_setup(void)
43 : {
44 0 : return 0;
45 : }
46 :
47 0 : static int _bf_cli_teardown(void)
48 : {
49 0 : return 0;
50 : }
51 :
52 0 : int _bf_cli_ruleset_flush(const struct bf_request *request,
53 : struct bf_response **response)
54 : {
55 : UNUSED(request);
56 : UNUSED(response);
57 :
58 0 : bf_ctx_flush(BF_FRONT_CLI);
59 :
60 0 : return 0;
61 : }
62 :
63 0 : static int _bf_cli_ruleset_get(const struct bf_request *request,
64 : struct bf_response **response)
65 : {
66 0 : _clean_bf_list_ bf_list cgens = bf_list_default(NULL, NULL);
67 0 : _clean_bf_list_ bf_list chains = bf_list_default(NULL, bf_chain_pack);
68 0 : _clean_bf_list_ bf_list hookopts = bf_list_default(NULL, bf_hookopts_pack);
69 0 : _clean_bf_list_ bf_list counters =
70 : bf_list_default(bf_list_free, bf_list_pack);
71 0 : _free_bf_wpack_ bf_wpack_t *pack = NULL;
72 : int r;
73 :
74 : UNUSED(request);
75 :
76 0 : r = bf_wpack_new(&pack);
77 0 : if (r)
78 : return r;
79 :
80 0 : r = bf_ctx_get_cgens_for_front(&cgens, BF_FRONT_CLI);
81 0 : if (r < 0)
82 0 : return bf_err_r(r, "failed to get cgen list");
83 :
84 0 : bf_list_foreach (&cgens, cgen_node) {
85 0 : struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
86 0 : _free_bf_list_ bf_list *cgen_counters = NULL;
87 :
88 0 : r = bf_list_add_tail(&chains, cgen->chain);
89 0 : if (r)
90 0 : return bf_err_r(r, "failed to add chain to list");
91 :
92 0 : r = bf_list_add_tail(&hookopts, cgen->program->link->hookopts);
93 0 : if (r)
94 0 : return bf_err_r(r, "failed to add hookopts to list");
95 :
96 0 : r = bf_list_new(&cgen_counters,
97 0 : &bf_list_ops_default(bf_counter_free, bf_counter_pack));
98 0 : if (r)
99 : return r;
100 :
101 0 : r = bf_cgen_get_counters(cgen, cgen_counters);
102 0 : if (r)
103 : return r;
104 :
105 0 : r = bf_list_add_tail(&counters, cgen_counters);
106 0 : if (r)
107 : return r;
108 :
109 0 : TAKE_PTR(cgen_counters);
110 : }
111 :
112 0 : bf_wpack_open_object(pack, "ruleset");
113 0 : bf_wpack_kv_list(pack, "chains", &chains);
114 0 : bf_wpack_kv_list(pack, "hookopts", &hookopts);
115 0 : bf_wpack_kv_list(pack, "counters", &counters);
116 0 : bf_wpack_close_object(pack);
117 :
118 0 : return bf_response_new_from_pack(response, pack);
119 : }
120 :
121 0 : int _bf_cli_ruleset_set(const struct bf_request *request,
122 : struct bf_response **response)
123 : {
124 0 : _clean_bf_list_ bf_list cgens = bf_list_default(NULL, NULL);
125 0 : _free_bf_rpack_ bf_rpack_t *pack;
126 : bf_rpack_node_t child, node;
127 : int r;
128 :
129 : UNUSED(response);
130 :
131 0 : bf_assert(request);
132 :
133 0 : bf_ctx_flush(BF_FRONT_CLI);
134 :
135 0 : r = bf_rpack_new(&pack, bf_request_data(request),
136 : bf_request_data_len(request));
137 0 : if (r)
138 : return r;
139 :
140 0 : r = bf_rpack_kv_array(bf_rpack_root(pack), "ruleset", &child);
141 0 : if (r)
142 : return r;
143 0 : bf_rpack_array_foreach (child, node) {
144 0 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
145 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
146 0 : _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
147 : bf_rpack_node_t child;
148 :
149 0 : r = bf_rpack_kv_obj(node, "chain", &child);
150 0 : if (r)
151 0 : goto err_load;
152 :
153 0 : r = bf_chain_new_from_pack(&chain, child);
154 0 : if (r)
155 0 : goto err_load;
156 :
157 0 : r = bf_rpack_kv_node(node, "hookopts", &child);
158 0 : if (r)
159 0 : goto err_load;
160 0 : if (!bf_rpack_is_nil(child)) {
161 0 : r = bf_hookopts_new_from_pack(&hookopts, child);
162 0 : if (r)
163 0 : goto err_load;
164 : }
165 :
166 0 : r = bf_cgen_new(&cgen, BF_FRONT_CLI, &chain);
167 0 : if (r)
168 0 : goto err_load;
169 :
170 0 : r = bf_cgen_set(cgen, bf_request_ns(request),
171 0 : hookopts ? &hookopts : NULL);
172 0 : if (r) {
173 0 : bf_err_r(r, "failed to set chain '%s'", cgen->chain->name);
174 0 : goto err_load;
175 : }
176 :
177 0 : r = bf_ctx_set_cgen(cgen);
178 0 : if (r) {
179 : /* The codegen is loaded already, if the daemon runs in persistent
180 : * mode, cleaning the codegen won't be sufficient to discard the
181 : * chain, it must be unpinned. */
182 0 : bf_cgen_unload(cgen);
183 0 : goto err_load;
184 : }
185 :
186 0 : TAKE_PTR(cgen);
187 : }
188 :
189 : return 0;
190 :
191 : err_load:
192 0 : bf_ctx_flush(BF_FRONT_CLI);
193 0 : return r;
194 : }
195 :
196 0 : int _bf_cli_chain_set(const struct bf_request *request,
197 : struct bf_response **response)
198 : {
199 : struct bf_cgen *old_cgen;
200 0 : _free_bf_cgen_ struct bf_cgen *new_cgen = NULL;
201 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
202 0 : _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
203 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
204 : bf_rpack_node_t root, child;
205 : int r;
206 :
207 : UNUSED(response);
208 :
209 0 : bf_assert(request);
210 :
211 0 : r = bf_rpack_new(&pack, bf_request_data(request),
212 : bf_request_data_len(request));
213 0 : if (r)
214 : return r;
215 :
216 0 : root = bf_rpack_root(pack);
217 :
218 0 : r = bf_rpack_kv_obj(root, "chain", &child);
219 0 : if (r)
220 : return r;
221 0 : r = bf_chain_new_from_pack(&chain, child);
222 0 : if (r)
223 : return r;
224 :
225 0 : r = bf_rpack_kv_node(root, "hookopts", &child);
226 0 : if (r)
227 : return r;
228 0 : if (!bf_rpack_is_nil(child)) {
229 0 : r = bf_hookopts_new_from_pack(&hookopts, child);
230 0 : if (r)
231 : return r;
232 : }
233 :
234 0 : r = bf_cgen_new(&new_cgen, BF_FRONT_CLI, &chain);
235 0 : if (r)
236 : return r;
237 :
238 0 : old_cgen = bf_ctx_get_cgen(new_cgen->chain->name);
239 0 : if (old_cgen) {
240 : /* bf_ctx_delete_cgen() can only fail if the codegen is not found,
241 : * but we know this codegen exist. */
242 0 : (void)bf_ctx_delete_cgen(old_cgen, true);
243 : }
244 :
245 0 : r = bf_cgen_set(new_cgen, bf_request_ns(request),
246 0 : hookopts ? &hookopts : NULL);
247 0 : if (r)
248 : return r;
249 :
250 0 : r = bf_ctx_set_cgen(new_cgen);
251 0 : if (r) {
252 0 : bf_cgen_unload(new_cgen);
253 0 : return r;
254 : }
255 :
256 0 : TAKE_PTR(new_cgen);
257 :
258 0 : return r;
259 : }
260 :
261 0 : static int _bf_cli_chain_get(const struct bf_request *request,
262 : struct bf_response **response)
263 : {
264 0 : _clean_bf_list_ bf_list counters =
265 : bf_list_default(bf_counter_free, bf_counter_pack);
266 : struct bf_cgen *cgen;
267 0 : _cleanup_free_ char *name = NULL;
268 0 : _free_bf_wpack_ bf_wpack_t *wpack = NULL;
269 0 : _free_bf_rpack_ bf_rpack_t *rpack = NULL;
270 : int r;
271 :
272 0 : r = bf_rpack_new(&rpack, bf_request_data(request),
273 : bf_request_data_len(request));
274 0 : if (r)
275 : return r;
276 :
277 0 : r = bf_rpack_kv_str(bf_rpack_root(rpack), "name", &name);
278 0 : if (r)
279 : return r;
280 :
281 0 : cgen = bf_ctx_get_cgen(name);
282 0 : if (!cgen)
283 0 : return bf_err_r(-ENOENT, "chain '%s' not found", name);
284 :
285 0 : r = bf_cgen_get_counters(cgen, &counters);
286 0 : if (r)
287 0 : return bf_err_r(r, "failed to request counters for '%s'", name);
288 :
289 0 : r = bf_wpack_new(&wpack);
290 0 : if (r)
291 : return r;
292 :
293 0 : bf_wpack_open_object(wpack, "chain");
294 0 : r = bf_chain_pack(cgen->chain, wpack);
295 0 : if (r)
296 : return r;
297 0 : bf_wpack_close_object(wpack);
298 :
299 0 : if (cgen->program->link->hookopts) {
300 0 : bf_wpack_open_object(wpack, "hookopts");
301 0 : r = bf_hookopts_pack(cgen->program->link->hookopts, wpack);
302 0 : if (r)
303 : return r;
304 0 : bf_wpack_close_object(wpack);
305 : } else {
306 0 : bf_wpack_kv_nil(wpack, "hookopts");
307 : }
308 :
309 0 : bf_wpack_kv_list(wpack, "counters", &counters);
310 :
311 0 : return bf_response_new_from_pack(response, wpack);
312 : }
313 :
314 0 : int _bf_cli_chain_prog_fd(const struct bf_request *request,
315 : struct bf_response **response)
316 : {
317 : struct bf_cgen *cgen;
318 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
319 0 : _cleanup_free_ char *name = NULL;
320 : int r;
321 :
322 : UNUSED(response);
323 :
324 0 : r = bf_rpack_new(&pack, bf_request_data(request),
325 : bf_request_data_len(request));
326 0 : if (r)
327 : return r;
328 :
329 0 : r = bf_rpack_kv_str(bf_rpack_root(pack), "name", &name);
330 0 : if (r)
331 : return r;
332 :
333 0 : cgen = bf_ctx_get_cgen(name);
334 0 : if (!cgen)
335 0 : return bf_err_r(-ENOENT, "failed to find chain '%s'", name);
336 :
337 0 : if (!cgen->program || cgen->program->runtime.prog_fd == -1)
338 0 : return bf_err_r(-ENODEV, "chain '%s' has no loaded program", name);
339 :
340 0 : r = bf_send_fd(bf_request_fd(request), cgen->program->runtime.prog_fd);
341 0 : if (r < 0)
342 0 : return bf_err_r(errno, "failed to send prog FD for '%s'", name);
343 :
344 : return 0;
345 : }
346 :
347 0 : int _bf_cli_chain_logs_fd(const struct bf_request *request,
348 : struct bf_response **response)
349 : {
350 : struct bf_cgen *cgen;
351 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
352 0 : _cleanup_free_ char *name = NULL;
353 : int r;
354 :
355 : UNUSED(response);
356 :
357 0 : r = bf_rpack_new(&pack, bf_request_data(request),
358 : bf_request_data_len(request));
359 0 : if (r)
360 : return r;
361 :
362 0 : r = bf_rpack_kv_str(bf_rpack_root(pack), "name", &name);
363 0 : if (r)
364 : return r;
365 :
366 0 : cgen = bf_ctx_get_cgen(name);
367 0 : if (!cgen)
368 0 : return bf_err_r(-ENOENT, "failed to find chain '%s'", name);
369 :
370 0 : if (!cgen->program || !cgen->program->lmap->fd)
371 0 : return bf_err_r(-ENOENT, "chain '%s' has no logs buffer", name);
372 :
373 0 : r = bf_send_fd(bf_request_fd(request), cgen->program->lmap->fd);
374 0 : if (r < 0)
375 0 : return bf_err_r(errno, "failed to send logs FD for '%s'", name);
376 :
377 : return 0;
378 : }
379 :
380 0 : int _bf_cli_chain_load(const struct bf_request *request,
381 : struct bf_response **response)
382 : {
383 0 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
384 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
385 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
386 : bf_rpack_node_t child;
387 : int r;
388 :
389 : UNUSED(response);
390 :
391 0 : bf_assert(request);
392 :
393 0 : r = bf_rpack_new(&pack, bf_request_data(request),
394 : bf_request_data_len(request));
395 0 : if (r)
396 : return r;
397 :
398 0 : r = bf_rpack_kv_obj(bf_rpack_root(pack), "chain", &child);
399 0 : if (r)
400 : return r;
401 0 : r = bf_chain_new_from_pack(&chain, child);
402 0 : if (r)
403 : return r;
404 :
405 0 : if (bf_ctx_get_cgen(chain->name)) {
406 0 : return bf_err_r(-EEXIST,
407 : "_bf_cli_chain_load: chain '%s' already exists",
408 : chain->name);
409 : }
410 :
411 0 : r = bf_cgen_new(&cgen, BF_FRONT_CLI, &chain);
412 0 : if (r)
413 : return r;
414 :
415 0 : r = bf_cgen_load(cgen);
416 0 : if (r)
417 : return r;
418 :
419 0 : r = bf_ctx_set_cgen(cgen);
420 0 : if (r) {
421 0 : bf_cgen_unload(cgen);
422 0 : return bf_err_r(
423 : r, "bf_ctx_set_cgen: failed to add cgen to the runtime context");
424 : }
425 :
426 0 : TAKE_PTR(cgen);
427 :
428 0 : return r;
429 : }
430 :
431 0 : int _bf_cli_chain_attach(const struct bf_request *request,
432 : struct bf_response **response)
433 : {
434 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
435 0 : _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
436 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
437 0 : _cleanup_free_ char *name = NULL;
438 : struct bf_cgen *cgen = NULL;
439 : bf_rpack_node_t child;
440 : int r;
441 :
442 : UNUSED(response);
443 :
444 0 : bf_assert(request);
445 :
446 0 : r = bf_rpack_new(&pack, bf_request_data(request),
447 : bf_request_data_len(request));
448 0 : if (r)
449 : return r;
450 :
451 0 : r = bf_rpack_kv_str(bf_rpack_root(pack), "name", &name);
452 0 : if (r)
453 : return r;
454 :
455 0 : r = bf_rpack_kv_obj(bf_rpack_root(pack), "hookopts", &child);
456 0 : if (r)
457 : return r;
458 0 : r = bf_hookopts_new_from_pack(&hookopts, child);
459 0 : if (r)
460 : return r;
461 :
462 0 : cgen = bf_ctx_get_cgen(name);
463 0 : if (!cgen)
464 0 : return bf_err_r(-ENOENT, "chain '%s' does not exist", name);
465 0 : if (cgen->program->link->hookopts)
466 0 : return bf_err_r(-EBUSY, "chain '%s' is already linked to a hook", name);
467 :
468 0 : r = bf_hookopts_validate(hookopts, cgen->chain->hook);
469 0 : if (r)
470 0 : return bf_err_r(r, "failed to validate hook options");
471 :
472 0 : r = bf_cgen_attach(cgen, bf_request_ns(request), &hookopts);
473 0 : if (r)
474 0 : return bf_err_r(r, "failed to attach codegen to hook");
475 :
476 : return r;
477 : }
478 :
479 0 : int _bf_cli_chain_update(const struct bf_request *request,
480 : struct bf_response **response)
481 : {
482 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
483 : struct bf_cgen *cgen = NULL;
484 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
485 : bf_rpack_node_t child;
486 : int r;
487 :
488 : UNUSED(response);
489 :
490 0 : bf_assert(request);
491 :
492 0 : r = bf_rpack_new(&pack, bf_request_data(request),
493 : bf_request_data_len(request));
494 0 : if (r)
495 : return r;
496 :
497 0 : r = bf_rpack_kv_obj(bf_rpack_root(pack), "chain", &child);
498 0 : if (r)
499 : return r;
500 0 : r = bf_chain_new_from_pack(&chain, child);
501 0 : if (r)
502 : return r;
503 :
504 0 : cgen = bf_ctx_get_cgen(chain->name);
505 0 : if (!cgen)
506 : return -ENOENT;
507 :
508 0 : if (!cgen->program->link->hookopts) {
509 0 : return bf_err_r(-EINVAL, "chain '%s' is not attached", chain->name);
510 : }
511 :
512 0 : r = bf_cgen_update(cgen, &chain);
513 0 : if (r)
514 : return -EINVAL;
515 :
516 : return r;
517 : }
518 :
519 0 : int _bf_cli_chain_flush(const struct bf_request *request,
520 : struct bf_response **response)
521 : {
522 : struct bf_cgen *cgen = NULL;
523 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
524 0 : _cleanup_free_ char *name = NULL;
525 : int r;
526 :
527 : UNUSED(response);
528 :
529 0 : bf_assert(request);
530 :
531 0 : r = bf_rpack_new(&pack, bf_request_data(request),
532 : bf_request_data_len(request));
533 0 : if (r)
534 : return r;
535 :
536 0 : r = bf_rpack_kv_str(bf_rpack_root(pack), "name", &name);
537 0 : if (r)
538 : return r;
539 :
540 0 : cgen = bf_ctx_get_cgen(name);
541 0 : if (!cgen)
542 : return -ENOENT;
543 :
544 0 : return bf_ctx_delete_cgen(cgen, true);
545 : }
546 :
547 0 : static int _bf_cli_request_handler(const struct bf_request *request,
548 : struct bf_response **response)
549 : {
550 : int r;
551 :
552 0 : bf_assert(request);
553 0 : bf_assert(response);
554 :
555 0 : switch (bf_request_cmd(request)) {
556 0 : case BF_REQ_RULESET_FLUSH:
557 0 : r = _bf_cli_ruleset_flush(request, response);
558 0 : break;
559 0 : case BF_REQ_RULESET_SET:
560 0 : r = _bf_cli_ruleset_set(request, response);
561 0 : break;
562 0 : case BF_REQ_RULESET_GET:
563 0 : r = _bf_cli_ruleset_get(request, response);
564 0 : break;
565 0 : case BF_REQ_CHAIN_SET:
566 0 : r = _bf_cli_chain_set(request, response);
567 0 : break;
568 0 : case BF_REQ_CHAIN_GET:
569 0 : r = _bf_cli_chain_get(request, response);
570 0 : break;
571 0 : case BF_REQ_CHAIN_PROG_FD:
572 0 : r = _bf_cli_chain_prog_fd(request, response);
573 0 : break;
574 0 : case BF_REQ_CHAIN_LOGS_FD:
575 0 : r = _bf_cli_chain_logs_fd(request, response);
576 0 : break;
577 0 : case BF_REQ_CHAIN_LOAD:
578 0 : r = _bf_cli_chain_load(request, response);
579 0 : break;
580 0 : case BF_REQ_CHAIN_ATTACH:
581 0 : r = _bf_cli_chain_attach(request, response);
582 0 : break;
583 0 : case BF_REQ_CHAIN_UPDATE:
584 0 : r = _bf_cli_chain_update(request, response);
585 0 : break;
586 0 : case BF_REQ_CHAIN_FLUSH:
587 0 : r = _bf_cli_chain_flush(request, response);
588 0 : break;
589 0 : default:
590 0 : r = bf_err_r(-EINVAL, "unsupported command %d for CLI front-end",
591 : bf_request_cmd(request));
592 : break;
593 : }
594 :
595 : /* If the callback don't need to send data back to the client, it can skip
596 : * the response creation and return a status code instead (0 on success,
597 : * negative errno value on error). The response is created based on the
598 : * status code. */
599 0 : if (!*response) {
600 0 : if (!r)
601 0 : r = bf_response_new_success(response, NULL, 0);
602 : else
603 0 : r = bf_response_new_failure(response, r);
604 : }
605 :
606 0 : return r;
607 : }
608 :
609 0 : static int _bf_cli_pack(bf_wpack_t *pack)
610 : {
611 : UNUSED(pack);
612 :
613 0 : return 0;
614 : }
615 :
616 0 : static int _bf_cli_unpack(bf_rpack_node_t node)
617 : {
618 : UNUSED(node);
619 :
620 0 : return 0;
621 : }
|