github.com/cilium/cilium@v1.16.2/Documentation/contributing/testing/bpf.rst (about)

     1  .. only:: not (epub or latex or html)
     2  
     3      WARNING: You are looking at unreleased Cilium documentation.
     4      Please use the official rendered version released here:
     5      https://docs.cilium.io
     6  
     7  .. _bpf_testing:
     8  
     9  ********************************
    10  BPF Unit and Integration Testing
    11  ********************************
    12  
    13  Our BPF datapath has its own test framework, which allows us to write unit and integration tests that
    14  verify that our BPF code works as intended, independently from the other Cilium components. The
    15  framework uses the ``BPF_PROG_RUN`` feature to run eBPF programs in the kernel without attaching
    16  them to actual hooks.
    17  
    18  The framework is designed to allow datapath developers to quickly write tests
    19  for the code they are working on. The tests themselves are fully written in C to minimize context
    20  switching. Tests pass results back to the framework which will outputs the results in Go test output,
    21  for optimal integration with CI and other tools.
    22  
    23  Running tests
    24  =============
    25  
    26  To run the tests in your local environment, execute the following command from the project root:
    27  
    28  .. code-block:: shell-session
    29  
    30      $ make run_bpf_tests
    31  
    32  .. note::
    33  
    34      Running BPF tests requires Docker and is only expected to work on Linux.
    35  
    36  To run a single test, specify its name without extension. For example:
    37  
    38      $ make run_bpf_tests BPF_TEST_FILE="xdp_nodeport_lb4_nat_lb"
    39  
    40  Writing tests
    41  =============
    42  
    43  All BPF tests live in the ``bpf/tests`` directory. All ``.c`` files in this directory are assumed to
    44  contain BPF test programs which can be independently compiled, loaded, and executed using
    45  ``BPF_PROG_RUN``. All files in this directory are automatically picked up, so all you have to do is
    46  create a new ``.c`` file and start writing. All other files like ``.h`` files are ignored and can be
    47  used for sharing code for example.
    48  
    49  Each ``.c`` file must at least have one ``CHECK`` program. The ``CHECK`` macro replaces the ``SEC`` which is
    50  typically used in BPF programs. The ``CHECK`` macro takes two arguments, the first being the program
    51  type (for example ``xdp`` or ``tc``. See `the list of recognized types in the Go library
    52  <https://github.com/cilium/ebpf/blob/v0.13.2/elf_sections.go#L9>`__),
    53  the second being the name of the test which will appear in the output. All macros are defined in
    54  ``bpf/tests/common.h``, so all programs should start by including this file: ``#include "common.h"``.
    55  
    56  Each ``CHECK`` program should start with ``test_init()`` and end with ``test_finish()``, ``CHECK`` programs
    57  will return implicitly with the result of the test, a user doesn't need to add ``return`` statements
    58  to the code manually. A test will PASS if it reaches ``test_finish()``, unless it is marked as
    59  failed(``test_fail()``, ``test_fail_now()``, ``test_fatal()``) or skipped(``test_skip()``, ``test_skip_now()``).
    60  
    61  The name of the function has no significance for the tests themselves. The function names are still
    62  used as indicators in the kernel (at least the first 15 chars), used to populate tail call maps,
    63  and should be unique for the purposes of compilation.
    64  
    65  .. code-block:: c
    66  
    67      #include "common.h"
    68  
    69      CHECK("xdp", "nodeport-lb4")
    70      int nodeportLB4(struct __ctx_buff *ctx)
    71      {
    72  	    test_init();
    73  
    74          /* ensure preconditions are met */
    75          /* call the functions you would like to test */
    76          /* check that everything works as expected */
    77  
    78          test_finish();
    79      }
    80  
    81  Sub-tests
    82  ---------
    83  
    84  Each ``CHECK`` program may contain sub-tests, each of which has its own test status. A sub-test is
    85  created with the ``TEST`` macro like so:
    86  
    87  .. code-block:: c
    88  
    89      #include "common.h"
    90  
    91      #include <bpf/ctx/xdp.h>
    92      #include <lib/jhash.h>
    93      #include "bpf/section.h"
    94  
    95      CHECK("xdp", "jhash")
    96      int bpf_test(__maybe_unused struct xdp_md *ctx)
    97      {
    98          test_init();
    99  
   100          TEST("Non-zero", {
   101              unsigned int hash = jhash_3words(123, 234, 345, 456);
   102  
   103              if (hash != 2698615579)
   104                  test_fatal("expected '2698615579' got '%lu'", hash);
   105          });
   106  
   107          TEST("Zero", {
   108              unsigned int hash = jhash_3words(0, 0, 0, 0);
   109  
   110              if (hash != 459859287)
   111                  test_fatal("expected '459859287' got '%lu'", hash);
   112          });
   113  
   114          test_finish();
   115      }
   116  
   117  Since all sub-tests are part of the same BPF program they are executed consecutively in one
   118  ``BPF_PROG_RUN`` invocation and can share setup code which can improve run speed and reduce code duplication.
   119  The name passed to the ``TEST`` macro for each sub-test serves to self-document the steps and makes it easier to spot what part of a test fails.
   120  
   121  Integration tests
   122  -----------------
   123  
   124  Writing tests for a single function or small group of functions should be fairly straightforward,
   125  only requiring a ``CHECK`` program. Testing functionality across tail calls requires an additional step:
   126  given that the program does not return to the ``CHECK`` function after making a tail call, we can't check whether it was successful.
   127  
   128  The workaround is to use ``PKTGEN`` and ``SETUP`` programs in addition to a ``CHECK`` program.
   129  These programs will run before the ``CHECK`` program with the same name.
   130  Intended usage is that the ``PKGTEN`` program builds a BPF context (for example fill a ``struct __sk_buff`` for TC programs), and passes it on
   131  to the ``SETUP`` program, which performs further setup steps (for example fill a BPF map). The two-stage pattern is needed so that ``BPF_PROG_RUN`` gets
   132  invoked with the actual packet content (and for example fills ``skb->protocol``).
   133  
   134  The BPF context is then passed to the ``CHECK`` program, which can inspect the result. By executing the test setup and executing the tail
   135  call in ``SETUP`` we can execute complete programs.  The return code of the ``SETUP`` program is prepended as a ``u32`` to the start of the
   136  packet data passed to ``CHECK``, meaning that the ``CHECK`` program will find the actual packet data at ``(void *)data + 4``.
   137  
   138  This is an abbreviated example showing the key components:
   139  
   140  .. code-block:: c
   141  
   142      #include "common.h"
   143  
   144      #include "bpf/ctx/xdp.h"
   145      #include "bpf_xdp.c"
   146  
   147      struct {
   148          __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
   149          __uint(key_size, sizeof(__u32));
   150          __uint(max_entries, 2);
   151          __array(values, int());
   152      } entry_call_map __section(".maps") = {
   153          .values = {
   154              [0] = &cil_xdp_entry,
   155          },
   156      };
   157  
   158      PKTGEN("xdp", "l2_example")
   159      int test1_pktgen(struct __ctx_buff *ctx)
   160      {
   161          /* Create room for our packet to be crafted */
   162          unsigned int data_len = ctx->data_end - ctx->data;
   163          int offset = offset = sizeof(struct ethhdr) - data_len;
   164          bpf_xdp_adjust_tail(ctx, offset);
   165  
   166          void *data = (void *)(long)ctx->data;
   167          void *data_end = (void *)(long)ctx->data_end;
   168  
   169          if (data + sizeof(struct ethhdr) > data_end)
   170              return TEST_ERROR;
   171  
   172          /* Writing just the L2 header for brevity */
   173          struct ethhdr l2 = {
   174              .h_source = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
   175              .h_dest = {0x12, 0x23, 0x34, 0x45, 0x56, 0x67},
   176              .h_proto = bpf_htons(ETH_P_IP)
   177          };
   178          memcpy(data, &l2, sizeof(struct ethhdr));
   179  
   180          return 0;
   181      }
   182  
   183      SETUP("xdp", "l2_example")
   184      int test1_setup(struct __ctx_buff *ctx)
   185      {
   186          /* OMITTED setting up map state */
   187  
   188          /* Jump into the entrypoint */
   189          tail_call_static(ctx, entry_call_map, 0);
   190          /* Fail if we didn't jump */
   191          return TEST_ERROR;
   192      }
   193  
   194      CHECK("xdp", "l2_example")
   195      int test1_check(__maybe_unused const struct __ctx_buff *ctx)
   196      {
   197          test_init();
   198  
   199          void *data = (void *)(long)ctx->data;
   200          void *data_end = (void *)(long)ctx->data_end;
   201  
   202          if (data + sizeof(__u32) > data_end)
   203              test_fatal("status code out of bounds");
   204  
   205          __u32 *status_code = data;
   206  
   207          if (*status_code != XDP_TX)
   208              test_fatal("status code != XDP_TX");
   209  
   210          data += sizeof(__u32);
   211  
   212          if (data + sizeof(struct ethhdr) > data_end)
   213              test_fatal("ctx doesn't fit ethhdr");
   214  
   215          struct ethhdr *l2 = data;
   216  
   217          data += sizeof(struct ethhdr);
   218  
   219          if (memcmp(l2->h_source, fib_smac, sizeof(fib_smac)))
   220              test_fatal("l2->h_source != fib_smac");
   221  
   222          if (memcmp(l2->h_dest, fib_dmac, sizeof(fib_dmac)))
   223              test_fatal("l2->h_dest != fib_dmac");
   224  
   225          if (data + sizeof(struct iphdr) > data_end)
   226              test_fatal("ctx doesn't fit iphdr");
   227  
   228          test_finish();
   229      }
   230  
   231  Function reference
   232  ------------------
   233  
   234  * ``test_log(fmt, args...)`` - writes a log message. The conversion specifiers supported by *fmt* are the same as for
   235    ``bpf_trace_printk()``. They are **%d**, **%i**, **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, **%lli**,
   236    **%llu**, **%llx**. No modifier (size of field, padding with zeroes, etc.) is available.
   237  
   238  * ``test_fail()`` - marks the current test or sub-test as failed but will continue execution.
   239  
   240  * ``test_fail_now()`` - marks the current test or sub-test as failed and will stop execution of the
   241    test or sub-test (If called in a sub-test, the other sub-tests will still run).
   242  
   243  * ``test_fatal(fmt, args...)`` - writes a log and then calls ``test_fail_now()``
   244  
   245  * ``assert(stmt)`` - asserts that the statement within is true and call ``test_fail_now()`` otherwise.
   246    ``assert`` will log the file and line number of the assert statement.
   247  
   248  * ``test_skip()`` - marks the current test or sub-test as skipped but will continue execution.
   249  
   250  * ``test_skip_now()`` - marks the current test or sub-test as skipped and will stop execution of the
   251    test or sub-test (If called in a sub-test, the other sub-tests will still run).
   252  
   253  * ``test_init()`` - initializes the internal state for the test and must be called before any of the
   254    functions above can be called.
   255  
   256  * ``test_finish()`` - submits the results and returns from the current function.
   257  
   258  .. warning::
   259      Functions that halt the execution (``test_fail_now()``, ``test_fatal()``, ``test_skip_now()``) can't be
   260      used within both a sub-test (``TEST``) and ``for``, ``while``, or ``switch/case`` blocks since they use the ``break`` keyword to stop a
   261      sub-test. These functions can still be used from within ``for``, ``while`` and ``switch/case`` blocks if no
   262      sub-tests are used, because in that case the flow interruption happens via ``return``.
   263  
   264  Function mocking
   265  ----------------
   266  
   267  Being able to mock out a function is a great tool to have when creating tests for a number of
   268  reasons. You might for example want to test what happens if a specific function returns an error
   269  to see if it is handled gracefully. You might want to proxy function calls to record if the function
   270  under test actually called specific dependencies. Or you might want to test code that uses helpers
   271  which rely on a state we can't set in BPF, like the routing table.
   272  
   273  Mocking is easy with this framework:
   274  
   275  1. Create a function with a unique name and the same signature as the function it is replacing.
   276  
   277  2. Create a macro with the exact same name as the function we want to replace and point it to the
   278     function created in step 1. For example ``#define original_function our_mocked_function```
   279  
   280  3. Include the file which contains the definition we are replacing.
   281  
   282  The following example mocks out the fib_lookup helper call and replaces it with our
   283  mocked version, since we don't actually have routes for the IPs we want to test:
   284  
   285  .. code-block:: c
   286  
   287      #include "common.h"
   288  
   289      #include "bpf/ctx/xdp.h"
   290  
   291      #define fib_lookup mock_fib_lookup
   292  
   293      static const char fib_smac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02};
   294      static const char fib_dmac[6] = {0x13, 0x37, 0x13, 0x37, 0x13, 0x37};
   295  
   296      long mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params,
   297                  __maybe_unused int plen, __maybe_unused __u32 flags)
   298      {
   299          memcpy(params->smac, fib_smac, sizeof(fib_smac));
   300          memcpy(params->dmac, fib_dmac, sizeof(fib_dmac));
   301          return 0;
   302      }
   303  
   304      #include "bpf_xdp.c"
   305      #include "lib/nodeport.h"
   306  
   307  Limitations
   308  -----------
   309  
   310  For all its benefits there are some limitations to this way of testing:
   311  
   312  * Code must pass the verifier, so our setup and test code has to obey the same rules as other BPF
   313    programs. A side effect is that it automatically guarantees that all code that passes will also
   314    load. The biggest concern is the complexity limit on older kernels, this can be somewhat mitigated
   315    by separating heavy setup work into its own ``SETUP`` program and optionally tail calling into the
   316    code to be tested, to ensure the testing harness doesn't push us over the complexity limit.
   317  
   318  * Test functions like ``test_log()``, ``test_fail()``, ``test_skip()`` can only be executed within the
   319    scope of the main program or a ``TEST``. These functions rely on local variables set by ``test_init()``
   320    and will produce errors when used in other functions.
   321  
   322  * Functions that halt the execution (``test_fail_now()``, ``test_fatal()``, ``test_skip_now()``) can't be
   323    used within both a sub-test (``TEST``) and ``for``, ``while``, or ``switch/case`` blocks since they use the ``break`` keyword to stop a
   324    sub-test. These functions can still be used from within ``for``, ``while`` and ``switch/case`` blocks if no
   325    sub-tests are used, because in that case the flow interruption happens via ``return``.
   326  
   327  * Sub-test names can't use more than 127 characters.
   328  
   329  * Log messages can't use more than 127 characters and have no more than 12 arguments.