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 0 : bf_assert(request && response);
130 :
131 0 : bf_ctx_flush(BF_FRONT_CLI);
132 :
133 0 : r = bf_rpack_new(&pack, bf_request_data(request),
134 : bf_request_data_len(request));
135 0 : if (r)
136 : return r;
137 :
138 0 : r = bf_rpack_kv_array(bf_rpack_root(pack), "ruleset", &child);
139 0 : if (r)
140 : return r;
141 0 : bf_rpack_array_foreach (child, node) {
142 0 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
143 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
144 0 : _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
145 : bf_rpack_node_t child;
146 :
147 0 : r = bf_rpack_kv_obj(node, "chain", &child);
148 0 : if (r)
149 0 : goto err_load;
150 :
151 0 : r = bf_chain_new_from_pack(&chain, child);
152 0 : if (r)
153 0 : goto err_load;
154 :
155 0 : r = bf_rpack_kv_node(node, "hookopts", &child);
156 0 : if (r)
157 0 : goto err_load;
158 0 : if (!bf_rpack_is_nil(child)) {
159 0 : r = bf_hookopts_new_from_pack(&hookopts, child);
160 0 : if (r)
161 0 : goto err_load;
162 : }
163 :
164 0 : r = bf_cgen_new(&cgen, BF_FRONT_CLI, &chain);
165 0 : if (r)
166 0 : goto err_load;
167 :
168 0 : r = bf_cgen_set(cgen, bf_request_ns(request),
169 0 : hookopts ? &hookopts : NULL);
170 0 : if (r) {
171 0 : bf_err_r(r, "failed to set chain '%s'", cgen->chain->name);
172 0 : goto err_load;
173 : }
174 :
175 0 : r = bf_ctx_set_cgen(cgen);
176 0 : if (r) {
177 : /* The codegen is loaded already, if the daemon runs in persistent
178 : * mode, cleaning the codegen won't be sufficient to discard the
179 : * chain, it must be unpinned. */
180 0 : bf_cgen_unload(cgen);
181 0 : goto err_load;
182 : }
183 :
184 0 : TAKE_PTR(cgen);
185 : }
186 :
187 : return 0;
188 :
189 : err_load:
190 0 : bf_ctx_flush(BF_FRONT_CLI);
191 0 : return r;
192 : }
193 :
194 0 : int _bf_cli_chain_set(const struct bf_request *request,
195 : struct bf_response **response)
196 : {
197 : struct bf_cgen *old_cgen;
198 0 : _free_bf_cgen_ struct bf_cgen *new_cgen = NULL;
199 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
200 0 : _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
201 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
202 : bf_rpack_node_t root, child;
203 : int r;
204 :
205 0 : bf_assert(request && response);
206 :
207 0 : r = bf_rpack_new(&pack, bf_request_data(request),
208 : bf_request_data_len(request));
209 0 : if (r)
210 : return r;
211 :
212 0 : root = bf_rpack_root(pack);
213 :
214 0 : r = bf_rpack_kv_obj(root, "chain", &child);
215 0 : if (r)
216 : return r;
217 0 : r = bf_chain_new_from_pack(&chain, child);
218 0 : if (r)
219 : return r;
220 :
221 0 : r = bf_rpack_kv_node(root, "hookopts", &child);
222 0 : if (r)
223 : return r;
224 0 : if (!bf_rpack_is_nil(child)) {
225 0 : r = bf_hookopts_new_from_pack(&hookopts, child);
226 0 : if (r)
227 : return r;
228 : }
229 :
230 0 : r = bf_cgen_new(&new_cgen, BF_FRONT_CLI, &chain);
231 0 : if (r)
232 : return r;
233 :
234 0 : old_cgen = bf_ctx_get_cgen(new_cgen->chain->name);
235 0 : if (old_cgen) {
236 : /* bf_ctx_delete_cgen() can only fail if the codegen is not found,
237 : * but we know this codegen exist. */
238 0 : (void)bf_ctx_delete_cgen(old_cgen, true);
239 : }
240 :
241 0 : r = bf_cgen_set(new_cgen, bf_request_ns(request),
242 0 : hookopts ? &hookopts : NULL);
243 0 : if (r)
244 : return r;
245 :
246 0 : r = bf_ctx_set_cgen(new_cgen);
247 0 : if (r) {
248 0 : bf_cgen_unload(new_cgen);
249 0 : return r;
250 : }
251 :
252 0 : TAKE_PTR(new_cgen);
253 :
254 0 : return r;
255 : }
256 :
257 0 : static int _bf_cli_chain_get(const struct bf_request *request,
258 : struct bf_response **response)
259 : {
260 0 : _clean_bf_list_ bf_list counters =
261 : bf_list_default(bf_counter_free, bf_counter_pack);
262 : struct bf_cgen *cgen;
263 0 : _cleanup_free_ char *name = NULL;
264 0 : _free_bf_wpack_ bf_wpack_t *wpack = NULL;
265 0 : _free_bf_rpack_ bf_rpack_t *rpack = NULL;
266 : int r;
267 :
268 0 : r = bf_rpack_new(&rpack, bf_request_data(request),
269 : bf_request_data_len(request));
270 0 : if (r)
271 : return r;
272 :
273 0 : r = bf_rpack_kv_str(bf_rpack_root(rpack), "name", &name);
274 0 : if (r)
275 : return r;
276 :
277 0 : cgen = bf_ctx_get_cgen(name);
278 0 : if (!cgen)
279 0 : return bf_err_r(-ENOENT, "chain '%s' not found", name);
280 :
281 0 : r = bf_cgen_get_counters(cgen, &counters);
282 0 : if (r)
283 0 : return bf_err_r(r, "failed to request counters for '%s'", name);
284 :
285 0 : r = bf_wpack_new(&wpack);
286 0 : if (r)
287 : return r;
288 :
289 0 : bf_wpack_open_object(wpack, "chain");
290 0 : r = bf_chain_pack(cgen->chain, wpack);
291 0 : if (r)
292 : return r;
293 0 : bf_wpack_close_object(wpack);
294 :
295 0 : if (cgen->program->link->hookopts) {
296 0 : bf_wpack_open_object(wpack, "hookopts");
297 0 : r = bf_hookopts_pack(cgen->program->link->hookopts, wpack);
298 0 : if (r)
299 : return r;
300 0 : bf_wpack_close_object(wpack);
301 : } else {
302 0 : bf_wpack_kv_nil(wpack, "hookopts");
303 : }
304 :
305 0 : bf_wpack_kv_list(wpack, "counters", &counters);
306 :
307 0 : return bf_response_new_from_pack(response, wpack);
308 : }
309 :
310 0 : int _bf_cli_chain_prog_fd(const struct bf_request *request,
311 : struct bf_response **response)
312 : {
313 : struct bf_cgen *cgen;
314 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
315 0 : _cleanup_free_ char *name = NULL;
316 : int r;
317 :
318 : UNUSED(response);
319 :
320 0 : r = bf_rpack_new(&pack, bf_request_data(request),
321 : bf_request_data_len(request));
322 0 : if (r)
323 : return r;
324 :
325 0 : r = bf_rpack_kv_str(bf_rpack_root(pack), "name", &name);
326 0 : if (r)
327 : return r;
328 :
329 0 : cgen = bf_ctx_get_cgen(name);
330 0 : if (!cgen)
331 0 : return bf_err_r(-ENOENT, "failed to find chain '%s'", name);
332 :
333 0 : if (!cgen->program || cgen->program->runtime.prog_fd == -1)
334 0 : return bf_err_r(-ENODEV, "chain '%s' has no loaded program", name);
335 :
336 0 : r = bf_send_fd(bf_request_fd(request), cgen->program->runtime.prog_fd);
337 0 : if (r < 0)
338 0 : return bf_err_r(errno, "failed to send prog FD for '%s'", name);
339 :
340 : return 0;
341 : }
342 :
343 0 : int _bf_cli_chain_logs_fd(const struct bf_request *request,
344 : struct bf_response **response)
345 : {
346 : struct bf_cgen *cgen;
347 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
348 0 : _cleanup_free_ char *name = NULL;
349 : int r;
350 :
351 : UNUSED(response);
352 :
353 0 : r = bf_rpack_new(&pack, bf_request_data(request),
354 : bf_request_data_len(request));
355 0 : if (r)
356 : return r;
357 :
358 0 : r = bf_rpack_kv_str(bf_rpack_root(pack), "name", &name);
359 0 : if (r)
360 : return r;
361 :
362 0 : cgen = bf_ctx_get_cgen(name);
363 0 : if (!cgen)
364 0 : return bf_err_r(-ENOENT, "failed to find chain '%s'", name);
365 :
366 0 : if (!cgen->program || !cgen->program->lmap->fd)
367 0 : return bf_err_r(-ENOENT, "chain '%s' has no logs buffer", name);
368 :
369 0 : r = bf_send_fd(bf_request_fd(request), cgen->program->lmap->fd);
370 0 : if (r < 0)
371 0 : return bf_err_r(errno, "failed to send logs FD for '%s'", name);
372 :
373 : return 0;
374 : }
375 :
376 0 : int _bf_cli_chain_load(const struct bf_request *request,
377 : struct bf_response **response)
378 : {
379 0 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
380 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
381 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
382 : bf_rpack_node_t child;
383 : int r;
384 :
385 0 : bf_assert(request && response);
386 :
387 0 : r = bf_rpack_new(&pack, bf_request_data(request),
388 : bf_request_data_len(request));
389 0 : if (r)
390 : return r;
391 :
392 0 : r = bf_rpack_kv_obj(bf_rpack_root(pack), "chain", &child);
393 0 : if (r)
394 : return r;
395 0 : r = bf_chain_new_from_pack(&chain, child);
396 0 : if (r)
397 : return r;
398 :
399 0 : if (bf_ctx_get_cgen(chain->name)) {
400 0 : return bf_err_r(-EEXIST,
401 : "_bf_cli_chain_load: chain '%s' already exists",
402 : chain->name);
403 : }
404 :
405 0 : r = bf_cgen_new(&cgen, BF_FRONT_CLI, &chain);
406 0 : if (r)
407 : return r;
408 :
409 0 : r = bf_cgen_load(cgen);
410 0 : if (r)
411 : return r;
412 :
413 0 : r = bf_ctx_set_cgen(cgen);
414 0 : if (r) {
415 0 : bf_cgen_unload(cgen);
416 0 : return bf_err_r(
417 : r, "bf_ctx_set_cgen: failed to add cgen to the runtime context");
418 : }
419 :
420 0 : TAKE_PTR(cgen);
421 :
422 0 : return r;
423 : }
424 :
425 0 : int _bf_cli_chain_attach(const struct bf_request *request,
426 : struct bf_response **response)
427 : {
428 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
429 0 : _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
430 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
431 0 : _cleanup_free_ char *name = NULL;
432 : struct bf_cgen *cgen = NULL;
433 : bf_rpack_node_t child;
434 : int r;
435 :
436 0 : bf_assert(request && response);
437 :
438 0 : r = bf_rpack_new(&pack, bf_request_data(request),
439 : bf_request_data_len(request));
440 0 : if (r)
441 : return r;
442 :
443 0 : r = bf_rpack_kv_str(bf_rpack_root(pack), "name", &name);
444 0 : if (r)
445 : return r;
446 :
447 0 : r = bf_rpack_kv_obj(bf_rpack_root(pack), "hookopts", &child);
448 0 : if (r)
449 : return r;
450 0 : r = bf_hookopts_new_from_pack(&hookopts, child);
451 0 : if (r)
452 : return r;
453 :
454 0 : cgen = bf_ctx_get_cgen(name);
455 0 : if (!cgen)
456 0 : return bf_err_r(-ENOENT, "chain '%s' does not exist", name);
457 0 : if (cgen->program->link->hookopts)
458 0 : return bf_err_r(-EBUSY, "chain '%s' is already linked to a hook", name);
459 :
460 0 : r = bf_hookopts_validate(hookopts, cgen->chain->hook);
461 0 : if (r)
462 0 : return bf_err_r(r, "failed to validate hook options");
463 :
464 0 : r = bf_cgen_attach(cgen, bf_request_ns(request), &hookopts);
465 0 : if (r)
466 0 : return bf_err_r(r, "failed to attach codegen to hook");
467 :
468 : return r;
469 : }
470 :
471 0 : int _bf_cli_chain_update(const struct bf_request *request,
472 : struct bf_response **response)
473 : {
474 0 : _free_bf_chain_ struct bf_chain *chain = NULL;
475 : struct bf_cgen *cgen = NULL;
476 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
477 : bf_rpack_node_t child;
478 : int r;
479 :
480 0 : bf_assert(request && response);
481 :
482 0 : r = bf_rpack_new(&pack, bf_request_data(request),
483 : bf_request_data_len(request));
484 0 : if (r)
485 : return r;
486 :
487 0 : r = bf_rpack_kv_obj(bf_rpack_root(pack), "chain", &child);
488 0 : if (r)
489 : return r;
490 0 : r = bf_chain_new_from_pack(&chain, child);
491 0 : if (r)
492 : return r;
493 :
494 0 : cgen = bf_ctx_get_cgen(chain->name);
495 0 : if (!cgen)
496 : return -ENOENT;
497 :
498 0 : if (!cgen->program->link->hookopts) {
499 0 : return bf_err_r(-EINVAL, "chain '%s' is not attached", chain->name);
500 : }
501 :
502 0 : r = bf_cgen_update(cgen, &chain);
503 0 : if (r)
504 : return -EINVAL;
505 :
506 : return r;
507 : }
508 :
509 0 : int _bf_cli_chain_flush(const struct bf_request *request,
510 : struct bf_response **response)
511 : {
512 : struct bf_cgen *cgen = NULL;
513 0 : _free_bf_rpack_ bf_rpack_t *pack = NULL;
514 0 : _cleanup_free_ char *name = NULL;
515 : int r;
516 :
517 0 : bf_assert(request && response);
518 :
519 0 : r = bf_rpack_new(&pack, bf_request_data(request),
520 : bf_request_data_len(request));
521 0 : if (r)
522 : return r;
523 :
524 0 : r = bf_rpack_kv_str(bf_rpack_root(pack), "name", &name);
525 0 : if (r)
526 : return r;
527 :
528 0 : cgen = bf_ctx_get_cgen(name);
529 0 : if (!cgen)
530 : return -ENOENT;
531 :
532 0 : return bf_ctx_delete_cgen(cgen, true);
533 : }
534 :
535 0 : static int _bf_cli_request_handler(const struct bf_request *request,
536 : struct bf_response **response)
537 : {
538 : int r;
539 :
540 0 : bf_assert(request);
541 0 : bf_assert(response);
542 :
543 0 : switch (bf_request_cmd(request)) {
544 0 : case BF_REQ_RULESET_FLUSH:
545 0 : r = _bf_cli_ruleset_flush(request, response);
546 0 : break;
547 0 : case BF_REQ_RULESET_SET:
548 0 : r = _bf_cli_ruleset_set(request, response);
549 0 : break;
550 0 : case BF_REQ_RULESET_GET:
551 0 : r = _bf_cli_ruleset_get(request, response);
552 0 : break;
553 0 : case BF_REQ_CHAIN_SET:
554 0 : r = _bf_cli_chain_set(request, response);
555 0 : break;
556 0 : case BF_REQ_CHAIN_GET:
557 0 : r = _bf_cli_chain_get(request, response);
558 0 : break;
559 0 : case BF_REQ_CHAIN_PROG_FD:
560 0 : r = _bf_cli_chain_prog_fd(request, response);
561 0 : break;
562 0 : case BF_REQ_CHAIN_LOGS_FD:
563 0 : r = _bf_cli_chain_logs_fd(request, response);
564 0 : break;
565 0 : case BF_REQ_CHAIN_LOAD:
566 0 : r = _bf_cli_chain_load(request, response);
567 0 : break;
568 0 : case BF_REQ_CHAIN_ATTACH:
569 0 : r = _bf_cli_chain_attach(request, response);
570 0 : break;
571 0 : case BF_REQ_CHAIN_UPDATE:
572 0 : r = _bf_cli_chain_update(request, response);
573 0 : break;
574 0 : case BF_REQ_CHAIN_FLUSH:
575 0 : r = _bf_cli_chain_flush(request, response);
576 0 : break;
577 0 : default:
578 0 : r = bf_err_r(-EINVAL, "unsupported command %d for CLI front-end",
579 : bf_request_cmd(request));
580 : break;
581 : }
582 :
583 : /* If the callback don't need to send data back to the client, it can skip
584 : * the response creation and return a status code instead (0 on success,
585 : * negative errno value on error). The response is created based on the
586 : * status code. */
587 0 : if (!*response) {
588 0 : if (!r)
589 0 : r = bf_response_new_success(response, NULL, 0);
590 : else
591 0 : r = bf_response_new_failure(response, r);
592 : }
593 :
594 0 : return r;
595 : }
596 :
597 0 : static int _bf_cli_pack(bf_wpack_t *pack)
598 : {
599 : UNUSED(pack);
600 :
601 0 : return 0;
602 : }
603 :
604 0 : static int _bf_cli_unpack(bf_rpack_node_t node)
605 : {
606 : UNUSED(node);
607 :
608 0 : return 0;
609 : }
|