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