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