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/program.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : : #include <linux/limits.h>
11 : :
12 : : #include <errno.h>
13 : : #include <fcntl.h>
14 : : #include <limits.h>
15 : : #include <stddef.h>
16 : : #include <stdint.h>
17 : : #include <stdio.h>
18 : : #include <stdlib.h>
19 : : #include <string.h>
20 : : #include <unistd.h>
21 : :
22 : : #include <bpfilter/bpf.h>
23 : : #include <bpfilter/btf.h>
24 : : #include <bpfilter/chain.h>
25 : : #include <bpfilter/core/hashset.h>
26 : : #include <bpfilter/core/list.h>
27 : : #include <bpfilter/counter.h>
28 : : #include <bpfilter/ctx.h>
29 : : #include <bpfilter/dump.h>
30 : : #include <bpfilter/flavor.h>
31 : : #include <bpfilter/helper.h>
32 : : #include <bpfilter/hook.h>
33 : : #include <bpfilter/io.h>
34 : : #include <bpfilter/logger.h>
35 : : #include <bpfilter/matcher.h>
36 : : #include <bpfilter/pack.h>
37 : : #include <bpfilter/rule.h>
38 : : #include <bpfilter/set.h>
39 : : #include <bpfilter/verdict.h>
40 : :
41 : : #include "cgen/cgroup_skb.h"
42 : : #include "cgen/cgroup_sock_addr.h"
43 : : #include "cgen/dump.h"
44 : : #include "cgen/fixup.h"
45 : : #include "cgen/handle.h"
46 : : #include "cgen/jmp.h"
47 : : #include "cgen/nf.h"
48 : : #include "cgen/printer.h"
49 : : #include "cgen/prog/link.h"
50 : : #include "cgen/prog/map.h"
51 : : #include "cgen/stub.h"
52 : : #include "cgen/tc.h"
53 : : #include "cgen/xdp.h"
54 : : #include "filter.h"
55 : :
56 : : #define _BF_LOG_BUF_SIZE \
57 : : (UINT32_MAX >> 8) /* verifier maximum in kernels <= 5.1 */
58 : : #define _BF_LOG_MAP_N_ENTRIES 1000
59 : : #define _BF_LOG_MAP_SIZE \
60 : : _bf_round_next_power_of_2(sizeof(struct bf_log) * _BF_LOG_MAP_N_ENTRIES)
61 : : #define _BF_SET_MAP_PREFIX "bf_set_"
62 : : #define _BF_COUNTER_MAP_NAME "bf_cmap"
63 : : #define _BF_PRINTER_MAP_NAME "bf_pmap"
64 : : #define _BF_LOG_MAP_NAME "bf_lmap"
65 : : #define _BF_STATE_MAP_NAME "bf_smap"
66 : :
67 : : static inline size_t _bf_round_next_power_of_2(size_t value)
68 : : {
69 : : if (value == 0)
70 : : return 1;
71 : :
72 : : value--;
73 : : value |= value >> 1;
74 : : value |= value >> 2;
75 : : value |= value >> 4;
76 : : value |= value >> 8;
77 : : value |= value >> 16;
78 : : #if SIZE_MAX > 0xFFFFFFFFU
79 : : value |= value >> 32;
80 : : #endif
81 : :
82 : : return ++value;
83 : : }
84 : :
85 : : /**
86 : : * @brief Sets sharing the same key format, collapsed to a single BPF map.
87 : : *
88 : : * For hash-keyed sets, one `bf_set_group` exists for every unique key
89 : : * format among a chain's non-empty sets. The sets list preserves insertion
90 : : * order; a set's position is its bit index within the group's bitmask
91 : : * value.
92 : : *
93 : : * LPM trie sets are never grouped together: each non-empty trie set gets
94 : : * its own group of size 1. See `_bf_program_build_set_groups()` for the
95 : : * rationale.
96 : : */
97 : : struct bf_set_group
98 : : {
99 : : /** Non-empty sets that map to the same BPF map. For hash-keyed groups,
100 : : * all sets share the same key format; LPM trie groups always hold a
101 : : * single set. Non-owning pointers into the chain's `bf_set` list.
102 : : * Never empty. */
103 : : bf_list sets;
104 : :
105 : : /** Backing BPF map. Populated during `bf_program_load()`; the map is
106 : : * owned by `bf_program->handle->sets`. */
107 : : struct bf_map *map;
108 : : };
109 : :
110 : : #define _free_bf_set_group_ __attribute__((__cleanup__(_bf_set_group_free)))
111 : :
112 : 906 : static void _bf_set_group_free(struct bf_set_group **group)
113 : : {
114 : : assert(group);
115 : :
116 [ + + ]: 906 : if (!*group)
117 : : return;
118 : :
119 : 302 : bf_list_clean(&(*group)->sets);
120 : 302 : BF_FREEP(group);
121 : : }
122 : :
123 : 302 : static int _bf_set_group_new(struct bf_set_group **group)
124 : : {
125 : 302 : _free_bf_set_group_ struct bf_set_group *_group = NULL;
126 : :
127 : : assert(group);
128 : :
129 : 302 : _group = calloc(1, sizeof(*_group));
130 [ + - ]: 302 : if (!_group)
131 : : return -ENOMEM;
132 : :
133 : : /* The list holds non-owning const struct bf_set * pointers; no free
134 : : * callback. Groups are temporary (not serialized), so no pack callback
135 : : * either. */
136 : 302 : _group->sets = bf_list_default(NULL, NULL);
137 : :
138 : 302 : *group = TAKE_PTR(_group);
139 : :
140 : 302 : return 0;
141 : : }
142 : :
143 : : static struct bf_set_group *
144 : 277 : _bf_program_find_set_group(const struct bf_program *program,
145 : : const struct bf_set *set)
146 : : {
147 : : assert(program);
148 : :
149 [ + - ]: 277 : if (!set)
150 : : return NULL;
151 : :
152 [ + - + - : 756 : bf_list_foreach (&program->set_groups, group_node) {
+ - ]
153 : : struct bf_set_group *group = bf_list_node_get_data(group_node);
154 [ + - + + : 1064 : bf_list_foreach (&group->sets, set_node) {
+ + ]
155 [ + + ]: 431 : if (bf_list_node_get_data(set_node) == set)
156 : : return group;
157 : : }
158 : : }
159 : :
160 : : return NULL;
161 : : }
162 : :
163 : 1311 : static int _bf_program_build_set_groups(struct bf_program *program)
164 : : {
165 : : assert(program);
166 : :
167 : 1311 : bf_list_clean(&program->set_groups);
168 : 1311 : program->set_groups = bf_list_default(_bf_set_group_free, NULL);
169 : :
170 [ + + + + : 3280 : bf_list_foreach (&program->runtime.chain->sets, set_node) {
+ + ]
171 : : struct bf_set *set = bf_list_node_get_data(set_node);
172 : : struct bf_set_group *match = NULL;
173 : : int r;
174 : :
175 [ + + ]: 329 : if (bf_hashset_is_empty(&set->elems))
176 : 4 : continue;
177 : :
178 : : /* LPM trie sets are not grouped: BPF LPM trie lookup always
179 : : * returns the longest-prefix match, so the read-modify-write
180 : : * step in _bf_program_load_sets_maps() can't preserve the
181 : : * per-set bitmask when prefixes overlap. */
182 [ + + ]: 325 : if (!set->use_trie) {
183 [ + + + + : 724 : bf_list_foreach (&program->set_groups, group_node) {
+ + ]
184 : : struct bf_set_group *group = bf_list_node_get_data(group_node);
185 : : const struct bf_set *head =
186 : : bf_list_node_get_data(bf_list_get_head(&group->sets));
187 : :
188 [ + + ]: 124 : if (bf_set_same_key(set, head)) {
189 : : match = group;
190 : : break;
191 : : }
192 : : }
193 : : }
194 : :
195 [ + + ]: 261 : if (match) {
196 : 23 : r = bf_list_add_tail(&match->sets, set);
197 [ - + ]: 23 : if (r)
198 [ # # ]: 0 : return bf_err_r(r, "failed to add set to existing group");
199 : : } else {
200 : 302 : _free_bf_set_group_ struct bf_set_group *new_group = NULL;
201 : :
202 : 302 : r = _bf_set_group_new(&new_group);
203 [ - + ]: 302 : if (r)
204 [ # # ]: 0 : return bf_err_r(r, "failed to allocate set group");
205 : :
206 : 302 : r = bf_list_add_tail(&new_group->sets, set);
207 [ - + ]: 302 : if (r)
208 [ # # ]: 0 : return bf_err_r(r, "failed to seed set group");
209 : :
210 : 302 : r = bf_list_push(&program->set_groups, (void **)&new_group);
211 [ - + ]: 302 : if (r)
212 [ # # ]: 0 : return bf_err_r(r, "failed to register set group");
213 : : }
214 : : }
215 : :
216 : : return 0;
217 : : }
218 : :
219 : 277 : int bf_program_set_bit_index(const struct bf_program *program,
220 : : const struct bf_set *set, size_t *bit_index)
221 : : {
222 : : assert(program);
223 : : assert(set);
224 : : assert(bit_index);
225 : :
226 [ + - + - : 756 : bf_list_foreach (&program->set_groups, group_node) {
+ - ]
227 : : struct bf_set_group *group = bf_list_node_get_data(group_node);
228 : : size_t i = 0;
229 : :
230 [ + - + + ]: 963 : bf_list_foreach (&group->sets, set_node) {
231 [ + + ]: 431 : if (bf_list_node_get_data(set_node) == set) {
232 : 277 : *bit_index = i;
233 : 277 : return 0;
234 : : }
235 [ + + ]: 154 : ++i;
236 : : }
237 : : }
238 : :
239 : : return -ENOENT;
240 : : }
241 : :
242 : : static const struct bf_flavor_ops *bf_flavor_ops_get(enum bf_flavor flavor)
243 : : {
244 : : static const struct bf_flavor_ops *flavor_ops[] = {
245 : : [BF_FLAVOR_TC] = &bf_flavor_ops_tc,
246 : : [BF_FLAVOR_NF] = &bf_flavor_ops_nf,
247 : : [BF_FLAVOR_XDP] = &bf_flavor_ops_xdp,
248 : : [BF_FLAVOR_CGROUP_SKB] = &bf_flavor_ops_cgroup_skb,
249 : : [BF_FLAVOR_CGROUP_SOCK_ADDR] = &bf_flavor_ops_cgroup_sock_addr,
250 : : };
251 : :
252 : : static_assert_enum_mapping(flavor_ops, _BF_FLAVOR_MAX);
253 : :
254 : 1311 : return flavor_ops[flavor];
255 : : }
256 : :
257 : 1311 : int bf_program_new(struct bf_program **program, const struct bf_chain *chain,
258 : : struct bf_handle *handle)
259 : : {
260 : 1311 : _free_bf_program_ struct bf_program *_program = NULL;
261 : : int r;
262 : :
263 : : assert(program);
264 : : assert(chain);
265 : : assert(handle);
266 : :
267 : 1311 : _program = calloc(1, sizeof(*_program));
268 [ + - ]: 1311 : if (!_program)
269 : : return -ENOMEM;
270 : :
271 : 1311 : _program->flavor = bf_hook_to_flavor(chain->hook);
272 : 1311 : _program->runtime.ops = bf_flavor_ops_get(_program->flavor);
273 : 1311 : _program->runtime.chain = chain;
274 : 1311 : _program->img = bf_vector_default(sizeof(struct bpf_insn));
275 : 1311 : _program->fixups = bf_list_default(bf_fixup_free, NULL);
276 : 1311 : _program->set_groups = bf_list_default(_bf_set_group_free, NULL);
277 : 1311 : _program->handle = handle;
278 : :
279 : 1311 : r = bf_vector_reserve(&_program->img, 512);
280 [ + - ]: 1311 : if (r)
281 : : return r;
282 : :
283 : 1311 : r = bf_printer_new(&_program->printer);
284 [ + - ]: 1311 : if (r)
285 : : return r;
286 : :
287 : 1311 : *program = TAKE_PTR(_program);
288 : :
289 : 1311 : return 0;
290 : : }
291 : :
292 : 2622 : void bf_program_free(struct bf_program **program)
293 : : {
294 : : assert(program);
295 : :
296 [ + + ]: 2622 : if (!*program)
297 : : return;
298 : :
299 : 1311 : bf_list_clean(&(*program)->fixups);
300 : 1311 : bf_list_clean(&(*program)->set_groups);
301 : 1311 : bf_vector_clean(&(*program)->img);
302 : :
303 : 1311 : bf_printer_free(&(*program)->printer);
304 : :
305 : 1311 : free(*program);
306 : 1311 : *program = NULL;
307 : : }
308 : :
309 : 0 : void bf_program_dump(const struct bf_program *program, prefix_t *prefix)
310 : : {
311 : : assert(program);
312 : : assert(prefix);
313 : :
314 [ # # ]: 0 : DUMP(prefix, "struct bf_program at %p", program);
315 : :
316 : 0 : bf_dump_prefix_push(prefix);
317 : :
318 [ # # ]: 0 : DUMP(prefix, "handle: struct bf_handle *");
319 : 0 : bf_dump_prefix_push(prefix);
320 : 0 : bf_handle_dump(program->handle, bf_dump_prefix_last(prefix));
321 : 0 : bf_dump_prefix_pop(prefix);
322 : :
323 [ # # ]: 0 : DUMP(prefix, "printer: struct bf_printer *");
324 : 0 : bf_dump_prefix_push(prefix);
325 : 0 : bf_printer_dump(program->printer, prefix);
326 : 0 : bf_dump_prefix_pop(prefix);
327 : :
328 [ # # ]: 0 : DUMP(prefix, "img: %p", program->img.data);
329 [ # # ]: 0 : DUMP(prefix, "img.size: %lu", program->img.size);
330 [ # # ]: 0 : DUMP(prefix, "img.cap: %lu", program->img.cap);
331 : :
332 [ # # ]: 0 : DUMP(prefix, "fixups: bf_list<struct bf_fixup>[%lu]",
333 : : bf_list_size(&program->fixups));
334 : 0 : bf_dump_prefix_push(prefix);
335 [ # # # # : 0 : bf_list_foreach (&program->fixups, fixup_node) {
# # ]
336 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
337 : :
338 [ # # ]: 0 : if (bf_list_is_tail(&program->fixups, fixup_node))
339 : 0 : bf_dump_prefix_last(prefix);
340 : :
341 : 0 : bf_fixup_dump(fixup, prefix);
342 : : }
343 : 0 : bf_dump_prefix_pop(prefix);
344 : :
345 [ # # ]: 0 : DUMP(prefix, "groups: bf_list<struct bf_set_group>[%lu]",
346 : : bf_list_size(&program->set_groups));
347 : 0 : bf_dump_prefix_push(prefix);
348 [ # # # # : 0 : bf_list_foreach (&program->set_groups, group_node) {
# # ]
349 : : const struct bf_set_group *group = bf_list_node_get_data(group_node);
350 : : size_t i = 0;
351 : :
352 [ # # ]: 0 : if (bf_list_is_tail(&program->set_groups, group_node))
353 : 0 : bf_dump_prefix_last(prefix);
354 : :
355 [ # # ]: 0 : DUMP(prefix, "struct bf_set_group at %p (%lu set(s), map=%p)", group,
356 : : bf_list_size(&group->sets), (void *)group->map);
357 : 0 : bf_dump_prefix_push(prefix);
358 [ # # # # ]: 0 : bf_list_foreach (&group->sets, set_node) {
359 : : const struct bf_set *set = bf_list_node_get_data(set_node);
360 : :
361 [ # # ]: 0 : if (bf_list_is_tail(&group->sets, set_node))
362 : 0 : bf_dump_prefix_last(prefix);
363 [ # # # # ]: 0 : DUMP(prefix, "bit %lu: bf_set '%s' (%lu element(s))", i,
364 : : set->name ?: "<anonymous>", bf_hashset_size(&set->elems));
365 [ # # ]: 0 : ++i;
366 : : }
367 : 0 : bf_dump_prefix_pop(prefix);
368 : : }
369 : 0 : bf_dump_prefix_pop(prefix);
370 : :
371 [ # # ]: 0 : DUMP(bf_dump_prefix_last(prefix), "runtime: <anonymous>");
372 : 0 : bf_dump_prefix_push(prefix);
373 [ # # ]: 0 : DUMP(bf_dump_prefix_last(prefix), "ops: %p", program->runtime.ops);
374 : 0 : bf_dump_prefix_pop(prefix);
375 : :
376 : 0 : bf_dump_prefix_pop(prefix);
377 : 0 : }
378 : :
379 : 21635 : static void _bf_program_fixup_insn(struct bpf_insn *insn,
380 : : enum bf_fixup_insn type, int32_t value)
381 : : {
382 [ + + - ]: 21635 : switch (type) {
383 : 4676 : case BF_FIXUP_INSN_OFF:
384 : : assert(!insn->off);
385 : : assert(value < SHRT_MAX);
386 : 4676 : insn->off = (int16_t)value;
387 : 4676 : break;
388 : 16959 : case BF_FIXUP_INSN_IMM:
389 : : assert(!insn->imm);
390 : 16959 : insn->imm = value;
391 : 16959 : break;
392 : 0 : default:
393 [ # # ]: 0 : bf_abort(
394 : : "unsupported fixup instruction type, this should not happen: %d",
395 : : type);
396 : : break;
397 : : }
398 : 21635 : }
399 : :
400 : 6827 : static int _bf_program_fixup(struct bf_program *program,
401 : : enum bf_fixup_type type)
402 : : {
403 : : assert(program);
404 : : assert(type >= 0 && type < _BF_FIXUP_TYPE_MAX);
405 : :
406 [ + - + + : 139004 : bf_list_foreach (&program->fixups, fixup_node) {
+ + ]
407 : : enum bf_fixup_insn insn_type = _BF_FIXUP_INSN_MAX;
408 : : int32_t value;
409 : : size_t offset;
410 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
411 : : struct bpf_insn *insn;
412 : :
413 [ + + ]: 62675 : if (type != fixup->type)
414 : 41040 : continue;
415 : :
416 : 21635 : insn = bf_vector_get(&program->img, fixup->insn);
417 [ - + ]: 21635 : if (!insn) {
418 [ # # ]: 0 : return bf_err_r(-EINVAL,
419 : : "fixup references invalid instruction index %lu",
420 : : fixup->insn);
421 : : }
422 : :
423 [ + + + + : 21635 : switch (type) {
- + + - ]
424 : 4676 : case BF_FIXUP_TYPE_JMP_NEXT_RULE:
425 : : insn_type = BF_FIXUP_INSN_OFF;
426 : 4676 : value = (int)(program->img.size - fixup->insn - 1U);
427 : 4676 : break;
428 : 7001 : case BF_FIXUP_TYPE_COUNTERS_MAP_FD:
429 : : insn_type = BF_FIXUP_INSN_IMM;
430 : 7001 : value = program->handle->cmap->fd;
431 : 7001 : break;
432 : 1338 : case BF_FIXUP_TYPE_PRINTER_MAP_FD:
433 : : insn_type = BF_FIXUP_INSN_IMM;
434 : 1338 : value = program->handle->pmap->fd;
435 : 1338 : break;
436 : 30 : case BF_FIXUP_TYPE_LOG_MAP_FD:
437 : : insn_type = BF_FIXUP_INSN_IMM;
438 : 30 : value = program->handle->lmap->fd;
439 : 30 : break;
440 : 0 : case BF_FIXUP_TYPE_STATE_MAP_FD:
441 : : insn_type = BF_FIXUP_INSN_IMM;
442 : 0 : value = program->handle->smap->fd;
443 : 0 : break;
444 : 277 : case BF_FIXUP_TYPE_SET_MAP_FD: {
445 : : const struct bf_set_group *group =
446 : 277 : _bf_program_find_set_group(program, fixup->attr.set_ptr);
447 [ + - - + ]: 277 : if (!group || !group->map) {
448 [ # # # # : 0 : return bf_err_r(
# # ]
449 : : -ENOENT, "set map fixup: set '%s' not in any loaded group",
450 : : fixup->attr.set_ptr ?
451 : : (fixup->attr.set_ptr->name ?: "<anonymous>") :
452 : : "(null)");
453 : : }
454 : : insn_type = BF_FIXUP_INSN_IMM;
455 : 277 : value = group->map->fd;
456 : 277 : break;
457 : : }
458 : 8313 : case BF_FIXUP_ELFSTUB_CALL:
459 : : insn_type = BF_FIXUP_INSN_IMM;
460 : 8313 : offset = program->elfstubs_location[fixup->attr.elfstub_id] -
461 : 8313 : fixup->insn - 1;
462 [ - + ]: 8313 : if (offset >= INT_MAX)
463 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid ELF stub call offset");
464 : 8313 : value = (int32_t)offset;
465 : 8313 : break;
466 : 0 : default:
467 [ # # ]: 0 : bf_abort("unsupported fixup type, this should not happen: %d",
468 : : type);
469 : : break;
470 : : }
471 : :
472 : 21635 : _bf_program_fixup_insn(insn, insn_type, value);
473 : 21635 : bf_list_delete(&program->fixups, fixup_node);
474 : : }
475 : :
476 : : return 0;
477 : : }
478 : :
479 : 2769 : static int _bf_program_check_proto(struct bf_program *program,
480 : : enum bf_matcher_type type,
481 : : uint32_t *checked_layers)
482 : : {
483 : : const struct bf_matcher_meta *meta;
484 : :
485 : : assert(program);
486 : : assert(checked_layers);
487 : :
488 : 2769 : meta = bf_matcher_get_meta(type);
489 [ - + ]: 2769 : if (!meta)
490 [ # # ]: 0 : return bf_err_r(-EINVAL, "missing meta for matcher type %d", type);
491 : :
492 [ + + ]: 2769 : if (*checked_layers & BF_FLAG(meta->layer))
493 : : return 0;
494 : :
495 : 1877 : if (meta->layer == BF_MATCHER_LAYER_2 ||
496 [ + + ]: 1877 : meta->layer == BF_MATCHER_LAYER_3 ||
497 : : meta->layer == BF_MATCHER_LAYER_4) {
498 : 1230 : int r = bf_stub_rule_check_protocol(program, meta);
499 [ + - ]: 1230 : if (r)
500 : : return r;
501 : 1230 : *checked_layers |= BF_FLAG(meta->layer);
502 : : }
503 : :
504 : : return 0;
505 : : }
506 : :
507 : 1581 : static int _bf_program_generate_rule(struct bf_program *program,
508 : : struct bf_rule *rule)
509 : : {
510 : 1581 : uint32_t checked_layers = 0;
511 : : int ret_code;
512 : : int r = 0;
513 : :
514 : : assert(program);
515 : : assert(rule);
516 : : assert(program->runtime.ops->gen_inline_matcher);
517 : : assert(program->runtime.ops->gen_inline_log);
518 : :
519 [ + + ]: 1581 : if (rule->disabled)
520 : : return 0;
521 : :
522 [ + - + + : 8656 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
523 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
524 : :
525 [ + + ]: 2760 : if (bf_matcher_get_type(matcher) == BF_MATCHER_SET) {
526 : : const struct bf_set *set =
527 : 277 : bf_chain_get_set_for_matcher(program->runtime.chain, matcher);
528 : :
529 [ - + ]: 277 : if (!set) {
530 [ # # ]: 0 : return bf_err_r(-ENOENT, "rule %u references non-existent set",
531 : : rule->index);
532 : : }
533 : :
534 [ + + + - ]: 563 : for (size_t i = 0; i < set->n_comps && !r; ++i) {
535 : 286 : r = _bf_program_check_proto(program, set->key[i],
536 : : &checked_layers);
537 : : }
538 : : } else {
539 : 2483 : r = _bf_program_check_proto(program, bf_matcher_get_type(matcher),
540 : : &checked_layers);
541 : : }
542 : :
543 [ + - ]: 2760 : if (r)
544 : : return r;
545 : : }
546 : :
547 [ + - + + : 8656 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
548 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
549 : :
550 : 2760 : r = program->runtime.ops->gen_inline_matcher(program, matcher);
551 [ + - ]: 2760 : if (r)
552 : : return r;
553 : : }
554 : :
555 [ + + ]: 1568 : if (bf_rule_mark_is_set(rule)) {
556 [ - + ]: 10 : if (!program->runtime.ops->gen_inline_set_mark) {
557 [ # # ]: 0 : return bf_err_r(-ENOTSUP, "set mark is not supported by %s",
558 : : program->runtime.chain->name);
559 : : }
560 : :
561 : 10 : r = program->runtime.ops->gen_inline_set_mark(program,
562 : : bf_rule_mark_get(rule));
563 [ - + ]: 10 : if (r) {
564 [ # # ]: 0 : return bf_err_r(r,
565 : : "failed to generate bytecode to set mark for '%s'",
566 : : program->runtime.chain->name);
567 : : }
568 : : }
569 : :
570 [ + + + + ]: 1568 : if (rule->log && rule->log_rate_ns) {
571 : : // Rate-limited log: check last_log_ts in the state map before logging.
572 : : //
573 : : // R9 (callee-saved) holds the pointer to this rule's state entry
574 : : // across the bpf_ktime_get_ns() call.
575 [ + - ]: 4 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_10,
576 : : BF_PROG_CTX_OFF(state_map)));
577 : 4 : {
578 : : // Outer skip: state_map is NULL (shouldn't happen at runtime,
579 : : // but the verifier requires the NULL check).
580 : 4 : _clean_bf_jmpctx_ struct bf_jmpctx null_ctx =
581 [ + - ]: 4 : bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_9, 0, 0));
582 : :
583 [ + - ]: 4 : if (rule->index > 0) {
584 [ + - ]: 4 : EMIT(program,
585 : : BPF_ALU64_IMM(
586 : : BPF_ADD, BPF_REG_9,
587 : : (int)(rule->index * sizeof(struct bf_rule_state))));
588 : : }
589 : :
590 [ + - ]: 4 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns));
591 : :
592 [ + - ]: 4 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_9, 0));
593 [ + - ]: 4 : EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_0));
594 [ + - ]: 4 : EMIT(program, BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_1));
595 : :
596 : : {
597 : : // Load log_rate_ns as a 64-bit immediate into R1.
598 : 4 : const struct bpf_insn rate_insn[2] = {
599 : 4 : BPF_LD_IMM64(BPF_REG_1, rule->log_rate_ns),
600 : : };
601 [ + - ]: 4 : EMIT(program, rate_insn[0]);
602 [ + - ]: 4 : EMIT(program, rate_insn[1]);
603 : : }
604 : :
605 : : {
606 : : // Inner skip: delta < log_rate_ns means still within window.
607 [ + - ]: 8 : _clean_bf_jmpctx_ struct bf_jmpctx rate_ctx = bf_jmpctx_get(
608 : : program, BPF_JMP_REG(BPF_JLT, BPF_REG_2, BPF_REG_1, 0));
609 : :
610 [ + - ]: 4 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_0, 0));
611 : :
612 : 4 : r = program->runtime.ops->gen_inline_log(program, rule);
613 [ + - ]: 4 : if (r)
614 : : return r;
615 : : }
616 : : }
617 [ + + ]: 1564 : } else if (rule->log) {
618 : 38 : r = program->runtime.ops->gen_inline_log(program, rule);
619 [ + - ]: 38 : if (r)
620 : : return r;
621 : : }
622 : :
623 [ + + ]: 1568 : if (rule->has_counters) {
624 [ + - ]: 1512 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
625 [ + - ]: 1512 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
626 [ + - + - ]: 3024 : EMIT_LOAD_COUNTERS_FD_FIXUP(program, BPF_REG_2);
627 [ + - ]: 1512 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, rule->index));
628 [ + - ]: 1512 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_UPDATE_COUNTERS);
629 : : }
630 : :
631 [ + + - + ]: 1568 : switch (rule->verdict) {
632 : 1516 : case BF_VERDICT_ACCEPT:
633 : : case BF_VERDICT_DROP:
634 : : case BF_VERDICT_NEXT:
635 : 1516 : r = program->runtime.ops->get_verdict(rule->verdict, &ret_code);
636 [ + - ]: 1516 : if (r)
637 : : return r;
638 [ + - ]: 1516 : EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
639 [ + - ]: 1516 : EMIT(program, BPF_EXIT_INSN());
640 : : break;
641 : 12 : case BF_VERDICT_REDIRECT:
642 [ + + ]: 12 : if (!program->runtime.ops->gen_inline_redirect) {
643 [ + - ]: 2 : return bf_err_r(-ENOTSUP, "redirect is not supported by %s hook",
644 : : bf_hook_to_str(program->runtime.chain->hook));
645 : : }
646 : 10 : r = program->runtime.ops->gen_inline_redirect(
647 : : program, rule->redirect_ifindex, rule->redirect_dir);
648 [ + + ]: 10 : if (r)
649 : : return r;
650 : : break;
651 : : case BF_VERDICT_CONTINUE:
652 : : // Fall through to next rule or default chain policy.
653 : : break;
654 : 0 : default:
655 [ # # ]: 0 : bf_abort("unsupported verdict, this should not happen: %d",
656 : : rule->verdict);
657 : : break;
658 : : }
659 : :
660 : 1565 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_JMP_NEXT_RULE);
661 [ - + ]: 1565 : if (r)
662 [ # # ]: 0 : return bf_err_r(r, "failed to generate next rule fixups");
663 : :
664 : : return 0;
665 : : }
666 : :
667 : 1308 : static int _bf_program_generate_elfstubs(struct bf_program *program)
668 : : {
669 : : const struct bf_elfstub *elfstub;
670 : : size_t start_at;
671 : : int r;
672 : :
673 : : assert(program);
674 : :
675 [ + - + + : 36528 : bf_list_foreach (&program->fixups, fixup_node) {
+ + ]
676 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
677 : 16956 : size_t off = program->img.size;
678 : :
679 [ + + ]: 16956 : if (fixup->type != BF_FIXUP_ELFSTUB_CALL)
680 : 8643 : continue;
681 : :
682 : : // Only generate each ELF stub once
683 [ + + ]: 8313 : if (program->elfstubs_location[fixup->attr.elfstub_id])
684 : 5705 : continue;
685 : :
686 [ - + ]: 2608 : bf_dbg("generate ELF stub for ID %d", fixup->attr.elfstub_id);
687 : :
688 : 2608 : elfstub = bf_ctx_get_elfstub(fixup->attr.elfstub_id);
689 [ - + ]: 2608 : if (!elfstub) {
690 [ # # ]: 0 : return bf_err_r(-ENOENT, "no ELF stub found for ID %d",
691 : : fixup->attr.elfstub_id);
692 : : }
693 : :
694 : 2608 : start_at = program->img.size;
695 : :
696 [ + + ]: 98672 : for (size_t i = 0; i < elfstub->ninsns; ++i) {
697 : 96064 : r = bf_program_emit(program, elfstub->insns[i]);
698 [ - + ]: 96064 : if (r)
699 [ # # ]: 0 : return bf_err_r(r, "failed to insert ELF stub instruction");
700 : : }
701 : :
702 [ + + - + : 7892 : bf_list_foreach (&elfstub->strs, pstr_node) {
+ + ]
703 : 1338 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
704 : : struct bf_printk_str *pstr = bf_list_node_get_data(pstr_node);
705 : 1338 : size_t insn_idx = start_at + pstr->insn_idx;
706 : : const struct bf_printer_msg *msg =
707 : 1338 : bf_printer_add_msg(program->printer, pstr->str);
708 : 1338 : struct bpf_insn ld_insn[2] = {
709 : : BPF_LD_MAP_FD(BPF_REG_1, 0),
710 : : };
711 : :
712 : 1338 : ld_insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
713 : 1338 : ld_insn[1].imm = (int)bf_printer_msg_offset(msg);
714 : :
715 : 1338 : r = bf_vector_set(&program->img, insn_idx, &ld_insn[0]);
716 [ - + ]: 1338 : if (r) {
717 [ # # ]: 0 : return bf_err_r(
718 : : r, "failed to set ELF stub instruction at index %lu",
719 : : insn_idx);
720 : : }
721 : 1338 : r = bf_vector_set(&program->img, insn_idx + 1, &ld_insn[1]);
722 [ - + ]: 1338 : if (r) {
723 [ # # ]: 0 : return bf_err_r(
724 : : r, "failed to set ELF stub instruction at index %lu",
725 : : insn_idx + 1);
726 : : }
727 : :
728 : 1338 : r = bf_fixup_new(&fixup, BF_FIXUP_TYPE_PRINTER_MAP_FD, insn_idx,
729 : : NULL);
730 [ + - ]: 1338 : if (r)
731 : : return r;
732 : :
733 : 1338 : r = bf_list_add_tail(&program->fixups, fixup);
734 [ + - ]: 1338 : if (r)
735 : : return r;
736 : :
737 : 1338 : TAKE_PTR(fixup);
738 : : }
739 : :
740 : 2608 : program->elfstubs_location[fixup->attr.elfstub_id] = off;
741 : : }
742 : :
743 : : return 0;
744 : : }
745 : :
746 : 4191 : int bf_program_emit_kfunc_call(struct bf_program *program, const char *name)
747 : : {
748 : : int r;
749 : :
750 : : assert(program);
751 : : assert(name);
752 : :
753 : 4191 : r = bf_btf_get_id(name);
754 [ + - ]: 4191 : if (r < 0)
755 : : return r;
756 : :
757 : 4191 : EMIT(program, ((struct bpf_insn) {.code = BPF_JMP | BPF_CALL,
758 : : .dst_reg = 0,
759 : : .src_reg = BPF_PSEUDO_KFUNC_CALL,
760 : : .off = 0,
761 : : .imm = r}));
762 : :
763 : : return 0;
764 : : }
765 : :
766 : 12001 : int bf_program_emit_fixup(struct bf_program *program, enum bf_fixup_type type,
767 : : struct bpf_insn insn, const union bf_fixup_attr *attr)
768 : : {
769 : 24002 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
770 : : int r;
771 : :
772 : : assert(program);
773 : :
774 [ + - ]: 12001 : EMIT(program, insn);
775 : :
776 : 12001 : r = bf_fixup_new(&fixup, type, program->img.size - 1, attr);
777 [ + - ]: 12001 : if (r)
778 : : return r;
779 : :
780 : 12001 : r = bf_list_add_tail(&program->fixups, fixup);
781 [ + - ]: 12001 : if (r)
782 : : return r;
783 : :
784 : 12001 : TAKE_PTR(fixup);
785 : :
786 : 12001 : return 0;
787 : : }
788 : :
789 : 8326 : int bf_program_emit_fixup_elfstub(struct bf_program *program,
790 : : enum bf_elfstub_id id)
791 : : {
792 : 16652 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
793 : : int r;
794 : :
795 : : assert(program);
796 : :
797 [ + - ]: 8326 : EMIT(program, BPF_CALL_REL(0));
798 : :
799 : 8326 : r = bf_fixup_new(&fixup, BF_FIXUP_ELFSTUB_CALL, program->img.size - 1,
800 : : NULL);
801 [ + - ]: 8326 : if (r)
802 : : return r;
803 : :
804 : 8326 : fixup->attr.elfstub_id = id;
805 : :
806 : 8326 : r = bf_list_add_tail(&program->fixups, fixup);
807 [ + - ]: 8326 : if (r)
808 : : return r;
809 : :
810 : 8326 : TAKE_PTR(fixup);
811 : :
812 : 8326 : return 0;
813 : : }
814 : :
815 : 1311 : int bf_program_generate(struct bf_program *program)
816 : : {
817 : 1311 : const struct bf_chain *chain = program->runtime.chain;
818 : : int ret_code;
819 : : int r;
820 : :
821 : 1311 : r = _bf_program_build_set_groups(program);
822 [ - + ]: 1311 : if (r)
823 [ # # ]: 0 : return bf_err_r(r, "failed to build set groups");
824 : :
825 : : // Save the program's argument into the context.
826 [ + - ]: 1311 : EMIT(program,
827 : : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
828 : :
829 : : // Reset the protocol ID registers
830 [ + - ]: 1311 : EMIT(program, BPF_MOV64_IMM(BPF_REG_7, 0));
831 [ + - ]: 1311 : EMIT(program, BPF_MOV64_IMM(BPF_REG_8, 0));
832 : :
833 : : // If at least one rule logs the matched packets, populate ctx->log_map
834 [ + + ]: 1311 : if (program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG)) {
835 [ + - + - ]: 60 : EMIT_LOAD_LOG_FD_FIXUP(program, BPF_REG_2);
836 [ + - ]: 30 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2,
837 : : BF_PROG_CTX_OFF(log_map)));
838 : : }
839 : :
840 : : // Zeroing IPv6 extension headers
841 [ + + ]: 1311 : if (program->runtime.chain->flags & BF_FLAG(BF_CHAIN_STORE_NEXTHDR)) {
842 [ + - ]: 30 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_7,
843 : : BF_PROG_CTX_OFF(ipv6_eh)));
844 : : }
845 : :
846 : 1311 : r = program->runtime.ops->gen_inline_prologue(program);
847 [ + - ]: 1311 : if (r)
848 : : return r;
849 : :
850 : : // Populate ctx->state_map with the base pointer from the single-entry
851 : : // state map. The key (0) is written to scratch[0..3] temporarily.
852 : : // Placed after gen_inline_prologue so R1-R5 are free: the helper call
853 : : // does not need to be followed by a ctx restore.
854 [ + + ]: 1311 : if (program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG_RATELIMIT)) {
855 [ + - ]: 1 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_SCR_OFF(0), 0));
856 [ + - + - ]: 2 : EMIT_LOAD_STATE_FD_FIXUP(program, BPF_REG_1);
857 [ + - ]: 1 : EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10));
858 [ + - ]: 1 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, BF_PROG_SCR_OFF(0)));
859 [ + - ]: 1 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem));
860 [ + - ]: 1 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0,
861 : : BF_PROG_CTX_OFF(state_map)));
862 : : }
863 : :
864 [ + + + + : 5778 : bf_list_foreach (&chain->rules, rule_node) {
+ + ]
865 : 1581 : r = _bf_program_generate_rule(program,
866 : : bf_list_node_get_data(rule_node));
867 [ + + ]: 1581 : if (r)
868 : : return r;
869 : : }
870 : :
871 : 1308 : r = program->runtime.ops->gen_inline_epilogue(program);
872 [ + - ]: 1308 : if (r)
873 : : return r;
874 : :
875 : : // Call the update counters function
876 : : /// @todo Allow chains to have no counters at all.
877 [ + - ]: 1308 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
878 [ + - ]: 1308 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
879 [ + - + - ]: 2616 : EMIT_LOAD_COUNTERS_FD_FIXUP(program, BPF_REG_2);
880 [ + - ]: 1308 : EMIT(program,
881 : : BPF_MOV32_IMM(BPF_REG_3, bf_program_chain_counter_idx(program)));
882 [ + - ]: 1308 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_UPDATE_COUNTERS);
883 : :
884 : 1308 : r = program->runtime.ops->get_verdict(chain->policy, &ret_code);
885 [ + - ]: 1308 : if (r)
886 : : return r;
887 [ + - ]: 1308 : EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
888 [ + - ]: 1308 : EMIT(program, BPF_EXIT_INSN());
889 : :
890 : 1308 : r = _bf_program_generate_elfstubs(program);
891 [ + - ]: 1308 : if (r)
892 : : return r;
893 : :
894 : 1308 : r = _bf_program_fixup(program, BF_FIXUP_ELFSTUB_CALL);
895 [ - + ]: 1308 : if (r)
896 [ # # ]: 0 : return bf_err_r(r, "failed to generate ELF stub call fixups");
897 : :
898 : : return 0;
899 : : }
900 : :
901 : 1308 : static int _bf_program_load_printer_map(struct bf_program *program)
902 : : {
903 : 1308 : _cleanup_free_ void *pstr = NULL;
904 : : size_t pstr_len;
905 : 1308 : uint32_t key = 0;
906 : : int r;
907 : :
908 : : assert(program);
909 : :
910 : 1308 : r = bf_printer_assemble(program->printer, &pstr, &pstr_len);
911 [ - + ]: 1308 : if (r)
912 [ # # ]: 0 : return bf_err_r(r, "failed to assemble printer map string");
913 : :
914 : 1308 : r = bf_map_new(&program->handle->pmap, _BF_PRINTER_MAP_NAME,
915 : : BF_MAP_TYPE_PRINTER, sizeof(uint32_t), pstr_len, 1);
916 [ - + ]: 1308 : if (r)
917 [ # # ]: 0 : return bf_err_r(r, "failed to create the printer bf_map object");
918 : :
919 : 1308 : r = bf_map_set_elem(program->handle->pmap, &key, pstr);
920 [ - + ]: 1308 : if (r)
921 [ # # ]: 0 : return bf_err_r(r, "failed to set print map elem");
922 : :
923 : 1308 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_PRINTER_MAP_FD);
924 [ - + ]: 1308 : if (r)
925 [ # # ]: 0 : return bf_err_r(r, "failed to fixup printer map FD");
926 : :
927 : : return 0;
928 : : }
929 : :
930 : 1308 : static int _bf_program_load_counters_map(struct bf_program *program)
931 : : {
932 : : int r;
933 : :
934 : : assert(program);
935 : :
936 : 1308 : r = bf_map_new(&program->handle->cmap, _BF_COUNTER_MAP_NAME,
937 : : BF_MAP_TYPE_COUNTERS, sizeof(uint32_t),
938 : : sizeof(struct bf_counter),
939 : 1308 : bf_list_size(&program->runtime.chain->rules) + 2);
940 [ - + ]: 1308 : if (r)
941 [ # # ]: 0 : return bf_err_r(r, "failed to create the counters bf_map object");
942 : :
943 : 1308 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_COUNTERS_MAP_FD);
944 [ - + ]: 1308 : if (r)
945 [ # # ]: 0 : return bf_err_r(r, "failed to fixup counters map FD");
946 : :
947 : : return 0;
948 : : }
949 : :
950 : 1308 : static int _bf_program_load_log_map(struct bf_program *program)
951 : : {
952 : : int r;
953 : :
954 : : assert(program);
955 : :
956 : : // Do not create a log map if it's unused in the chain
957 [ + + ]: 1308 : if (!(program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG)))
958 : : return 0;
959 : :
960 : 30 : r = bf_map_new(&program->handle->lmap, _BF_LOG_MAP_NAME, BF_MAP_TYPE_LOG, 0,
961 : : 0, _BF_LOG_MAP_SIZE);
962 [ - + ]: 30 : if (r)
963 [ # # ]: 0 : return bf_err_r(r, "failed to create the log bf_map object");
964 : :
965 : 30 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_LOG_MAP_FD);
966 [ - + ]: 30 : if (r)
967 [ # # ]: 0 : return bf_err_r(r, "failed to fixup log map FD");
968 : :
969 : : return 0;
970 : : }
971 : :
972 : 1308 : static int _bf_program_load_state_map(struct bf_program *program)
973 : : {
974 : : size_t n_rules;
975 : : int r;
976 : :
977 : : assert(program);
978 : :
979 [ + + ]: 1308 : if (!(program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG_RATELIMIT)))
980 : : return 0;
981 : :
982 : : n_rules = bf_list_size(&program->runtime.chain->rules);
983 [ + - ]: 1 : if (n_rules == 0)
984 : : return 0;
985 : :
986 : 1 : r = bf_map_new(&program->handle->smap, _BF_STATE_MAP_NAME,
987 : : BF_MAP_TYPE_STATE, sizeof(uint32_t),
988 : 1 : n_rules * sizeof(struct bf_rule_state), 1);
989 [ + - ]: 1 : if (r)
990 [ + - ]: 1 : return bf_err_r(r, "failed to create the state bf_map object");
991 : :
992 : 0 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_STATE_MAP_FD);
993 [ # # ]: 0 : if (r)
994 [ # # ]: 0 : return bf_err_r(r, "failed to fixup state map FD");
995 : :
996 : : return 0;
997 : : }
998 : :
999 : 680 : static uint64_t _bf_dedup_hash(const void *data, void *ctx)
1000 : : {
1001 : 680 : return bf_fnv1a(data, *(const size_t *)ctx, bf_fnv1a_init());
1002 : : }
1003 : :
1004 : 31 : static bool _bf_dedup_equal(const void *lhs, const void *rhs, void *ctx)
1005 : : {
1006 : 31 : return memcmp(lhs, rhs, *(const size_t *)ctx) == 0;
1007 : : }
1008 : :
1009 : : /**
1010 : : * @brief Load set maps, one BPF map per `bf_set_group`.
1011 : : *
1012 : : * Hash-keyed sets that share the same key format have already been
1013 : : * grouped by `_bf_program_build_set_groups()`; LPM trie sets each occupy
1014 : : * their own single-set group. Each group collapses to one BPF map whose
1015 : : * value is a bitmask: bit `i` of byte `i / CHAR_BIT` identifies the `i`-th
1016 : : * set in the group.
1017 : : *
1018 : : * Per-group keys and bitmask values are prepared in user space so a single
1019 : : * `bf_bpf_map_update_batch()` call populates the map on the kernel side.
1020 : : *
1021 : : * Group ownership of the created maps is transferred to `handle->sets`;
1022 : : * the `bf_set_group::map` pointer is a non-owning back-reference used by
1023 : : * `_bf_program_fixup()` when resolving `BF_FIXUP_TYPE_SET_MAP_FD` fixups.
1024 : : */
1025 : 1308 : static int _bf_program_load_sets_maps(struct bf_program *new_prog)
1026 : : {
1027 : : char name[BPF_OBJ_NAME_LEN];
1028 : : size_t map_idx = 0;
1029 : : int r;
1030 : :
1031 : : assert(new_prog);
1032 : :
1033 [ + + + + : 3220 : bf_list_foreach (&new_prog->set_groups, group_node) {
+ + ]
1034 : : struct bf_set_group *group = bf_list_node_get_data(group_node);
1035 : : const struct bf_set *key_set =
1036 : : bf_list_node_get_data(bf_list_get_head(&group->sets));
1037 : : size_t n_sets = bf_list_size(&group->sets);
1038 : : size_t i = 0;
1039 : : _cleanup_free_ uint8_t *keys = NULL;
1040 : 302 : size_t key_size = key_set->elem_size;
1041 : : _cleanup_free_ uint8_t *values = NULL;
1042 : 302 : size_t value_size = (n_sets + CHAR_BIT - 1) / CHAR_BIT;
1043 : : const bf_hashset_ops dedup_ops = {
1044 : : .hash = _bf_dedup_hash,
1045 : : .equal = _bf_dedup_equal,
1046 : : // The dedup hashset borrows elements from `bf_set`s.
1047 : : .free = NULL,
1048 : : };
1049 : 302 : _clean_bf_hashset_ bf_hashset unique_elements =
1050 : : bf_hashset_default(&dedup_ops, &key_size);
1051 : : size_t n_total_elems = 0;
1052 : : size_t n_unique_elems;
1053 : 0 : _free_bf_map_ struct bf_map *new_map = NULL;
1054 : : struct bf_map *map_ref;
1055 : :
1056 : : // Upper-bound the set capacity to avoid incremental rehashing.
1057 [ + + ]: 650 : bf_list_foreach (&group->sets, set_node) {
1058 : : const struct bf_set *set = bf_list_node_get_data(set_node);
1059 : :
1060 [ + + ]: 325 : n_total_elems += bf_hashset_size(&set->elems);
1061 : : }
1062 : :
1063 : 302 : r = bf_hashset_reserve(&unique_elements, n_total_elems);
1064 [ - + ]: 302 : if (r)
1065 [ # # ]: 0 : return bf_err_r(r, "failed to reserve dedup hashset capacity");
1066 : :
1067 : : // Find all unique elements across all sets in this group.
1068 [ + - + + : 1254 : bf_list_foreach (&group->sets, set_node) {
+ + ]
1069 : : const struct bf_set *set = bf_list_node_get_data(set_node);
1070 : :
1071 [ + - + + : 2010 : bf_hashset_foreach (&set->elems, elem) {
+ + ]
1072 : 680 : void *to_add = elem->data;
1073 : 680 : r = bf_hashset_add(&unique_elements, &to_add);
1074 [ - + ]: 680 : if (r && r != -EEXIST)
1075 [ # # ]: 0 : return bf_err_r(r, "failed to dedup element");
1076 : : }
1077 : : }
1078 : 302 : n_unique_elems = bf_hashset_size(&unique_elements);
1079 : :
1080 : : // Compute bf_map keys and values for batch insertion.
1081 : 302 : keys = calloc(n_unique_elems, key_size);
1082 [ - + ]: 302 : if (!keys)
1083 [ # # ]: 0 : return bf_err_r(-ENOMEM, "failed to allocate map keys");
1084 : :
1085 : 302 : values = calloc(n_unique_elems, value_size);
1086 [ - + ]: 302 : if (!values)
1087 [ # # ]: 0 : return bf_err_r(-ENOMEM, "failed to allocate map values");
1088 : :
1089 [ + - + + ]: 1646 : bf_hashset_foreach (&unique_elements, hentry) {
1090 : : size_t bit_idx = 0;
1091 : :
1092 : : // Compute the key.
1093 : 672 : memcpy(keys + (i * key_size), hentry->data, key_size);
1094 : :
1095 : : // Compute the value (bitmask).
1096 [ + - + + ]: 2274 : bf_list_foreach (&group->sets, set_node) {
1097 : : const struct bf_set *set = bf_list_node_get_data(set_node);
1098 : :
1099 [ + + ]: 801 : if (bf_hashset_contains(&set->elems, hentry->data)) {
1100 : 680 : values[(i * value_size) + (bit_idx / CHAR_BIT)] |=
1101 : 680 : (uint8_t)(1U << (bit_idx % CHAR_BIT));
1102 : : }
1103 [ + + ]: 801 : ++bit_idx;
1104 : : }
1105 [ + + ]: 672 : ++i;
1106 : : }
1107 : :
1108 : : // Create the BPF map from the computed keys and values.
1109 : 302 : (void)snprintf(name, BPF_OBJ_NAME_LEN, _BF_SET_MAP_PREFIX "%04x",
1110 : 302 : (uint16_t)map_idx++);
1111 : :
1112 : 302 : r = bf_map_new_from_set(&new_map, name, key_set, n_unique_elems,
1113 : : value_size);
1114 [ + - ]: 302 : if (r)
1115 : : return r;
1116 : :
1117 : 302 : r = bf_bpf_map_update_batch(new_map->fd, keys, values, n_unique_elems,
1118 : : BPF_ANY);
1119 [ - + ]: 302 : if (r)
1120 [ # # ]: 0 : return bf_err_r(r, "failed to add set elements to the map");
1121 : :
1122 : 302 : map_ref = new_map;
1123 : 302 : r = bf_list_push(&new_prog->handle->sets, (void **)&new_map);
1124 [ + - ]: 302 : if (r)
1125 : : return r;
1126 : 302 : group->map = map_ref;
1127 : : }
1128 : :
1129 : 1308 : return _bf_program_fixup(new_prog, BF_FIXUP_TYPE_SET_MAP_FD);
1130 : : }
1131 : :
1132 : 1308 : int bf_program_load(struct bf_program *prog)
1133 : : {
1134 : : _cleanup_free_ char *log_buf = NULL;
1135 : : int r;
1136 : :
1137 : : assert(prog);
1138 : :
1139 : 1308 : r = _bf_program_load_sets_maps(prog);
1140 [ - + ]: 1308 : if (r)
1141 [ # # ]: 0 : return bf_err_r(r, "failed to load the sets map");
1142 : :
1143 : 1308 : r = _bf_program_load_counters_map(prog);
1144 [ - + ]: 1308 : if (r)
1145 [ # # ]: 0 : return bf_err_r(r, "failed to load the counter map");
1146 : :
1147 : 1308 : r = _bf_program_load_printer_map(prog);
1148 [ - + ]: 1308 : if (r)
1149 [ # # ]: 0 : return bf_err_r(r, "failed to load the printer map");
1150 : :
1151 : 1308 : r = _bf_program_load_log_map(prog);
1152 [ - + ]: 1308 : if (r)
1153 [ # # ]: 0 : return bf_err_r(r, "failed to load the log map");
1154 : :
1155 : 1308 : r = _bf_program_load_state_map(prog);
1156 [ + + ]: 1308 : if (r)
1157 [ + - ]: 1 : return bf_err_r(r, "failed to load the state map");
1158 : :
1159 [ + + ]: 1307 : if (bf_ctx_is_verbose(BF_VERBOSE_DEBUG)) {
1160 : 1 : log_buf = malloc(_BF_LOG_BUF_SIZE);
1161 [ - + ]: 1 : if (!log_buf) {
1162 [ # # ]: 0 : return bf_err_r(-ENOMEM,
1163 : : "failed to allocate BPF_PROG_LOAD logs buffer");
1164 : : }
1165 : : }
1166 : :
1167 [ - + ]: 1307 : if (bf_ctx_is_verbose(BF_VERBOSE_BYTECODE))
1168 : 0 : bf_program_dump_bytecode(prog);
1169 : :
1170 [ + + ]: 2614 : r = bf_bpf_prog_load(prog->handle->prog_name,
1171 : 1307 : bf_hook_to_bpf_prog_type(prog->runtime.chain->hook),
1172 : : prog->img.data, prog->img.size,
1173 : 1307 : bf_hook_to_bpf_attach_type(prog->runtime.chain->hook),
1174 : : log_buf, log_buf ? _BF_LOG_BUF_SIZE : 0,
1175 : 1307 : bf_ctx_token(), &prog->handle->prog_fd);
1176 [ - + ]: 1307 : if (r) {
1177 [ # # # # ]: 0 : return bf_err_r(r, "failed to load bf_program (%lu insns):\n%s\nerrno:",
1178 : : prog->img.size, log_buf ? log_buf : "<NO LOG BUFFER>");
1179 : : }
1180 : :
1181 : : return r;
1182 : : }
1183 : :
1184 : 0 : int bf_program_get_counter(const struct bf_program *program,
1185 : : uint32_t counter_idx, struct bf_counter *counter)
1186 : : {
1187 : : assert(program);
1188 : : assert(counter);
1189 : :
1190 : 0 : return bf_handle_get_counter(program->handle, counter_idx, counter);
1191 : : }
1192 : :
1193 : 1308 : size_t bf_program_chain_counter_idx(const struct bf_program *program)
1194 : : {
1195 : 1308 : return bf_list_size(&program->runtime.chain->rules);
1196 : : }
1197 : :
1198 : 4191 : size_t bf_program_error_counter_idx(const struct bf_program *program)
1199 : : {
1200 : 4191 : return bf_list_size(&program->runtime.chain->rules) + 1;
1201 : : }
|