LCOV - code coverage report
Current view: top level - bpfilter - main.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 71.0 % 138 98
Test Date: 2026-03-12 11:07:40 Functions: 100.0 % 8 8
Branches: 33.3 % 123 41

             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                 :             : #define _GNU_SOURCE
       7                 :             : 
       8                 :             : #include <argp.h>
       9                 :             : #include <errno.h>
      10                 :             : #include <signal.h>
      11                 :             : #include <stdint.h>
      12                 :             : #include <string.h>
      13                 :             : #include <sys/socket.h>
      14                 :             : #include <sys/un.h>
      15                 :             : #include <unistd.h>
      16                 :             : 
      17                 :             : #include <bpfilter/btf.h>
      18                 :             : #include <bpfilter/dump.h>
      19                 :             : #include <bpfilter/helper.h>
      20                 :             : #include <bpfilter/io.h>
      21                 :             : #include <bpfilter/logger.h>
      22                 :             : #include <bpfilter/ns.h>
      23                 :             : #include <bpfilter/request.h>
      24                 :             : #include <bpfilter/response.h>
      25                 :             : #include <bpfilter/version.h>
      26                 :             : 
      27                 :             : #include "ctx.h"
      28                 :             : 
      29                 :             : #define BF_DEFAULT_BPFFS_PATH "/sys/fs/bpf"
      30                 :             : 
      31                 :             : enum
      32                 :             : {
      33                 :             :     BF_OPT_NO_IPTABLES_KEY,
      34                 :             :     BF_OPT_NO_NFTABLES_KEY,
      35                 :             :     BF_OPT_NO_CLI_KEY,
      36                 :             :     BF_OPT_WITH_BPF_TOKEN,
      37                 :             :     BF_OPT_BPFFS_PATH,
      38                 :             : };
      39                 :             : 
      40                 :             : struct bf_options
      41                 :             : {
      42                 :             :     bool transient;
      43                 :             :     bool with_bpf_token;
      44                 :             :     const char *bpffs_path;
      45                 :             :     uint16_t verbose;
      46                 :             : };
      47                 :             : 
      48                 :             : static const char *_bf_verbose_strs[] = {
      49                 :             :     [BF_VERBOSE_DEBUG] = "debug",
      50                 :             :     [BF_VERBOSE_BPF] = "bpf",
      51                 :             :     [BF_VERBOSE_BYTECODE] = "bytecode",
      52                 :             : };
      53                 :             : 
      54                 :             : static_assert_enum_mapping(_bf_verbose_strs, _BF_VERBOSE_MAX);
      55                 :             : 
      56                 :          29 : static enum bf_verbose _bf_verbose_from_str(const char *str)
      57                 :             : {
      58                 :             :     assert(str);
      59                 :             : 
      60         [ +  - ]:          29 :     for (enum bf_verbose verbose = 0; verbose < _BF_VERBOSE_MAX; ++verbose) {
      61         [ -  + ]:          29 :         if (bf_streq(_bf_verbose_strs[verbose], str))
      62                 :             :             return verbose;
      63                 :             :     }
      64                 :             : 
      65                 :             :     return -EINVAL;
      66                 :             : }
      67                 :             : 
      68                 :             : static struct argp_option _bf_options[] = {
      69                 :             :     {"transient", 't', 0, 0,
      70                 :             :      "Do not load or save runtime context and remove all BPF programs on shutdown",
      71                 :             :      0},
      72                 :             :     {"buffer-len", 'b', "BUF_LEN_POW", 0,
      73                 :             :      "DEPRECATED. Size of the BPF log buffer as a power of 2 (only used when --verbose is used). Default: 16.",
      74                 :             :      0},
      75                 :             :     {"no-iptables", BF_OPT_NO_IPTABLES_KEY, 0, 0,
      76                 :             :      "DEPRECATED. Disable iptables support", 0},
      77                 :             :     {"no-nftables", BF_OPT_NO_NFTABLES_KEY, 0, 0,
      78                 :             :      "DEPRECATED. Disable nftables support", 0},
      79                 :             :     {"no-cli", BF_OPT_NO_CLI_KEY, 0, 0, "DEPRECATED. Disable CLI support", 0},
      80                 :             :     {"with-bpf-token", BF_OPT_WITH_BPF_TOKEN, NULL, 0,
      81                 :             :      "Use a BPF token with the bpf() system calls. The token is created from the bpffs instance mounted at /sys/fs/bpf.",
      82                 :             :      0},
      83                 :             :     {"bpffs-path", BF_OPT_BPFFS_PATH, "BPFFS_PATH", 0,
      84                 :             :      "Path to the bpffs to pin the BPF objects into. Defaults to " BF_DEFAULT_BPFFS_PATH
      85                 :             :      ".",
      86                 :             :      0},
      87                 :             :     {"verbose", 'v', "VERBOSE_FLAG", 0,
      88                 :             :      "Verbose flags to enable. Can be used more than once.", 0},
      89                 :             :     {0},
      90                 :             : };
      91                 :             : 
      92                 :         203 : static error_t _bf_opts_parser(int key, char *arg, struct argp_state *state)
      93                 :             : {
      94                 :         203 :     struct bf_options *args = state->input;
      95                 :             :     enum bf_verbose opt;
      96                 :             : 
      97                 :             :     (void)arg;
      98                 :             : 
      99   [ -  -  -  -  :         203 :     switch (key) {
             -  -  +  +  
                      + ]
     100                 :           0 :     case 't':
     101                 :           0 :         args->transient = true;
     102                 :           0 :         break;
     103                 :           0 :     case 'b':
     104         [ #  # ]:           0 :         bf_warn(
     105                 :             :             "--buffer-len is deprecated, buffer size is defined automatically");
     106                 :             :         break;
     107                 :           0 :     case BF_OPT_NO_IPTABLES_KEY:
     108         [ #  # ]:           0 :         bf_warn("--no-iptables is deprecated");
     109                 :             :         break;
     110                 :           0 :     case BF_OPT_NO_NFTABLES_KEY:
     111         [ #  # ]:           0 :         bf_warn("--no-nftables is deprecated");
     112                 :             :         break;
     113                 :           0 :     case BF_OPT_NO_CLI_KEY:
     114         [ #  # ]:           0 :         bf_warn("--no-cli is deprecated");
     115                 :             :         break;
     116                 :           0 :     case BF_OPT_WITH_BPF_TOKEN:
     117                 :           0 :         args->with_bpf_token = true;
     118         [ #  # ]:           0 :         bf_info("using a BPF token");
     119                 :             :         break;
     120                 :          29 :     case BF_OPT_BPFFS_PATH:
     121                 :          29 :         args->bpffs_path = arg;
     122         [ -  + ]:          29 :         bf_info("using bpffs at %s", args->bpffs_path);
     123                 :             :         break;
     124                 :          29 :     case 'v':
     125                 :          29 :         opt = _bf_verbose_from_str(arg);
     126         [ -  + ]:          29 :         if ((int)opt < 0) {
     127         [ #  # ]:           0 :             return bf_err_r(
     128                 :             :                 (int)opt,
     129                 :             :                 "unknown --verbose option '%s', valid --verbose options: [debug, bpf, bytecode]",
     130                 :             :                 arg);
     131                 :             :         }
     132         [ +  - ]:          29 :         bf_info("enabling verbose for '%s'", arg);
     133         [ +  - ]:          29 :         if (opt == BF_VERBOSE_DEBUG)
     134                 :          29 :             bf_log_set_level(BF_LOG_DBG);
     135                 :          29 :         args->verbose |= BF_FLAG(opt);
     136                 :          29 :         break;
     137                 :             :     default:
     138                 :             :         return ARGP_ERR_UNKNOWN;
     139                 :             :     }
     140                 :             : 
     141                 :             :     return 0;
     142                 :             : }
     143                 :             : 
     144                 :          29 : static int _bf_opts_init(struct bf_options *opts, int argc, char *argv[])
     145                 :             : {
     146                 :          29 :     struct argp argp = {_bf_options, _bf_opts_parser, NULL, NULL, 0, NULL,
     147                 :             :                         NULL};
     148                 :             : 
     149                 :          29 :     return argp_parse(&argp, argc, argv, 0, 0, opts);
     150                 :             : }
     151                 :             : 
     152                 :             : /**
     153                 :             :  * Global flag to indicate whether the daemon should stop.
     154                 :             :  */
     155                 :             : static volatile sig_atomic_t _bf_stop_received = 0;
     156                 :             : 
     157                 :             : /**
     158                 :             :  * Set atomic flag to stop the daemon if specific signals are received.
     159                 :             :  *
     160                 :             :  * @param sig Signal number.
     161                 :             :  */
     162                 :          28 : void _bf_sig_handler(int sig)
     163                 :             : {
     164                 :             :     (void)sig;
     165                 :             : 
     166                 :          28 :     _bf_stop_received = 1;
     167                 :          28 : }
     168                 :             : 
     169                 :             : /**
     170                 :             :  * Initialize bpfilter's daemon runtime.
     171                 :             :  *
     172                 :             :  * Setup signal handler (for graceful shutdown), initialize a fresh context,
     173                 :             :  * discover existing chains from bpffs, and initialise various front-ends.
     174                 :             :  *
     175                 :             :  * @return 0 on success, negative error code on failure.
     176                 :             :  */
     177                 :          29 : static int _bf_init(int argc, char *argv[])
     178                 :             : {
     179                 :          29 :     struct sigaction sighandler = {.sa_handler = _bf_sig_handler};
     180                 :          29 :     struct bf_options opts = {
     181                 :             :         .transient = false,
     182                 :             :         .with_bpf_token = false,
     183                 :             :         .bpffs_path = BF_DEFAULT_BPFFS_PATH,
     184                 :             :         .verbose = 0,
     185                 :             :     };
     186                 :             :     int r;
     187                 :             : 
     188         [ -  + ]:          29 :     if (sigaction(SIGINT, &sighandler, NULL) < 0)
     189         [ #  # ]:           0 :         return bf_err_r(errno, "can't override handler for SIGINT");
     190                 :             : 
     191         [ -  + ]:          29 :     if (sigaction(SIGTERM, &sighandler, NULL) < 0)
     192         [ #  # ]:           0 :         return bf_err_r(errno, "can't override handler for SIGTERM");
     193                 :             : 
     194         [ +  - ]:          29 :     bf_info("starting bpfilter version %s", BF_VERSION);
     195                 :             : 
     196                 :          29 :     r = _bf_opts_init(&opts, argc, argv);
     197         [ -  + ]:          29 :     if (r < 0)
     198         [ #  # ]:           0 :         return bf_err_r(r, "failed to parse command line arguments");
     199                 :             : 
     200                 :          29 :     r = bf_ensure_dir(BF_RUNTIME_DIR);
     201         [ -  + ]:          29 :     if (r)
     202         [ #  # ]:           0 :         return bf_err_r(r, "failed to ensure runtime directory exists");
     203                 :             : 
     204                 :          29 :     r = bf_ctx_setup(opts.transient, opts.with_bpf_token, opts.bpffs_path,
     205                 :          29 :                      opts.verbose);
     206         [ -  + ]:          29 :     if (r)
     207         [ #  # ]:           0 :         return bf_err_r(r, "failed to setup context");
     208                 :             : 
     209                 :          29 :     bf_ctx_dump(EMPTY_PREFIX);
     210                 :             : 
     211                 :          29 :     return 0;
     212                 :             : }
     213                 :             : 
     214                 :             : extern int bf_request_handler(const struct bf_request *request,
     215                 :             :                               struct bf_response **response);
     216                 :             : 
     217                 :             : /**
     218                 :             :  * Process a request.
     219                 :             :  *
     220                 :             :  * If the handler returns 0, @p response is expected to be filled, and ready
     221                 :             :  * to be returned to the client.
     222                 :             :  * If the handler returns a negative error code, @p response is filled by @ref
     223                 :             :  * _bf_process_request with a generated error response and 0 is returned. If
     224                 :             :  * generating the error response fails, then 0 is returned.
     225                 :             :  *
     226                 :             :  * In other words, if 0 is returned, @p response is ready to be sent back, if
     227                 :             :  * a negative error code is returned, an error occured during @p request
     228                 :             :  * processing, and no response is available.
     229                 :             :  *
     230                 :             :  * @param request Request to process. Can't be NULL.
     231                 :             :  * @param response Response to fill. Can't be NULL.
     232                 :             :  * @return 0 on success, negative error code on failure.
     233                 :             :  */
     234                 :         176 : static int _bf_process_request(struct bf_request *request,
     235                 :             :                                struct bf_response **response)
     236                 :             : {
     237                 :             :     int r;
     238                 :             : 
     239                 :             :     assert(request);
     240                 :             :     assert(response);
     241                 :             : 
     242         [ -  + ]:         352 :     if (bf_request_cmd(request) < 0 ||
     243                 :         176 :         bf_request_cmd(request) >= _BF_REQ_CMD_MAX) {
     244         [ #  # ]:           0 :         bf_warn("received a request with command %d, unknown command, ignoring",
     245                 :             :                 bf_request_cmd(request));
     246                 :           0 :         return bf_response_new_failure(response, -EINVAL);
     247                 :             :     }
     248                 :             : 
     249         [ +  - ]:         176 :     bf_info("processing request %s",
     250                 :             :             bf_request_cmd_to_str(bf_request_cmd(request)));
     251                 :             : 
     252                 :         176 :     r = bf_request_handler(request, response);
     253         [ -  + ]:         176 :     if (r) {
     254                 :             :         /* We failed to process the request, so we need to generate an
     255                 :             :          * error. If the error response is successfully generated, then we
     256                 :             :          * return 0, otherwise we return the error code. */
     257                 :           0 :         r = bf_response_new_failure(response, r);
     258                 :             :     }
     259                 :             : 
     260                 :             :     return r;
     261                 :             : }
     262                 :             : 
     263                 :             : /**
     264                 :             :  * Loop and process requests.
     265                 :             :  *
     266                 :             :  * Create a socket and perform blocking accept() calls. For each connection,
     267                 :             :  * receive a request, process it, and send the response back.
     268                 :             :  *
     269                 :             :  * If a signal is received, @ref _bf_stop_received will be set to 1 by @ref
     270                 :             :  * _bf_sig_handler and blocking call to `accept()` will be interrupted.
     271                 :             :  *
     272                 :             :  * @return 0 on success, negative error code on failure.
     273                 :             :  */
     274                 :          29 : static int _bf_run(void)
     275                 :             : {
     276                 :          29 :     _cleanup_close_ int fd = -1;
     277                 :          29 :     _cleanup_close_ int lock = -1;
     278                 :          29 :     struct sockaddr_un addr = {};
     279                 :             :     struct ucred peer_cred;
     280                 :          29 :     socklen_t peer_cred_len = sizeof(peer_cred);
     281                 :             :     int r;
     282                 :             : 
     283                 :          29 :     lock = bf_acquire_lock(BF_LOCK_PATH);
     284         [ +  + ]:          29 :     if (lock < 0) {
     285         [ +  - ]:           1 :         return bf_err_r(
     286                 :             :             lock,
     287                 :             :             "failed to acquire the daemon lock, is the daemon already running? Error");
     288                 :             :     }
     289                 :             : 
     290                 :          28 :     fd = socket(AF_UNIX, SOCK_STREAM, 0);
     291         [ -  + ]:          28 :     if (fd < 0)
     292         [ #  # ]:           0 :         return bf_err_r(errno, "failed to create socket");
     293                 :             : 
     294                 :             :     // We have a lock on the lock file, so no other daemon is running, we can
     295                 :             :     // remove the socket file (if any).
     296                 :          28 :     unlink(BF_SOCKET_PATH);
     297                 :          28 :     addr.sun_family = AF_UNIX;
     298                 :          28 :     strncpy(addr.sun_path, BF_SOCKET_PATH, sizeof(addr.sun_path) - 1);
     299                 :             : 
     300                 :          28 :     r = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
     301         [ -  + ]:          28 :     if (r < 0) {
     302         [ #  # ]:           0 :         return bf_err_r(errno, "failed to bind socket to %s", BF_SOCKET_PATH);
     303                 :             :     }
     304                 :             : 
     305                 :          28 :     r = listen(fd, 1);
     306         [ -  + ]:          28 :     if (r < 0)
     307         [ #  # ]:           0 :         return bf_err_r(errno, "listen() failed");
     308                 :             : 
     309         [ +  - ]:          28 :     bf_info("waiting for requests...");
     310                 :             : 
     311         [ +  + ]:         232 :     while (!_bf_stop_received) {
     312                 :         204 :         _cleanup_close_ int client_fd = -1;
     313                 :         204 :         _free_bf_request_ struct bf_request *request = NULL;
     314                 :         204 :         _free_bf_response_ struct bf_response *response = NULL;
     315                 :         204 :         _clean_bf_ns_ struct bf_ns ns = bf_ns_default();
     316                 :             : 
     317                 :         204 :         client_fd = accept(fd, NULL, NULL);
     318         [ +  + ]:         204 :         if (client_fd < 0) {
     319         [ +  - ]:          28 :             if (_bf_stop_received) {
     320         [ +  - ]:          28 :                 bf_info("received stop signal, exiting...");
     321                 :          28 :                 continue;
     322                 :             :             }
     323                 :             : 
     324         [ #  # ]:           0 :             bf_err_r(errno, "failed to accept connection, ignoring");
     325                 :           0 :             continue;
     326                 :             :         }
     327                 :             : 
     328                 :             :         // NOLINTNEXTLINE: SOL_SOCKET and SO_PEERCRED can't be directly included
     329                 :         176 :         r = getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &peer_cred,
     330                 :             :                        &peer_cred_len);
     331         [ -  + ]:         176 :         if (r) {
     332         [ #  # ]:           0 :             bf_err_r(errno,
     333                 :             :                      "failed to read the client's credentials, ignoring");
     334                 :           0 :             continue;
     335                 :             :         }
     336                 :             : 
     337                 :         176 :         r = bf_ns_init(&ns, peer_cred.pid);
     338         [ -  + ]:         176 :         if (r) {
     339         [ #  # ]:           0 :             bf_err_r(r, "failed to open the client's namespaces, ignoring");
     340                 :           0 :             continue;
     341                 :             :         }
     342                 :             : 
     343                 :         176 :         r = bf_recv_request(client_fd, &request);
     344         [ -  + ]:         176 :         if (r) {
     345         [ #  # ]:           0 :             bf_err_r(r, "failed to receive request, ignoring");
     346                 :           0 :             continue;
     347                 :             :         }
     348                 :             : 
     349                 :         176 :         bf_request_set_ns(request, &ns);
     350                 :         176 :         bf_request_set_fd(request, client_fd);
     351                 :             : 
     352                 :         176 :         r = _bf_process_request(request, &response);
     353         [ -  + ]:         176 :         if (r) {
     354         [ #  # ]:           0 :             bf_err_r(r, "failed to process request, ignoring");
     355                 :           0 :             continue;
     356                 :             :         }
     357                 :             : 
     358                 :         176 :         r = bf_send_response(client_fd, response);
     359         [ -  + ]:         176 :         if (r) {
     360         [ #  # ]:           0 :             bf_err_r(r, "failed to send response, ignoring");
     361                 :           0 :             continue;
     362                 :             :         }
     363                 :             :     }
     364                 :             : 
     365                 :             :     return 0;
     366                 :             : }
     367                 :             : 
     368                 :          29 : int main(int argc, char *argv[])
     369                 :             : {
     370                 :             :     int r;
     371                 :             : 
     372                 :          29 :     bf_logger_setup();
     373                 :             : 
     374                 :          29 :     argp_program_version = "bpfilter version " BF_VERSION;
     375                 :          29 :     argp_program_bug_address = BF_CONTACT;
     376                 :             : 
     377                 :          29 :     r = bf_btf_setup();
     378         [ -  + ]:          29 :     if (r < 0)
     379         [ #  # ]:           0 :         return bf_err_r(r, "failed to setup BTF module");
     380                 :             : 
     381                 :          29 :     r = _bf_init(argc, argv);
     382         [ -  + ]:          29 :     if (r < 0)
     383         [ #  # ]:           0 :         return bf_err_r(r, "failed to initialize bpfilter");
     384                 :             : 
     385                 :          29 :     r = _bf_run();
     386         [ +  + ]:          29 :     if (r < 0)
     387         [ +  - ]:           1 :         return bf_err_r(r, "run() failed");
     388                 :             : 
     389                 :          28 :     bf_ctx_teardown();
     390                 :          28 :     bf_btf_teardown();
     391                 :             : 
     392                 :          28 :     return r;
     393                 :             : }
        

Generated by: LCOV version 2.0-1