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 <stdlib.h>
7 : : #include <string.h>
8 : : #include <unistd.h>
9 : :
10 : : #include <bpfilter/ctx.h>
11 : :
12 : : #include "bpfilter/chain.h"
13 : : #include "bpfilter/core/list.h"
14 : : #include "bpfilter/counter.h"
15 : : #include "bpfilter/helper.h"
16 : : #include "bpfilter/hook.h"
17 : : #include "bpfilter/logger.h"
18 : : #include "bpfilter/pack.h"
19 : : #include "bpfilter/set.h"
20 : : #include "cgen/cgen.h"
21 : : #include "cgen/handle.h"
22 : : #include "cgen/prog/link.h"
23 : : #include "cgen/prog/map.h"
24 : :
25 : 143 : static int copy_hookopts(struct bf_hookopts **dest,
26 : : const struct bf_hookopts *src)
27 : : {
28 : : struct bf_hookopts *copy;
29 : :
30 : 143 : copy = bf_memdup(src, sizeof(*src));
31 [ - + ]: 143 : if (!copy)
32 : : return -ENOMEM;
33 : :
34 [ + + ]: 143 : if (src->cgpath) {
35 : 51 : copy->cgpath = strdup(src->cgpath);
36 [ - + ]: 51 : if (!copy->cgpath) {
37 : 0 : free(copy);
38 : 0 : return -ENOMEM;
39 : : }
40 : : }
41 : :
42 : 143 : *dest = copy;
43 : :
44 : 143 : return 0;
45 : : }
46 : :
47 : 4 : int bf_ruleset_get(bf_list *chains, bf_list *hookopts, bf_list *counters)
48 : : {
49 : 4 : _clean_bf_list_ bf_list cgens = bf_list_default(NULL, NULL);
50 : 4 : _clean_bf_list_ bf_list _chains = bf_list_default_from(*chains);
51 : 4 : _clean_bf_list_ bf_list _hookopts = bf_list_default_from(*hookopts);
52 : 4 : _clean_bf_list_ bf_list _counters = bf_list_default_from(*counters);
53 : : int r;
54 : :
55 : 4 : r = bf_ctx_get_cgens(&cgens);
56 [ - + ]: 4 : if (r < 0)
57 [ # # ]: 0 : return bf_err_r(r, "failed to get cgen list");
58 : :
59 [ + - + + : 18 : bf_list_foreach (&cgens, cgen_node) {
+ + ]
60 : : struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
61 : 5 : _free_bf_chain_ struct bf_chain *chain = NULL;
62 : 5 : _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
63 : 0 : _free_bf_list_ bf_list *cgen_counters = NULL;
64 : :
65 : 5 : r = bf_chain_new_from_copy(&chain, cgen->chain);
66 [ - + ]: 5 : if (r)
67 [ # # ]: 0 : return bf_err_r(r, "failed to copy chain");
68 : :
69 : 5 : r = bf_list_add_tail(&_chains, chain);
70 [ + - ]: 5 : if (r)
71 : : return r;
72 : 5 : TAKE_PTR(chain);
73 : :
74 [ + + + - ]: 5 : if (cgen->handle->link && cgen->handle->link->hookopts) {
75 : 4 : r = copy_hookopts(&hookopts_copy, cgen->handle->link->hookopts);
76 [ - + ]: 4 : if (r)
77 [ # # ]: 0 : return bf_err_r(r, "failed to copy hookopts");
78 : : }
79 : 5 : r = bf_list_add_tail(&_hookopts, hookopts_copy);
80 [ + - ]: 5 : if (r)
81 : : return r;
82 : 5 : TAKE_PTR(hookopts_copy);
83 : :
84 : 5 : r = bf_list_new(&cgen_counters,
85 : 5 : &bf_list_ops_default(bf_counter_free, NULL));
86 [ + - ]: 5 : if (r)
87 : : return r;
88 : :
89 : 5 : r = bf_cgen_get_counters(cgen, cgen_counters);
90 [ + - ]: 5 : if (r)
91 : : return r;
92 : :
93 : 5 : r = bf_list_add_tail(&_counters, cgen_counters);
94 [ + - ]: 5 : if (r)
95 : : return r;
96 : 5 : TAKE_PTR(cgen_counters);
97 : : }
98 : :
99 : 4 : *chains = bf_list_move(_chains);
100 : 4 : *hookopts = bf_list_move(_hookopts);
101 : 4 : *counters = bf_list_move(_counters);
102 : :
103 : 4 : return 0;
104 : : }
105 : :
106 [ + + ]: 9 : int bf_ruleset_set(bf_list *chains, bf_list *hookopts)
107 : : {
108 : : struct bf_list_node *chain_node = bf_list_get_head(chains);
109 : : struct bf_list_node *hookopts_node = bf_list_get_head(hookopts);
110 : : int r;
111 : :
112 [ + + ]: 9 : if (bf_list_size(chains) != bf_list_size(hookopts))
113 : : return -EINVAL;
114 : :
115 : 8 : bf_ctx_flush();
116 : :
117 [ + + ]: 18 : while (chain_node && hookopts_node) {
118 : 12 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
119 : 12 : _free_bf_chain_ struct bf_chain *chain_copy = NULL;
120 : 24 : _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
121 : : struct bf_chain *chain = bf_list_node_get_data(chain_node);
122 : : struct bf_hookopts *node_hookopts =
123 : : bf_list_node_get_data(hookopts_node);
124 : :
125 : 12 : r = bf_chain_new_from_copy(&chain_copy, chain);
126 [ - + ]: 12 : if (r)
127 : 0 : goto err_load;
128 : :
129 [ + + ]: 12 : if (node_hookopts) {
130 : 10 : r = copy_hookopts(&hookopts_copy, node_hookopts);
131 [ - + ]: 10 : if (r)
132 : 0 : goto err_load;
133 : : }
134 : :
135 : 12 : r = bf_cgen_new(&cgen, &chain_copy);
136 [ - + ]: 12 : if (r)
137 : 0 : goto err_load;
138 : :
139 [ + + ]: 14 : r = bf_cgen_set(cgen, hookopts_copy ? &hookopts_copy : NULL);
140 [ + + ]: 12 : if (r) {
141 [ + - ]: 2 : bf_err_r(r, "failed to set chain '%s'", cgen->chain->name);
142 : 2 : goto err_load;
143 : : }
144 : :
145 : 10 : r = bf_ctx_set_cgen(cgen);
146 [ - + ]: 10 : if (r) {
147 : 0 : bf_cgen_unload(cgen);
148 : 0 : goto err_load;
149 : : }
150 : :
151 : 10 : TAKE_PTR(cgen);
152 : :
153 : : chain_node = bf_list_node_next(chain_node);
154 : : hookopts_node = bf_list_node_next(hookopts_node);
155 : : }
156 : :
157 : : return 0;
158 : :
159 : : err_load:
160 : 2 : bf_ctx_flush();
161 : 2 : return r;
162 : : }
163 : :
164 : 1680 : int bf_ruleset_flush(void)
165 : : {
166 : 1680 : bf_ctx_flush();
167 : :
168 : 1680 : return 0;
169 : : }
170 : :
171 : 1148 : int bf_chain_set(struct bf_chain *chain, struct bf_hookopts *hookopts)
172 : : {
173 : : struct bf_cgen *old_cgen;
174 : 1148 : _free_bf_cgen_ struct bf_cgen *new_cgen = NULL;
175 : 1148 : _free_bf_chain_ struct bf_chain *chain_copy = NULL;
176 : 1148 : _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
177 : : int r;
178 : :
179 : : assert(chain);
180 : :
181 : 1148 : r = bf_chain_new_from_copy(&chain_copy, chain);
182 [ + - ]: 1148 : if (r)
183 : : return r;
184 : :
185 [ + + ]: 1148 : if (hookopts) {
186 : 87 : r = copy_hookopts(&hookopts_copy, hookopts);
187 [ + - ]: 87 : if (r)
188 : : return r;
189 : : }
190 : :
191 : 1148 : r = bf_cgen_new(&new_cgen, &chain_copy);
192 [ + - ]: 1148 : if (r)
193 : : return r;
194 : :
195 : 1148 : old_cgen = bf_ctx_get_cgen(new_cgen->chain->name);
196 [ + + ]: 1148 : if (old_cgen)
197 : 233 : (void)bf_ctx_delete_cgen(old_cgen, true);
198 : :
199 [ + + ]: 2209 : r = bf_cgen_set(new_cgen, hookopts_copy ? &hookopts_copy : NULL);
200 [ + + ]: 1148 : if (r)
201 : : return r;
202 : :
203 : 1145 : r = bf_ctx_set_cgen(new_cgen);
204 [ - + ]: 1145 : if (r) {
205 : 0 : bf_cgen_unload(new_cgen);
206 : 0 : return r;
207 : : }
208 : :
209 : 1145 : TAKE_PTR(new_cgen);
210 : :
211 : 1145 : return 0;
212 : : }
213 : :
214 : 1047 : int bf_chain_get(const char *name, struct bf_chain **chain,
215 : : struct bf_hookopts **hookopts, bf_list *counters)
216 : : {
217 : 1047 : _free_bf_chain_ struct bf_chain *_chain = NULL;
218 : 1047 : _free_bf_hookopts_ struct bf_hookopts *_hookopts = NULL;
219 : 1047 : _clean_bf_list_ bf_list _counters = bf_list_default_from(*counters);
220 : : struct bf_cgen *cgen;
221 : : int r;
222 : :
223 : : assert(name);
224 : : assert(chain);
225 : : assert(hookopts);
226 : : assert(counters);
227 : :
228 : 1047 : cgen = bf_ctx_get_cgen(name);
229 [ + + ]: 1047 : if (!cgen)
230 [ + - ]: 1 : return bf_err_r(-ENOENT, "chain '%s' not found", name);
231 : :
232 : 1046 : r = bf_chain_new_from_copy(&_chain, cgen->chain);
233 [ + - ]: 1046 : if (r)
234 : : return r;
235 : :
236 [ + + + - ]: 1046 : if (cgen->handle->link && cgen->handle->link->hookopts) {
237 : 28 : r = copy_hookopts(&_hookopts, cgen->handle->link->hookopts);
238 [ + - ]: 28 : if (r)
239 : : return r;
240 : : }
241 : :
242 : 1046 : r = bf_cgen_get_counters(cgen, &_counters);
243 [ - + ]: 1046 : if (r)
244 [ # # ]: 0 : return bf_err_r(r, "failed to get counters for '%s'", name);
245 : :
246 : 1046 : *chain = TAKE_PTR(_chain);
247 : 1046 : *hookopts = TAKE_PTR(_hookopts);
248 : 1046 : *counters = bf_list_move(_counters);
249 : :
250 : 1046 : return 0;
251 : : }
252 : :
253 : 2681 : int bf_chain_prog_fd(const char *name)
254 : : {
255 : : struct bf_cgen *cgen;
256 : :
257 [ + + ]: 2681 : if (!name)
258 : : return -EINVAL;
259 : :
260 : 2680 : cgen = bf_ctx_get_cgen(name);
261 [ - + ]: 2680 : if (!cgen)
262 [ # # ]: 0 : return bf_err_r(-ENOENT, "failed to find chain '%s'", name);
263 : :
264 [ - + ]: 2680 : if (cgen->handle->prog_fd == -1)
265 [ # # ]: 0 : return bf_err_r(-ENODEV, "chain '%s' has no loaded program", name);
266 : :
267 : 2680 : return dup(cgen->handle->prog_fd);
268 : : }
269 : :
270 : 1 : int bf_chain_logs_fd(const char *name)
271 : : {
272 : : struct bf_cgen *cgen;
273 : :
274 [ - + ]: 1 : if (!name)
275 : : return -EINVAL;
276 : :
277 : 0 : cgen = bf_ctx_get_cgen(name);
278 [ # # ]: 0 : if (!cgen)
279 [ # # ]: 0 : return bf_err_r(-ENOENT, "failed to find chain '%s'", name);
280 : :
281 [ # # ]: 0 : if (!cgen->handle->lmap)
282 [ # # ]: 0 : return bf_err_r(-ENOENT, "chain '%s' has no logs buffer", name);
283 : :
284 : 0 : return dup(cgen->handle->lmap->fd);
285 : : }
286 : :
287 : 16 : int bf_chain_load(struct bf_chain *chain)
288 : : {
289 : 16 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
290 : 16 : _free_bf_chain_ struct bf_chain *chain_copy = NULL;
291 : : int r;
292 : :
293 : : assert(chain);
294 : :
295 [ - + ]: 16 : if (bf_ctx_get_cgen(chain->name))
296 [ # # ]: 0 : return bf_err_r(-EEXIST, "chain '%s' already exists", chain->name);
297 : :
298 : 16 : r = bf_chain_new_from_copy(&chain_copy, chain);
299 [ + - ]: 16 : if (r)
300 : : return r;
301 : :
302 : 16 : r = bf_cgen_new(&cgen, &chain_copy);
303 [ + - ]: 16 : if (r)
304 : : return r;
305 : :
306 : 16 : r = bf_cgen_load(cgen);
307 [ + - ]: 16 : if (r)
308 : : return r;
309 : :
310 : 16 : r = bf_ctx_set_cgen(cgen);
311 [ - + ]: 16 : if (r) {
312 : 0 : bf_cgen_unload(cgen);
313 [ # # ]: 0 : return bf_err_r(r, "failed to add cgen to the runtime context");
314 : : }
315 : :
316 : 16 : TAKE_PTR(cgen);
317 : :
318 : 16 : return 0;
319 : : }
320 : :
321 : 15 : int bf_chain_attach(const char *name, const struct bf_hookopts *hookopts)
322 : : {
323 : : struct bf_cgen *cgen;
324 : 15 : _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
325 : : int r;
326 : :
327 : : assert(name);
328 : : assert(hookopts);
329 : :
330 : 15 : cgen = bf_ctx_get_cgen(name);
331 [ - + ]: 15 : if (!cgen)
332 [ # # ]: 0 : return bf_err_r(-ENOENT, "chain '%s' does not exist", name);
333 : :
334 [ - + ]: 15 : if (cgen->handle->link)
335 [ # # ]: 0 : return bf_err_r(-EBUSY, "chain '%s' is already linked to a hook", name);
336 : :
337 : 15 : r = bf_hookopts_validate(hookopts, cgen->chain->hook);
338 [ + + ]: 15 : if (r)
339 [ + - ]: 1 : return bf_err_r(r, "failed to validate hook options");
340 : :
341 : 14 : r = copy_hookopts(&hookopts_copy, hookopts);
342 [ + - ]: 14 : if (r)
343 : : return r;
344 : :
345 : 14 : r = bf_cgen_attach(cgen, &hookopts_copy);
346 [ + + ]: 14 : if (r)
347 [ + - ]: 2 : return bf_err_r(r, "failed to attach codegen to hook");
348 : :
349 : : return 0;
350 : : }
351 : :
352 : 7 : int bf_chain_update(const struct bf_chain *chain)
353 : : {
354 : 7 : _free_bf_chain_ struct bf_chain *chain_copy = NULL;
355 : : struct bf_cgen *cgen;
356 : : int r;
357 : :
358 : : assert(chain);
359 : :
360 : 7 : cgen = bf_ctx_get_cgen(chain->name);
361 [ + + ]: 7 : if (!cgen)
362 : : return -ENOENT;
363 : :
364 : 6 : r = bf_chain_new_from_copy(&chain_copy, chain);
365 [ + - ]: 6 : if (r)
366 : : return r;
367 : :
368 : 6 : r = bf_cgen_update(cgen, &chain_copy, 0);
369 : : if (r)
370 : : return r;
371 : :
372 : : return 0;
373 : : }
374 : :
375 : 16 : static int copy_set(struct bf_set **dest, const struct bf_set *src)
376 : : {
377 : 16 : _free_bf_wpack_ bf_wpack_t *wpack = NULL;
378 : 16 : _free_bf_rpack_ bf_rpack_t *rpack = NULL;
379 : : const void *data;
380 : : size_t data_len;
381 : : int r;
382 : :
383 : 16 : r = bf_wpack_new(&wpack);
384 [ + - ]: 16 : if (r)
385 : : return r;
386 : :
387 : 16 : bf_wpack_open_object(wpack, "set");
388 : 16 : r = bf_set_pack(src, wpack);
389 [ + - ]: 16 : if (r)
390 : : return r;
391 : 16 : bf_wpack_close_object(wpack);
392 : :
393 : 16 : r = bf_wpack_get_data(wpack, &data, &data_len);
394 [ + - ]: 16 : if (r)
395 : : return r;
396 : :
397 : 16 : r = bf_rpack_new(&rpack, data, data_len);
398 [ + - ]: 16 : if (r)
399 : : return r;
400 : :
401 : : bf_rpack_node_t child;
402 : 16 : r = bf_rpack_kv_obj(bf_rpack_root(rpack), "set", &child);
403 [ + - ]: 16 : if (r)
404 : : return r;
405 : :
406 : 16 : return bf_set_new_from_pack(dest, child);
407 : : }
408 : :
409 : 8 : int bf_chain_update_set(const char *name, const struct bf_set *to_add,
410 : : const struct bf_set *to_remove)
411 : : {
412 : 8 : _free_bf_chain_ struct bf_chain *new_chain = NULL;
413 : : struct bf_set *dest_set = NULL;
414 : : struct bf_cgen *cgen;
415 : 8 : _free_bf_set_ struct bf_set *add_copy = NULL;
416 : 8 : _free_bf_set_ struct bf_set *remove_copy = NULL;
417 : : int r;
418 : :
419 : : assert(name);
420 : : assert(to_add);
421 : : assert(to_remove);
422 : :
423 [ - + ]: 8 : if (!bf_streq(to_add->name, to_remove->name))
424 [ # # ]: 0 : return bf_err_r(-EINVAL, "to_add->name must match to_remove->name");
425 : :
426 : 8 : cgen = bf_ctx_get_cgen(name);
427 [ - + ]: 8 : if (!cgen)
428 [ # # ]: 0 : return bf_err_r(-ENOENT, "chain '%s' does not exist", name);
429 : :
430 : 8 : r = bf_chain_new_from_copy(&new_chain, cgen->chain);
431 [ + - ]: 8 : if (r)
432 : : return r;
433 : :
434 : 8 : dest_set = bf_chain_get_set_by_name(new_chain, to_add->name);
435 [ - + ]: 8 : if (!dest_set)
436 [ # # ]: 0 : return bf_err_r(-ENOENT, "set '%s' does not exist", to_add->name);
437 : :
438 : 8 : r = copy_set(&add_copy, to_add);
439 [ + - ]: 8 : if (r)
440 : : return r;
441 : :
442 : 8 : r = copy_set(&remove_copy, to_remove);
443 [ + - ]: 8 : if (r)
444 : : return r;
445 : :
446 : 8 : r = bf_set_add_many(dest_set, &add_copy);
447 [ - + ]: 8 : if (r)
448 [ # # ]: 0 : return bf_err_r(r, "failed to calculate set union");
449 : :
450 : 8 : r = bf_set_remove_many(dest_set, &remove_copy);
451 [ - + ]: 8 : if (r)
452 [ # # ]: 0 : return bf_err_r(r, "failed to calculate set difference");
453 : :
454 : 8 : r = bf_cgen_update(cgen, &new_chain,
455 : : BF_FLAG(BF_CGEN_UPDATE_PRESERVE_COUNTERS));
456 [ - + ]: 8 : if (r)
457 [ # # ]: 0 : return bf_err_r(r, "failed to update chain with new set data");
458 : :
459 : : return 0;
460 : : }
461 : :
462 : 29 : int bf_chain_flush(const char *name)
463 : : {
464 : : struct bf_cgen *cgen;
465 : :
466 : : assert(name);
467 : :
468 : 29 : cgen = bf_ctx_get_cgen(name);
469 [ + - ]: 29 : if (!cgen)
470 : : return -ENOENT;
471 : :
472 : 29 : return bf_ctx_delete_cgen(cgen, true);
473 : : }
|