LCOV - code coverage report
Current view: top level - core - hook.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 18.6 % 97 18
Test Date: 2025-02-26 17:59:59 Functions: 29.4 % 17 5

            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 "core/hook.h"
       7              : 
       8              : #include <linux/bpf.h>
       9              : 
      10              : #include <errno.h>
      11              : #include <limits.h>
      12              : #include <stdbool.h>
      13              : #include <stdint.h>
      14              : #include <stdlib.h>
      15              : #include <string.h>
      16              : 
      17              : #include "core/dump.h"
      18              : #include "core/helper.h"
      19              : #include "core/list.h"
      20              : #include "core/logger.h"
      21              : 
      22              : /// Maximum length of the chain name: @c BPF_OBJ_NAME_LEN minus the length
      23              : /// of the BPF object suffix.
      24              : #define BF_CHAIN_NAME_LEN 11
      25              : 
      26              : static const char *_bf_hook_strs[] = {
      27              :     [BF_HOOK_XDP] = "BF_HOOK_XDP",
      28              :     [BF_HOOK_TC_INGRESS] = "BF_HOOK_TC_INGRESS",
      29              :     [BF_HOOK_NF_PRE_ROUTING] = "BF_HOOK_NF_PRE_ROUTING",
      30              :     [BF_HOOK_NF_LOCAL_IN] = "BF_HOOK_NF_LOCAL_IN",
      31              :     [BF_HOOK_CGROUP_INGRESS] = "BF_HOOK_CGROUP_INGRESS",
      32              :     [BF_HOOK_CGROUP_EGRESS] = "BF_HOOK_CGROUP_EGRESS",
      33              :     [BF_HOOK_NF_FORWARD] = "BF_HOOK_NF_FORWARD",
      34              :     [BF_HOOK_NF_LOCAL_OUT] = "BF_HOOK_NF_LOCAL_OUT",
      35              :     [BF_HOOK_NF_POST_ROUTING] = "BF_HOOK_NF_POST_ROUTING",
      36              :     [BF_HOOK_TC_EGRESS] = "BF_HOOK_TC_EGRESS",
      37              : };
      38              : 
      39              : static_assert(ARRAY_SIZE(_bf_hook_strs) == _BF_HOOK_MAX,
      40              :               "missing entries in hooks_str array");
      41              : 
      42           12 : const char *bf_hook_to_str(enum bf_hook hook)
      43              : {
      44           12 :     bf_assert(0 <= hook && hook < _BF_HOOK_MAX);
      45              : 
      46           10 :     return _bf_hook_strs[hook];
      47              : }
      48              : 
      49           14 : int bf_hook_from_str(const char *str, enum bf_hook *hook)
      50              : {
      51           14 :     bf_assert(str);
      52           13 :     bf_assert(hook);
      53              : 
      54           77 :     for (size_t i = 0; i < _BF_HOOK_MAX; ++i) {
      55           75 :         if (bf_streq(_bf_hook_strs[i], str)) {
      56           10 :             *hook = i;
      57           10 :             return 0;
      58              :         }
      59              :     }
      60              : 
      61              :     return -EINVAL;
      62              : }
      63              : 
      64           12 : unsigned int bf_hook_to_bpf_prog_type(enum bf_hook hook)
      65              : {
      66              :     static const unsigned int prog_type[] = {
      67              :         [BF_HOOK_XDP] = BPF_PROG_TYPE_XDP,
      68              :         [BF_HOOK_TC_INGRESS] = BPF_PROG_TYPE_SCHED_CLS,
      69              :         [BF_HOOK_NF_PRE_ROUTING] = BPF_PROG_TYPE_NETFILTER,
      70              :         [BF_HOOK_NF_LOCAL_IN] = BPF_PROG_TYPE_NETFILTER,
      71              :         [BF_HOOK_CGROUP_INGRESS] = BPF_PROG_TYPE_CGROUP_SKB,
      72              :         [BF_HOOK_CGROUP_EGRESS] = BPF_PROG_TYPE_CGROUP_SKB,
      73              :         [BF_HOOK_NF_FORWARD] = BPF_PROG_TYPE_NETFILTER,
      74              :         [BF_HOOK_NF_LOCAL_OUT] = BPF_PROG_TYPE_NETFILTER,
      75              :         [BF_HOOK_NF_POST_ROUTING] = BPF_PROG_TYPE_NETFILTER,
      76              :         [BF_HOOK_TC_EGRESS] = BPF_PROG_TYPE_SCHED_CLS,
      77              :     };
      78              : 
      79           12 :     bf_assert(0 <= hook && hook < _BF_HOOK_MAX);
      80              :     static_assert(ARRAY_SIZE(prog_type) == _BF_HOOK_MAX,
      81              :                   "missing entries in prog_type array");
      82              : 
      83           10 :     return prog_type[hook];
      84              : }
      85              : 
      86           12 : enum bpf_attach_type bf_hook_to_attach_type(enum bf_hook hook)
      87              : {
      88              :     static const enum bpf_attach_type hooks[] = {
      89              :         [BF_HOOK_XDP] = 0,
      90              :         [BF_HOOK_TC_INGRESS] = BPF_TCX_INGRESS,
      91              :         [BF_HOOK_NF_PRE_ROUTING] = BPF_NETFILTER,
      92              :         [BF_HOOK_NF_LOCAL_IN] = BPF_NETFILTER,
      93              :         [BF_HOOK_CGROUP_INGRESS] = BPF_CGROUP_INET_INGRESS,
      94              :         [BF_HOOK_CGROUP_EGRESS] = BPF_CGROUP_INET_EGRESS,
      95              :         [BF_HOOK_NF_FORWARD] = BPF_NETFILTER,
      96              :         [BF_HOOK_NF_LOCAL_OUT] = BPF_NETFILTER,
      97              :         [BF_HOOK_NF_POST_ROUTING] = BPF_NETFILTER,
      98              :         [BF_HOOK_TC_EGRESS] = BPF_TCX_EGRESS,
      99              :     };
     100              : 
     101           12 :     bf_assert(0 <= hook && hook < _BF_HOOK_MAX);
     102              :     static_assert(ARRAY_SIZE(hooks) == _BF_HOOK_MAX,
     103              :                   "missing entries in hooks array");
     104              : 
     105           10 :     return hooks[hook];
     106              : }
     107              : 
     108            0 : static int _bf_hook_opt_ifindex_parse(struct bf_hook_opts *opts,
     109              :                                       const char *raw_opt)
     110              : {
     111              :     unsigned long ifindex;
     112              : 
     113            0 :     errno = 0;
     114            0 :     ifindex = strtoul(raw_opt, NULL, 0);
     115            0 :     if (errno != 0) {
     116            0 :         return bf_err_r(-errno, "failed to parse hook options ifindex=%s",
     117              :                         raw_opt);
     118              :     }
     119              : 
     120            0 :     if (ifindex > UINT_MAX)
     121            0 :         return bf_err_r(-E2BIG, "ifindex is too big: %lu", ifindex);
     122              : 
     123            0 :     opts->ifindex = (uint32_t)ifindex;
     124              : 
     125            0 :     return 0;
     126              : }
     127              : 
     128            0 : static void _bf_hook_opt_ifindex_dump(const struct bf_hook_opts *opts,
     129              :                                       prefix_t *prefix)
     130              : {
     131            0 :     DUMP(prefix, "ifindex: %d", opts->ifindex);
     132            0 : }
     133              : 
     134            0 : static int _bf_hook_opt_cgroup_parse(struct bf_hook_opts *opts,
     135              :                                      const char *raw_opt)
     136              : {
     137            0 :     opts->cgroup = strdup(raw_opt);
     138            0 :     if (!opts->cgroup)
     139            0 :         return bf_err_r(-ENOMEM, "failed to copy cgroup path '%s'", raw_opt);
     140              : 
     141              :     return 0;
     142              : }
     143              : 
     144            0 : static void _bf_hook_opt_cgroup_dump(const struct bf_hook_opts *opts,
     145              :                                      prefix_t *prefix)
     146              : {
     147            0 :     DUMP(prefix, "cgroup: %s", opts->cgroup);
     148            0 : }
     149              : 
     150            0 : static int _bf_hook_opt_name_parse(struct bf_hook_opts *opts,
     151              :                                    const char *raw_opt)
     152              : {
     153            0 :     if (strlen(raw_opt) > BF_CHAIN_NAME_LEN) {
     154            0 :         return bf_err_r(E2BIG, "a chain name should be at most %d characters",
     155              :                         BF_CHAIN_NAME_LEN);
     156              :     }
     157              : 
     158            0 :     opts->name = strdup(raw_opt);
     159            0 :     if (!opts->name)
     160            0 :         return bf_err_r(-ENOMEM, "failed to copy chain name '%s'", raw_opt);
     161              : 
     162              :     return 0;
     163              : }
     164              : 
     165            0 : static void _bf_hook_opt_name_dump(const struct bf_hook_opts *opts,
     166              :                                    prefix_t *prefix)
     167              : {
     168            0 :     DUMP(prefix, "name: %s", opts->name);
     169            0 : }
     170              : 
     171            0 : static int _bf_hook_opt_attach_parse(struct bf_hook_opts *opts,
     172              :                                      const char *raw_opt)
     173              : {
     174            0 :     if (bf_streq(raw_opt, "yes"))
     175            0 :         opts->attach = true;
     176            0 :     else if (bf_streq(raw_opt, "no"))
     177            0 :         opts->attach = false;
     178              :     else
     179            0 :         return bf_err_r(-EINVAL, "unknown attach value '%s'", raw_opt);
     180              : 
     181              :     return 0;
     182              : }
     183              : 
     184            0 : static void _bf_hook_opt_attach_dump(const struct bf_hook_opts *opts,
     185              :                                      prefix_t *prefix)
     186              : {
     187            0 :     DUMP(prefix, "attach: %s", opts->attach ? "yes" : "no");
     188            0 : }
     189              : 
     190              : static struct bf_hook_opt_support
     191              : {
     192              :     uint32_t required;
     193              :     uint32_t supported;
     194              : } _bf_hook_opts_support[] = {
     195              :     [BF_HOOK_XDP] =
     196              :         {
     197              :             .required = 1 << BF_HOOK_OPT_IFINDEX,
     198              :             .supported = 1 << BF_HOOK_OPT_IFINDEX | 1 << BF_HOOK_OPT_NAME |
     199              :                          1 << BF_HOOK_OPT_ATTACH,
     200              :         },
     201              :     [BF_HOOK_TC_INGRESS] =
     202              :         {
     203              :             .required = 1 << BF_HOOK_OPT_IFINDEX,
     204              :             .supported = 1 << BF_HOOK_OPT_IFINDEX | 1 << BF_HOOK_OPT_NAME |
     205              :                          1 << BF_HOOK_OPT_ATTACH,
     206              :         },
     207              :     [BF_HOOK_NF_PRE_ROUTING] =
     208              :         {
     209              :             .supported = 1 << BF_HOOK_OPT_NAME | 1 << BF_HOOK_OPT_ATTACH,
     210              :         },
     211              :     [BF_HOOK_NF_LOCAL_IN] =
     212              :         {
     213              :             .supported = 1 << BF_HOOK_OPT_NAME | 1 << BF_HOOK_OPT_ATTACH,
     214              :         },
     215              :     [BF_HOOK_CGROUP_INGRESS] =
     216              :         {
     217              :             .required = 1 << BF_HOOK_OPT_CGROUP,
     218              :             .supported = 1 << BF_HOOK_OPT_CGROUP | 1 << BF_HOOK_OPT_NAME |
     219              :                          1 << BF_HOOK_OPT_ATTACH,
     220              :         },
     221              :     [BF_HOOK_CGROUP_EGRESS] =
     222              :         {
     223              :             .required = 1 << BF_HOOK_OPT_CGROUP,
     224              :             .supported = 1 << BF_HOOK_OPT_CGROUP | 1 << BF_HOOK_OPT_NAME |
     225              :                          1 << BF_HOOK_OPT_ATTACH,
     226              :         },
     227              :     [BF_HOOK_NF_FORWARD] =
     228              :         {
     229              :             .supported = 1 << BF_HOOK_OPT_NAME | 1 << BF_HOOK_OPT_ATTACH,
     230              :         },
     231              :     [BF_HOOK_NF_LOCAL_OUT] =
     232              :         {
     233              :             .supported = 1 << BF_HOOK_OPT_NAME | 1 << BF_HOOK_OPT_ATTACH,
     234              :         },
     235              :     [BF_HOOK_NF_POST_ROUTING] =
     236              :         {
     237              :             .supported = 1 << BF_HOOK_OPT_NAME | 1 << BF_HOOK_OPT_ATTACH,
     238              :         },
     239              :     [BF_HOOK_TC_EGRESS] =
     240              :         {
     241              :             .required = 1 << BF_HOOK_OPT_IFINDEX,
     242              :             .supported = 1 << BF_HOOK_OPT_IFINDEX | 1 << BF_HOOK_OPT_NAME |
     243              :                          1 << BF_HOOK_OPT_ATTACH,
     244              :         },
     245              : };
     246              : 
     247              : static_assert(ARRAY_SIZE(_bf_hook_opts_support) == _BF_HOOK_MAX,
     248              :               "missing entries in hook options support array");
     249              : 
     250              : static struct bf_hook_opt_ops
     251              : {
     252              :     const char *name;
     253              :     enum bf_hook_opt opt;
     254              :     int (*parse)(struct bf_hook_opts *opts, const char *raw_opt);
     255              :     void (*dump)(const struct bf_hook_opts *opts, prefix_t *prefix);
     256              : } _bf_hook_opt_ops[] = {
     257              :     {
     258              :         .name = "ifindex",
     259              :         .opt = BF_HOOK_OPT_IFINDEX,
     260              :         .parse = _bf_hook_opt_ifindex_parse,
     261              :         .dump = _bf_hook_opt_ifindex_dump,
     262              :     },
     263              :     {
     264              :         .name = "cgroup",
     265              :         .opt = BF_HOOK_OPT_CGROUP,
     266              :         .parse = _bf_hook_opt_cgroup_parse,
     267              :         .dump = _bf_hook_opt_cgroup_dump,
     268              :     },
     269              :     {
     270              :         .name = "name",
     271              :         .opt = BF_HOOK_OPT_NAME,
     272              :         .parse = _bf_hook_opt_name_parse,
     273              :         .dump = _bf_hook_opt_name_dump,
     274              :     },
     275              :     {
     276              :         .name = "attach",
     277              :         .opt = BF_HOOK_OPT_ATTACH,
     278              :         .parse = _bf_hook_opt_attach_parse,
     279              :         .dump = _bf_hook_opt_attach_dump,
     280              :     },
     281              : };
     282              : 
     283              : static_assert(ARRAY_SIZE(_bf_hook_opt_ops) == _BF_HOOK_OPT_MAX,
     284              :               "missing entries in hook option ops array");
     285              : 
     286              : #define _bf_hook_opt_is_supported(hook, opt)                                   \
     287              :     (_bf_hook_opts_support[hook].supported & (1 << (opt)))
     288              : #define _bf_hook_opt_is_required(hook, opt)                                    \
     289              :     (_bf_hook_opts_support[hook].required & (1 << (opt)))
     290              : #define _bf_hook_opt_is_used(opts, opt) ((opts)->used_opts & (1 << (opt)))
     291              : 
     292            0 : static struct bf_hook_opt_ops *_bf_hook_opts_get_ops(const char *key,
     293              :                                                      size_t len)
     294              : {
     295              :     int r;
     296              : 
     297            0 :     bf_assert(key && len > 0);
     298              : 
     299            0 :     for (int i = 0; i < _BF_HOOK_OPT_MAX; ++i) {
     300            0 :         r = strncmp(_bf_hook_opt_ops[i].name, key, len);
     301            0 :         if (r == 0)
     302            0 :             return &_bf_hook_opt_ops[i];
     303              :     }
     304              : 
     305              :     return NULL;
     306              : }
     307              : 
     308            0 : static int _bf_hook_opts_process_opts(struct bf_hook_opts *opts,
     309              :                                       enum bf_hook hook, bf_list *raw_opts)
     310              : {
     311              :     const char *value;
     312              :     const struct bf_hook_opt_ops *ops;
     313              :     int r;
     314              : 
     315            0 :     bf_assert(opts);
     316              : 
     317            0 :     if (!raw_opts)
     318              :         return 0;
     319              : 
     320            0 :     bf_list_foreach (raw_opts, raw_opt_node) {
     321            0 :         const char *raw_opt = bf_list_node_get_data(raw_opt_node);
     322              : 
     323            0 :         value = strchr(raw_opt, '=');
     324              : 
     325            0 :         ops = _bf_hook_opts_get_ops(raw_opt, value - raw_opt);
     326            0 :         if (!ops)
     327            0 :             return bf_err_r(-ENOTSUP, "unknown option '%s', ignoring", raw_opt);
     328              : 
     329            0 :         if (!_bf_hook_opt_is_supported(hook, ops->opt)) {
     330            0 :             return bf_err_r(-ENOTSUP, "hook '%s' doesn't support option '%s'",
     331              :                             bf_hook_to_str(hook), ops->name);
     332              :         }
     333              : 
     334            0 :         r = ops->parse(opts, value + 1);
     335            0 :         if (r < 0)
     336              :             return r;
     337              : 
     338            0 :         opts->used_opts |= (1 << ops->opt);
     339              :     }
     340              : 
     341              :     return 0;
     342              : }
     343              : 
     344            0 : int bf_hook_opts_init(struct bf_hook_opts *opts, enum bf_hook hook,
     345              :                       bf_list *raw_opts)
     346              : {
     347              :     int r;
     348              : 
     349            0 :     bf_assert(opts);
     350              : 
     351            0 :     *opts = (struct bf_hook_opts) {
     352              :         .used_opts = 1 << BF_HOOK_OPT_ATTACH,
     353              :         .attach = true,
     354              :     };
     355              : 
     356            0 :     r = _bf_hook_opts_process_opts(opts, hook, raw_opts);
     357            0 :     if (r < 0)
     358              :         return r;
     359              : 
     360            0 :     for (int i = 0; i < _BF_HOOK_OPT_MAX; ++i) {
     361            0 :         if (_bf_hook_opt_is_required(hook, i) &&
     362            0 :             !_bf_hook_opt_is_used(opts, i)) {
     363            0 :             return bf_err_r(-EINVAL, "hook '%s' requires option '%s'",
     364              :                             bf_hook_to_str(hook), _bf_hook_opt_ops[i].name);
     365              :         }
     366              :     }
     367              : 
     368              :     return 0;
     369              : }
     370              : 
     371            9 : void bf_hook_opts_clean(struct bf_hook_opts *opts)
     372              : {
     373              :     freep((void *)&opts->cgroup);
     374              :     freep((void *)&opts->name);
     375            9 : }
     376              : 
     377            0 : void bf_hook_opts_dump(const struct bf_hook_opts *opts, prefix_t *prefix,
     378              :                        enum bf_hook hook)
     379              : {
     380            0 :     DUMP(prefix, "struct bf_hook_opts at %p", opts);
     381            0 :     bf_dump_prefix_push(prefix);
     382              : 
     383            0 :     for (int i = 0; i < _BF_HOOK_OPT_MAX; ++i) {
     384              :         struct bf_hook_opt_ops *ops = &_bf_hook_opt_ops[i];
     385            0 :         if (i == _BF_HOOK_OPT_MAX - 1)
     386            0 :             bf_dump_prefix_last(prefix);
     387              : 
     388            0 :         if (!_bf_hook_opt_is_supported(hook, i)) {
     389            0 :             DUMP(prefix, "%s: <unsupported>", ops->name);
     390            0 :         } else if (!_bf_hook_opt_is_used(opts, i)) {
     391            0 :             DUMP(prefix, "%s: <unset>", ops->name);
     392              :         } else {
     393            0 :             ops->dump(opts, prefix);
     394              :         }
     395              :     }
     396              : 
     397            0 :     bf_dump_prefix_pop(prefix);
     398            0 : }
        

Generated by: LCOV version 2.0-1