LCOV - code coverage report
Current view: top level - libbpfilter/cgen - program.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 81.4 % 478 389
Test Date: 2026-10-13 09:41:22 Functions: 93.3 % 30 28
Branches: 51.8 % 573 297

             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                 :             : }
        

Generated by: LCOV version 2.0-1