LCOV - code coverage report
Current view: top level - libbpfilter/cgen - program.h (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 100.0 % 1 1
Test Date: 2026-04-17 15:45:04 Functions: - 0 0
Branches: - 0 0

             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                 :             : #pragma once
       7                 :             : 
       8                 :             : #include <stddef.h>
       9                 :             : #include <stdint.h>
      10                 :             : 
      11                 :             : #include <bpfilter/chain.h>
      12                 :             : #include <bpfilter/core/list.h>
      13                 :             : #include <bpfilter/core/vector.h>
      14                 :             : #include <bpfilter/dump.h>
      15                 :             : #include <bpfilter/elfstub.h>
      16                 :             : #include <bpfilter/flavor.h>
      17                 :             : #include <bpfilter/helper.h>
      18                 :             : #include <bpfilter/pack.h>
      19                 :             : 
      20                 :             : #include "cgen/fixup.h"
      21                 :             : #include "cgen/printer.h"
      22                 :             : #include "cgen/runtime.h"
      23                 :             : #include "filter.h"
      24                 :             : 
      25                 :             : /**
      26                 :             :  * @file program.h
      27                 :             :  *
      28                 :             :  * @ref bf_program is used to represent a BPF program. It contains the BPF
      29                 :             :  * bytecode, as well as the required maps and metadata.
      30                 :             :  *
      31                 :             :  * **Workflow**
      32                 :             :  *
      33                 :             :  * The program is composed of different steps:
      34                 :             :  * 1. Initialize the generic context
      35                 :             :  * 2. Preprocess the packet's headers: gather information about the packet's
      36                 :             :  *    size, the protocols available, the input interface...
      37                 :             :  * 3. Execute the filtering rules: execute all the rules defined in the program
      38                 :             :  *    sequentially. If a rule matches the packet, apply its verdict and return.
      39                 :             :  * 4. Apply the policy if no rule matched: if no rule matched the packet, return
      40                 :             :  *    the chain's policy (default action).
      41                 :             :  *
      42                 :             :  * **Memory layout**
      43                 :             :  *
      44                 :             :  * The program will use the BPF registers to following way:
      45                 :             :  * - @c r0 : return value
      46                 :             :  * - @c r1 to @c r5 (included): general purpose registers
      47                 :             :  * - @c r6 : address of the header currently filtered on
      48                 :             :  * - @c r7 : L3 protocol ID
      49                 :             :  * - @c r8 : L4 protocol ID
      50                 :             :  * - @c r9 : unused
      51                 :             :  * - @c r10 : frame pointer
      52                 :             :  *
      53                 :             :  * This convention is followed throughout the project and must be followed all
      54                 :             :  * the time to prevent incompatibilities. Debugging this kind of issues is not
      55                 :             :  * fun, so stick to it.
      56                 :             :  *
      57                 :             :  * @warning L3 and L4 protocol IDs **must** be stored in registers, no on the
      58                 :             :  * stack, as older verifier aren't able to keep track of scalar values located
      59                 :             :  * on the stack. This means the verification will fail because the verifier
      60                 :             :  * can't verify branches properly.
      61                 :             :  *
      62                 :             :  * `bf_runtime` is used to represent the layout of the first stack frame in the
      63                 :             :  * program. It is filled during preprocessing and contains data required for
      64                 :             :  * packet filtering.
      65                 :             :  *
      66                 :             :  * **About preprocessing**
      67                 :             :  *
      68                 :             :  * The packets are preprocessed according to the program type (i.e. BPF flavor).
      69                 :             :  * Each flavor needs to perform the following steps during preprocessing:
      70                 :             :  * - Store the packet size and the input interface index into the runtime context
      71                 :             :  * - Create a BPF dynamic pointer for the packet
      72                 :             :  * - Preprocess the L2, L3, and L4 headers
      73                 :             :  *
      74                 :             :  * The header's preprocessing is required to discover the protocols used in the
      75                 :             :  * packet: processing L2 will provide us with information about L3, and so on. The
      76                 :             :  * logic used to process layer X is responsible for discovering layer X+1: the L2
      77                 :             :  * header preprocessing logic will discover the L3 protocol ID. When processing
      78                 :             :  * layer X, if the protocol is not supported, the protocol ID is reset to 0 (so
      79                 :             :  * we won't execute the rules for this layer) and subsequent layers are not
      80                 :             :  * processed (because we can't discover their protocol).
      81                 :             :  *
      82                 :             :  * For example, assuming IPv6 and TCP are the only supported protocols:
      83                 :             :  * - L2 processing: discover the packet's ethertype (IPv6), and store it into
      84                 :             :  *   @c r7 .
      85                 :             :  * - L3 processing: the protocol ID in @c r7 is supported (IPv6), so a slice is
      86                 :             :  *   created, and the L4 protocol ID is read from the IPV6 header into @c r8 .
      87                 :             :  * - L4 processing: the protocol ID in @c r8 is supported (TCP), so a slice
      88                 :             :  *   is created.
      89                 :             :  * - The program can now start executing the rules.
      90                 :             :  *
      91                 :             :  * However, assuming only IPv6 and UDP are supported:
      92                 :             :  * - L2 processing: discover the packet's ethertype (IPv6), and store it into
      93                 :             :  *   @c r7 .
      94                 :             :  * - L3 processing: the protocol ID in @c r7 is supported (IPv6), so a slice is
      95                 :             :  *   created, and the L4 protocol ID is read from the IPV6 header into @c r8 .
      96                 :             :  * - L4 processing: the protocol ID in @c r8 is no supported (TCP), @c r8 is
      97                 :             :  *   set to 0 and we stop processing this layer.
      98                 :             :  * - The program can now start executing the rules. No layer 4 rule will be
      99                 :             :  *   executed as @c r8 won't match any protocol ID.
     100                 :             :  */
     101                 :             : 
     102                 :             : #define EMIT(program, x)                                                       \
     103                 :             :     ({                                                                         \
     104                 :             :         int __r = bf_program_emit((program), (x));                             \
     105                 :             :         if (__r < 0)                                                           \
     106                 :             :             return __r;                                                        \
     107                 :             :     })
     108                 :             : 
     109                 :             : #define EMIT_KFUNC_CALL(program, function)                                     \
     110                 :             :     ({                                                                         \
     111                 :             :         int __r = bf_program_emit_kfunc_call((program), (function));           \
     112                 :             :         if (__r < 0)                                                           \
     113                 :             :             return __r;                                                        \
     114                 :             :     })
     115                 :             : 
     116                 :             : #define EMIT_FIXUP(program, type, insn)                                        \
     117                 :             :     ({                                                                         \
     118                 :             :         int __r = bf_program_emit_fixup((program), (type), (insn), NULL);      \
     119                 :             :         if (__r < 0)                                                           \
     120                 :             :             return __r;                                                        \
     121                 :             :     })
     122                 :             : 
     123                 :             : #define EMIT_FIXUP_ELFSTUB(program, elfstub_id)                                \
     124                 :             :     ({                                                                         \
     125                 :             :         int __r = bf_program_emit_fixup_elfstub((program), (elfstub_id));      \
     126                 :             :         if (__r < 0)                                                           \
     127                 :             :             return __r;                                                        \
     128                 :             :     })
     129                 :             : 
     130                 :             : #define EMIT_FIXUP_JMP_NEXT_RULE(program, insn)                                \
     131                 :             :     ({                                                                         \
     132                 :             :         int __r = bf_program_emit_fixup(                                       \
     133                 :             :             (program), BF_FIXUP_TYPE_JMP_NEXT_RULE, (insn), NULL);             \
     134                 :             :         if (__r < 0)                                                           \
     135                 :             :             return __r;                                                        \
     136                 :             :     })
     137                 :             : 
     138                 :             : #define EMIT_LOAD_COUNTERS_FD_FIXUP(program, reg)                              \
     139                 :             :     ({                                                                         \
     140                 :             :         const struct bpf_insn ld_insn[2] = {BPF_LD_MAP_FD(reg, 0)};            \
     141                 :             :         int __r = bf_program_emit_fixup(                                       \
     142                 :             :             (program), BF_FIXUP_TYPE_COUNTERS_MAP_FD, ld_insn[0], NULL);       \
     143                 :             :         if (__r < 0)                                                           \
     144                 :             :             return __r;                                                        \
     145                 :             :         __r = bf_program_emit((program), ld_insn[1]);                          \
     146                 :             :         if (__r < 0)                                                           \
     147                 :             :             return __r;                                                        \
     148                 :             :     })
     149                 :             : 
     150                 :             : #define EMIT_LOAD_LOG_FD_FIXUP(program, reg)                                   \
     151                 :             :     ({                                                                         \
     152                 :             :         const struct bpf_insn ld_insn[2] = {BPF_LD_MAP_FD(reg, 0)};            \
     153                 :             :         int __r = bf_program_emit_fixup((program), BF_FIXUP_TYPE_LOG_MAP_FD,   \
     154                 :             :                                         ld_insn[0], NULL);                     \
     155                 :             :         if (__r < 0)                                                           \
     156                 :             :             return __r;                                                        \
     157                 :             :         __r = bf_program_emit((program), ld_insn[1]);                          \
     158                 :             :         if (__r < 0)                                                           \
     159                 :             :             return __r;                                                        \
     160                 :             :     })
     161                 :             : 
     162                 :             : /**
     163                 :             :  * Load a specific set's file descriptor.
     164                 :             :  *
     165                 :             :  * @note Similarly to every @c EMIT_* macro, it must be called from a function
     166                 :             :  * returning an @c int , if the call fails, the macro will return a negative
     167                 :             :  * errno value.
     168                 :             :  *
     169                 :             :  * @param program Program to generate the bytecode for. Can't be NULL.
     170                 :             :  * @param reg Register to store the set file descriptor in.
     171                 :             :  * @param index Index of the set in the program.
     172                 :             :  */
     173                 :             : #define EMIT_LOAD_SET_FD_FIXUP(program, reg, index)                            \
     174                 :             :     ({                                                                         \
     175                 :             :         union bf_fixup_attr __attr;                                            \
     176                 :             :         memset(&__attr, 0, sizeof(__attr));                                    \
     177                 :             :         __attr.set_index = (index);                                            \
     178                 :             :         const struct bpf_insn ld_insn[2] = {BPF_LD_MAP_FD(reg, 0)};            \
     179                 :             :         int __r = bf_program_emit_fixup((program), BF_FIXUP_TYPE_SET_MAP_FD,   \
     180                 :             :                                         ld_insn[0], &__attr);                  \
     181                 :             :         if (__r < 0)                                                           \
     182                 :             :             return __r;                                                        \
     183                 :             :         __r = bf_program_emit((program), ld_insn[1]);                          \
     184                 :             :         if (__r < 0)                                                           \
     185                 :             :             return __r;                                                        \
     186                 :             :     })
     187                 :             : 
     188                 :             : struct bf_chain;
     189                 :             : struct bf_counter;
     190                 :             : struct bf_hookopts;
     191                 :             : struct bf_handle;
     192                 :             : 
     193                 :             : struct bf_program
     194                 :             : {
     195                 :             :     enum bf_flavor flavor;
     196                 :             : 
     197                 :             :     /// Log messages printer
     198                 :             :     struct bf_printer *printer;
     199                 :             : 
     200                 :             :     /** Handle containing BPF object references (prog_fd, maps, link).
     201                 :             :      * Non-owning pointer, passed in from `bf_cgen` via `bf_program_new()`.
     202                 :             :      * Populated during load/attach. */
     203                 :             :     struct bf_handle *handle;
     204                 :             : 
     205                 :             :     /* Bytecode */
     206                 :             :     uint32_t elfstubs_location[_BF_ELFSTUB_MAX];
     207                 :             :     bf_vector img;
     208                 :             :     bf_list fixups;
     209                 :             : 
     210                 :             :     /** Runtime data used to interact with the program and cache information.
     211                 :             :      * This data is not serialized. */
     212                 :             :     struct
     213                 :             :     {
     214                 :             :         /** Hook-specific ops to use to generate the program. */
     215                 :             :         const struct bf_flavor_ops *ops;
     216                 :             : 
     217                 :             :         /** Chain the program is generated from. This is a non-owning pointer:
     218                 :             :          * the @ref bf_program doesn't have to manage its lifetime. */
     219                 :             :         const struct bf_chain *chain;
     220                 :             :     } runtime;
     221                 :             : };
     222                 :             : 
     223                 :             : #define _free_bf_program_ __attribute__((__cleanup__(bf_program_free)))
     224                 :             : 
     225                 :             : /**
     226                 :             :  * @brief Allocate and initialize a new `bf_program` object.
     227                 :             :  *
     228                 :             :  * @param program `bf_program` object to allocate and initialize. Can't be NULL.
     229                 :             :  * @param chain Chain the program is generated from. Can't be NULL.
     230                 :             :  * @param handle Handle to store BPF object references in. The program borrows
     231                 :             :  *        this pointer (non-owning). Can't be NULL.
     232                 :             :  * @return 0 on success, or a negative error value on failure.
     233                 :             :  */
     234                 :             : int bf_program_new(struct bf_program **program, const struct bf_chain *chain,
     235                 :             :                    struct bf_handle *handle);
     236                 :             : 
     237                 :             : void bf_program_free(struct bf_program **program);
     238                 :             : 
     239                 :             : void bf_program_dump(const struct bf_program *program, prefix_t *prefix);
     240                 :             : 
     241                 :             : static inline int bf_program_emit(struct bf_program *program,
     242                 :             :                                   struct bpf_insn insn)
     243                 :             : {
     244                 :             :     assert(program);
     245                 :             : 
     246                 :      269870 :     return bf_vector_add(&program->img, &insn);
     247                 :             : }
     248                 :             : 
     249                 :             : int bf_program_emit_kfunc_call(struct bf_program *program, const char *name);
     250                 :             : int bf_program_emit_fixup(struct bf_program *program, enum bf_fixup_type type,
     251                 :             :                           struct bpf_insn insn,
     252                 :             :                           const union bf_fixup_attr *attr);
     253                 :             : int bf_program_emit_fixup_elfstub(struct bf_program *program,
     254                 :             :                                   enum bf_elfstub_id id);
     255                 :             : int bf_program_generate(struct bf_program *program);
     256                 :             : 
     257                 :             : /**
     258                 :             :  * Load the BPF program into the kernel.
     259                 :             :  *
     260                 :             :  * Prior to loading the BPF program, multiple BPF maps are created to store
     261                 :             :  * the counters, the debug strings, and the sets. If the program can't be
     262                 :             :  * loaded, all the maps are destroyed.
     263                 :             :  *
     264                 :             :  * Once the loading succeeds, the program and the maps are pinned to the
     265                 :             :  * filesystem. If the BPF objects can't be pinned, the program is unloaded and
     266                 :             :  * the maps destroyed.
     267                 :             :  *
     268                 :             :  * @param prog Program to load into the kernel. Can't be NULL and must contain
     269                 :             :  *        instructions.
     270                 :             :  * @return 0 on success, or negative errno value on failure.
     271                 :             :  */
     272                 :             : int bf_program_load(struct bf_program *prog);
     273                 :             : 
     274                 :             : int bf_program_get_counter(const struct bf_program *program,
     275                 :             :                            uint32_t counter_idx, struct bf_counter *counter);
     276                 :             : int bf_program_set_counters(struct bf_program *program,
     277                 :             :                             const struct bf_counter *counters);
     278                 :             : 
     279                 :             : size_t bf_program_chain_counter_idx(const struct bf_program *program);
     280                 :             : size_t bf_program_error_counter_idx(const struct bf_program *program);
        

Generated by: LCOV version 2.0-1