LCOV - code coverage report
Current view: top level - libbpfilter - hook.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 88.1 % 219 193
Test Date: 2026-04-17 15:45:04 Functions: 96.2 % 26 25
Branches: 61.4 % 220 135

             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 "bpfilter/hook.h"
       7                 :             : 
       8                 :             : #include <linux/bpf.h>
       9                 :             : #include <linux/netfilter.h>
      10                 :             : 
      11                 :             : #include <errno.h>
      12                 :             : #include <limits.h>
      13                 :             : #include <stdbool.h>
      14                 :             : #include <stdint.h>
      15                 :             : #include <stdio.h>
      16                 :             : #include <stdlib.h>
      17                 :             : #include <string.h>
      18                 :             : #include <sys/socket.h>
      19                 :             : 
      20                 :             : #include "bpfilter/core/list.h"
      21                 :             : #include "bpfilter/dump.h"
      22                 :             : #include "bpfilter/flavor.h"
      23                 :             : #include "bpfilter/helper.h"
      24                 :             : #include "bpfilter/logger.h"
      25                 :             : #include "bpfilter/pack.h"
      26                 :             : 
      27                 :             : static const char *_bf_hook_strs[] = {
      28                 :             :     [BF_HOOK_XDP] = "BF_HOOK_XDP",
      29                 :             :     [BF_HOOK_TC_INGRESS] = "BF_HOOK_TC_INGRESS",
      30                 :             :     [BF_HOOK_NF_PRE_ROUTING] = "BF_HOOK_NF_PRE_ROUTING",
      31                 :             :     [BF_HOOK_NF_LOCAL_IN] = "BF_HOOK_NF_LOCAL_IN",
      32                 :             :     [BF_HOOK_CGROUP_SKB_INGRESS] = "BF_HOOK_CGROUP_SKB_INGRESS",
      33                 :             :     [BF_HOOK_CGROUP_SKB_EGRESS] = "BF_HOOK_CGROUP_SKB_EGRESS",
      34                 :             :     [BF_HOOK_NF_FORWARD] = "BF_HOOK_NF_FORWARD",
      35                 :             :     [BF_HOOK_NF_LOCAL_OUT] = "BF_HOOK_NF_LOCAL_OUT",
      36                 :             :     [BF_HOOK_NF_POST_ROUTING] = "BF_HOOK_NF_POST_ROUTING",
      37                 :             :     [BF_HOOK_TC_EGRESS] = "BF_HOOK_TC_EGRESS",
      38                 :             :     [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4] = "BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4",
      39                 :             :     [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6] = "BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6",
      40                 :             :     [BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG4] = "BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG4",
      41                 :             :     [BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG6] = "BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG6",
      42                 :             : };
      43                 :             : static_assert_enum_mapping(_bf_hook_strs, _BF_HOOK_MAX);
      44                 :             : 
      45                 :        1054 : const char *bf_hook_to_str(enum bf_hook hook)
      46                 :             : {
      47         [ +  + ]:        1054 :     if (hook < 0 || hook >= _BF_HOOK_MAX)
      48                 :             :         return "<bf_hook unknown>";
      49                 :             : 
      50                 :         969 :     return _bf_hook_strs[hook];
      51                 :             : }
      52                 :             : 
      53                 :        1190 : int bf_hook_from_str(const char *str, enum bf_hook *hook)
      54                 :             : {
      55                 :             :     assert(hook);
      56                 :             : 
      57         [ +  + ]:        3467 :     for (enum bf_hook i = 0; i < _BF_HOOK_MAX; ++i) {
      58         [ +  + ]:        3464 :         if (bf_streq(_bf_hook_strs[i], str)) {
      59                 :        1187 :             *hook = i;
      60                 :        1187 :             return 0;
      61                 :             :         }
      62                 :             :     }
      63                 :             : 
      64                 :             :     return -EINVAL;
      65                 :             : }
      66                 :             : 
      67                 :        7509 : enum bf_flavor bf_hook_to_flavor(enum bf_hook hook)
      68                 :             : {
      69                 :             :     static const enum bf_flavor flavors[] = {
      70                 :             :         [BF_HOOK_XDP] = BF_FLAVOR_XDP,
      71                 :             :         [BF_HOOK_TC_INGRESS] = BF_FLAVOR_TC,
      72                 :             :         [BF_HOOK_NF_PRE_ROUTING] = BF_FLAVOR_NF,
      73                 :             :         [BF_HOOK_NF_LOCAL_IN] = BF_FLAVOR_NF,
      74                 :             :         [BF_HOOK_CGROUP_SKB_INGRESS] = BF_FLAVOR_CGROUP_SKB,
      75                 :             :         [BF_HOOK_CGROUP_SKB_EGRESS] = BF_FLAVOR_CGROUP_SKB,
      76                 :             :         [BF_HOOK_NF_FORWARD] = BF_FLAVOR_NF,
      77                 :             :         [BF_HOOK_NF_LOCAL_OUT] = BF_FLAVOR_NF,
      78                 :             :         [BF_HOOK_NF_POST_ROUTING] = BF_FLAVOR_NF,
      79                 :             :         [BF_HOOK_TC_EGRESS] = BF_FLAVOR_TC,
      80                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4] = BF_FLAVOR_CGROUP_SOCK_ADDR,
      81                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6] = BF_FLAVOR_CGROUP_SOCK_ADDR,
      82                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG4] = BF_FLAVOR_CGROUP_SOCK_ADDR,
      83                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG6] = BF_FLAVOR_CGROUP_SOCK_ADDR,
      84                 :             :     };
      85                 :             : 
      86                 :             :     static_assert_enum_mapping(flavors, _BF_HOOK_MAX);
      87                 :             : 
      88                 :        7509 :     return flavors[hook];
      89                 :             : }
      90                 :             : 
      91                 :        1324 : enum bf_bpf_attach_type bf_hook_to_bpf_attach_type(enum bf_hook hook)
      92                 :             : {
      93                 :             :     static const enum bf_bpf_attach_type attach_types[] = {
      94                 :             :         [BF_HOOK_XDP] = BF_BPF_XDP,
      95                 :             :         [BF_HOOK_TC_INGRESS] = BF_BPF_TCX_INGRESS,
      96                 :             :         [BF_HOOK_NF_PRE_ROUTING] = BF_BPF_NETFILTER,
      97                 :             :         [BF_HOOK_NF_LOCAL_IN] = BF_BPF_NETFILTER,
      98                 :             :         [BF_HOOK_CGROUP_SKB_INGRESS] = BF_BPF_CGROUP_INET_INGRESS,
      99                 :             :         [BF_HOOK_CGROUP_SKB_EGRESS] = BF_BPF_CGROUP_INET_EGRESS,
     100                 :             :         [BF_HOOK_NF_FORWARD] = BF_BPF_NETFILTER,
     101                 :             :         [BF_HOOK_NF_LOCAL_OUT] = BF_BPF_NETFILTER,
     102                 :             :         [BF_HOOK_NF_POST_ROUTING] = BF_BPF_NETFILTER,
     103                 :             :         [BF_HOOK_TC_EGRESS] = BF_BPF_TCX_ENGRESS,
     104                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4] = BF_BPF_CGROUP_INET4_CONNECT,
     105                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6] = BF_BPF_CGROUP_INET6_CONNECT,
     106                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG4] = BF_BPF_CGROUP_UDP4_SENDMSG,
     107                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG6] = BF_BPF_CGROUP_UDP6_SENDMSG,
     108                 :             :     };
     109                 :             : 
     110                 :             :     static_assert_enum_mapping(attach_types, _BF_HOOK_MAX);
     111                 :             : 
     112                 :        1324 :     return attach_types[hook];
     113                 :             : }
     114                 :             : 
     115                 :        1206 : enum bf_bpf_prog_type bf_hook_to_bpf_prog_type(enum bf_hook hook)
     116                 :             : {
     117                 :             :     static const enum bf_bpf_prog_type prog_types[] = {
     118                 :             :         [BF_HOOK_XDP] = BF_BPF_PROG_TYPE_XDP,
     119                 :             :         [BF_HOOK_TC_INGRESS] = BF_BPF_PROG_TYPE_SCHED_CLS,
     120                 :             :         [BF_HOOK_NF_PRE_ROUTING] = BF_BPF_PROG_TYPE_NETFILTER,
     121                 :             :         [BF_HOOK_NF_LOCAL_IN] = BF_BPF_PROG_TYPE_NETFILTER,
     122                 :             :         [BF_HOOK_CGROUP_SKB_INGRESS] = BF_BPF_PROG_TYPE_CGROUP_SKB,
     123                 :             :         [BF_HOOK_CGROUP_SKB_EGRESS] = BF_BPF_PROG_TYPE_CGROUP_SKB,
     124                 :             :         [BF_HOOK_NF_FORWARD] = BF_BPF_PROG_TYPE_NETFILTER,
     125                 :             :         [BF_HOOK_NF_LOCAL_OUT] = BF_BPF_PROG_TYPE_NETFILTER,
     126                 :             :         [BF_HOOK_NF_POST_ROUTING] = BF_BPF_PROG_TYPE_NETFILTER,
     127                 :             :         [BF_HOOK_TC_EGRESS] = BF_BPF_PROG_TYPE_SCHED_CLS,
     128                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4] = BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
     129                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6] = BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
     130                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG4] = BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
     131                 :             :         [BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG6] = BF_BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
     132                 :             :     };
     133                 :             : 
     134                 :             :     static_assert_enum_mapping(prog_types, _BF_HOOK_MAX);
     135                 :             : 
     136                 :        1206 :     return prog_types[hook];
     137                 :             : }
     138                 :             : 
     139                 :        1359 : enum bf_nf_inet_hooks bf_hook_to_nf_hook(enum bf_hook hook)
     140                 :             : {
     141   [ +  +  +  +  :        1359 :     switch (hook) {
                   +  + ]
     142                 :             :     case BF_HOOK_NF_PRE_ROUTING:
     143                 :             :         return BF_NF_INET_PRE_ROUTING;
     144                 :         283 :     case BF_HOOK_NF_LOCAL_IN:
     145                 :         283 :         return BF_NF_INET_LOCAL_IN;
     146                 :         268 :     case BF_HOOK_NF_FORWARD:
     147                 :         268 :         return BF_NF_INET_FORWARD;
     148                 :         268 :     case BF_HOOK_NF_LOCAL_OUT:
     149                 :         268 :         return BF_NF_INET_LOCAL_OUT;
     150                 :         268 :     case BF_HOOK_NF_POST_ROUTING:
     151                 :         268 :         return BF_NF_INET_POST_ROUTING;
     152                 :           3 :     default:
     153         [ +  - ]:           3 :         bf_warn("bf_hook %s (%d) is not an bf_nf_inet_hooks value",
     154                 :             :                 bf_hook_to_str(hook), hook);
     155                 :             :         return -EINVAL;
     156                 :             :     }
     157                 :             : }
     158                 :             : 
     159         [ +  + ]:          12 : enum bf_hook bf_hook_from_nf_hook(enum bf_nf_inet_hooks hook)
     160                 :             : {
     161                 :             :     switch (hook) {
     162                 :             :     case BF_NF_INET_PRE_ROUTING:
     163                 :             :         return BF_HOOK_NF_PRE_ROUTING;
     164                 :             :     case BF_NF_INET_LOCAL_IN:
     165                 :             :         return BF_HOOK_NF_LOCAL_IN;
     166                 :             :     case BF_NF_INET_FORWARD:
     167                 :             :         return BF_HOOK_NF_FORWARD;
     168                 :             :     case BF_NF_INET_LOCAL_OUT:
     169                 :             :         return BF_HOOK_NF_LOCAL_OUT;
     170                 :             :     case BF_NF_INET_POST_ROUTING:
     171                 :             :         return BF_HOOK_NF_POST_ROUTING;
     172                 :           2 :     default:
     173         [ +  - ]:           2 :         bf_warn("nf_inet_hooks %s (%d) is not a bf_hook value",
     174                 :             :                 bf_nf_hook_to_str(hook), hook);
     175                 :             :         return -EINVAL;
     176                 :             :     }
     177                 :             : }
     178                 :             : 
     179                 :          14 : const char *bf_nf_hook_to_str(enum bf_nf_inet_hooks hook)
     180                 :             : {
     181   [ +  +  +  +  :          14 :     switch (hook) {
                   +  + ]
     182                 :             :     case BF_NF_INET_PRE_ROUTING:
     183                 :             :         return "nf_prerouting";
     184                 :           2 :     case BF_NF_INET_LOCAL_IN:
     185                 :           2 :         return "nf_input";
     186                 :           2 :     case BF_NF_INET_FORWARD:
     187                 :           2 :         return "nf_forward";
     188                 :           2 :     case BF_NF_INET_LOCAL_OUT:
     189                 :           2 :         return "nf_output";
     190                 :           2 :     case BF_NF_INET_POST_ROUTING:
     191                 :           2 :         return "nf_postrouting";
     192                 :           4 :     default:
     193         [ +  - ]:           4 :         bf_warn("unknown nf_inet_hooks value %d", hook);
     194                 :             :         return NULL;
     195                 :             :     }
     196                 :             : }
     197                 :             : 
     198                 :          68 : static int _bf_hookopts_ifindex_parse(struct bf_hookopts *hookopts,
     199                 :             :                                       const char *raw_opt)
     200                 :             : {
     201                 :             :     unsigned long ifindex;
     202                 :             : 
     203                 :             :     assert(hookopts);
     204                 :             :     assert(raw_opt);
     205                 :             : 
     206         [ +  + ]:          68 :     if (hookopts->used_opts & BF_FLAG(BF_HOOKOPTS_IFINDEX))
     207         [ +  - ]:           2 :         return bf_err_r(-EEXIST, "ifindex= is defined multiple times");
     208                 :             : 
     209                 :          66 :     errno = 0;
     210                 :          66 :     ifindex = strtoul(raw_opt, NULL, 0);
     211         [ -  + ]:          66 :     if (errno != 0) {
     212         [ #  # ]:           0 :         return bf_err_r(-errno, "failed to parse bf_hookopts type ifindex=%s",
     213                 :             :                         raw_opt);
     214                 :             :     }
     215                 :             : 
     216         [ -  + ]:          66 :     if (ifindex > INT_MAX)
     217         [ #  # ]:           0 :         return bf_err_r(-E2BIG, "ifindex is too big: %lu", ifindex);
     218                 :             : 
     219                 :          66 :     hookopts->ifindex = (int)ifindex;
     220                 :          66 :     hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_IFINDEX);
     221                 :             : 
     222                 :          66 :     return 0;
     223                 :             : }
     224                 :             : 
     225                 :           8 : static void _bf_hookopts_ifindex_dump(const struct bf_hookopts *hookopts,
     226                 :             :                                       prefix_t *prefix)
     227                 :             : {
     228                 :             :     assert(hookopts);
     229                 :             :     assert(prefix);
     230                 :             : 
     231         [ -  + ]:           8 :     DUMP(prefix, "ifindex: %d", hookopts->ifindex);
     232                 :           8 : }
     233                 :             : 
     234                 :          62 : static int _bf_hookopts_cgpath_parse(struct bf_hookopts *hookopts,
     235                 :             :                                      const char *raw_opt)
     236                 :             : {
     237                 :             :     assert(hookopts);
     238                 :             :     assert(raw_opt);
     239                 :             : 
     240         [ +  + ]:          62 :     if (hookopts->used_opts & BF_FLAG(BF_HOOKOPTS_CGPATH))
     241         [ +  - ]:           1 :         return bf_err_r(-EEXIST, "cgpath= is defined multiple times");
     242                 :             : 
     243                 :          61 :     hookopts->cgpath = strdup(raw_opt);
     244         [ -  + ]:          61 :     if (!hookopts->cgpath) {
     245         [ #  # ]:           0 :         return bf_err_r(-ENOMEM, "failed to copy hook option cgpath=%s",
     246                 :             :                         raw_opt);
     247                 :             :     }
     248                 :             : 
     249                 :          61 :     hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_CGPATH);
     250                 :             : 
     251                 :          61 :     return 0;
     252                 :             : }
     253                 :             : 
     254                 :           8 : static void _bf_hookopts_cgpath_dump(const struct bf_hookopts *hookopts,
     255                 :             :                                      prefix_t *prefix)
     256                 :             : {
     257                 :             :     assert(hookopts);
     258                 :             :     assert(prefix);
     259                 :             : 
     260         [ -  + ]:           8 :     DUMP(prefix, "cgpath: %s", hookopts->cgpath);
     261                 :           8 : }
     262                 :             : 
     263                 :          20 : static int _bf_hookopts_family_parse(struct bf_hookopts *hookopts,
     264                 :             :                                      const char *raw_opt)
     265                 :             : {
     266                 :             :     assert(hookopts);
     267                 :             :     assert(raw_opt);
     268                 :             : 
     269         [ -  + ]:          20 :     if (hookopts->used_opts & BF_FLAG(BF_HOOKOPTS_FAMILY))
     270         [ #  # ]:           0 :         return bf_err_r(-EEXIST, "family= is defined multiple times");
     271                 :             : 
     272   [ +  +  +  + ]:          20 :     if (bf_streq("inet4", raw_opt) || bf_streq("inet6", raw_opt)) {
     273         [ -  + ]:          18 :         bf_warn(
     274                 :             :             "family= hook option is deprecated for Netfilter chains, bpfilter will automatically filter on both the IPv4 and IPv6 families");
     275                 :             :     } else {
     276         [ +  - ]:           2 :         return bf_err_r(-ENOTSUP, "unknown netfilter family '%s'", raw_opt);
     277                 :             :     }
     278                 :             : 
     279                 :             :     return 0;
     280                 :             : }
     281                 :             : 
     282                 :           0 : static void _bf_hookopts_family_dump(const struct bf_hookopts *hookopts,
     283                 :             :                                      prefix_t *prefix)
     284                 :             : {
     285                 :             :     assert(hookopts);
     286                 :             :     assert(prefix);
     287                 :             : 
     288                 :             :     (void)hookopts;
     289                 :             : 
     290         [ #  # ]:           0 :     DUMP(prefix, "family: <deprecated>");
     291                 :           0 : }
     292                 :             : 
     293                 :          26 : static int _bf_hookopts_priorities_parse(struct bf_hookopts *hookopts,
     294                 :             :                                          const char *raw_opt)
     295                 :             : {
     296                 :             :     unsigned long priorities[2];
     297                 :             :     _cleanup_free_ char *copy = NULL;
     298                 :             :     char *right, *end;
     299                 :             : 
     300                 :             :     assert(hookopts);
     301                 :             :     assert(raw_opt);
     302                 :             : 
     303         [ +  + ]:          26 :     if (hookopts->used_opts & BF_FLAG(BF_HOOKOPTS_PRIORITIES))
     304         [ +  - ]:           1 :         return bf_err_r(-EEXIST, "priorities= is defined multiple times");
     305                 :             : 
     306                 :          25 :     copy = strdup(raw_opt);
     307         [ +  - ]:          25 :     if (!copy)
     308                 :             :         return -ENOMEM;
     309                 :             : 
     310                 :          25 :     end = copy + strlen(copy);
     311                 :             : 
     312                 :          25 :     right = strchr(copy, '-');
     313         [ +  + ]:          25 :     if (!right)
     314                 :           1 :         goto err_parsing;
     315                 :             : 
     316                 :          24 :     *right = '\0';
     317                 :          24 :     ++right;
     318         [ +  + ]:          24 :     if (end <= right)
     319                 :           1 :         goto err_parsing;
     320                 :             : 
     321                 :          23 :     errno = 0;
     322                 :          23 :     priorities[0] = strtoul(copy, NULL, 0);
     323         [ -  + ]:          23 :     if (errno != 0)
     324                 :           0 :         goto err_parsing;
     325                 :             : 
     326                 :          23 :     priorities[1] = strtoul(right, NULL, 0);
     327         [ -  + ]:          23 :     if (errno != 0)
     328                 :           0 :         goto err_parsing;
     329                 :             : 
     330   [ +  -  -  + ]:          23 :     if (priorities[0] > INT_MAX || priorities[1] > INT_MAX)
     331         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "priorities can't be bigger than %d", INT_MAX);
     332                 :             : 
     333         [ +  + ]:          23 :     if (priorities[0] == priorities[1])
     334         [ +  - ]:           2 :         return bf_err_r(-EINVAL, "priorities must be different");
     335                 :             : 
     336   [ +  +  +  + ]:          21 :     if (!priorities[0] || !priorities[1])
     337         [ +  - ]:           4 :         return bf_err_r(-EINVAL, "priorities can't be 0");
     338                 :             : 
     339                 :          17 :     hookopts->priorities[0] = (int)priorities[0];
     340                 :          17 :     hookopts->priorities[1] = (int)priorities[1];
     341                 :          17 :     hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_PRIORITIES);
     342                 :             : 
     343                 :          17 :     return 0;
     344                 :             : 
     345                 :           2 : err_parsing:
     346         [ -  + ]:           2 :     return bf_err_r(-EINVAL, "failed to parse '%s', expecting '$INT-$INT'",
     347                 :             :                     raw_opt);
     348                 :             : }
     349                 :             : 
     350                 :           4 : static void _bf_hookopts_priorities_dump(const struct bf_hookopts *hookopts,
     351                 :             :                                          prefix_t *prefix)
     352                 :             : {
     353                 :             :     assert(hookopts);
     354                 :             :     assert(prefix);
     355                 :             : 
     356         [ -  + ]:           4 :     DUMP(prefix, "priorities: %d, %d", hookopts->priorities[0],
     357                 :             :          hookopts->priorities[1]);
     358                 :           4 : }
     359                 :             : 
     360                 :             : static struct bf_hookopts_ops
     361                 :             : {
     362                 :             :     const char *name;
     363                 :             :     enum bf_hookopts_type type;
     364                 :             :     uint32_t supported_by;
     365                 :             :     uint32_t required_by;
     366                 :             :     int (*parse)(struct bf_hookopts *, const char *);
     367                 :             :     void (*dump)(const struct bf_hookopts *, prefix_t *);
     368                 :             : } _bf_hookopts_ops[] = {
     369                 :             :     [BF_HOOKOPTS_IFINDEX] = {.name = "ifindex",
     370                 :             :                              .type = BF_HOOKOPTS_IFINDEX,
     371                 :             :                              .required_by =
     372                 :             :                                  BF_FLAGS(BF_FLAVOR_XDP, BF_FLAVOR_TC),
     373                 :             :                              .supported_by = 0,
     374                 :             :                              .parse = _bf_hookopts_ifindex_parse,
     375                 :             :                              .dump = _bf_hookopts_ifindex_dump},
     376                 :             :     [BF_HOOKOPTS_CGPATH] = {.name = "cgpath",
     377                 :             :                             .type = BF_HOOKOPTS_CGPATH,
     378                 :             :                             .required_by = BF_FLAGS(BF_FLAVOR_CGROUP_SKB,
     379                 :             :                                                     BF_FLAVOR_CGROUP_SOCK_ADDR),
     380                 :             :                             .supported_by = 0,
     381                 :             :                             .parse = _bf_hookopts_cgpath_parse,
     382                 :             :                             .dump = _bf_hookopts_cgpath_dump},
     383                 :             :     /** @deprecated Hook option `family=` is deprecated for Netfilter chains.
     384                 :             :      * bpfilter will automatically filter on both the IPv4 and IPv6 families. */
     385                 :             :     [BF_HOOKOPTS_FAMILY] = {.name = "family",
     386                 :             :                             .type = BF_HOOKOPTS_FAMILY,
     387                 :             :                             .required_by = 0,
     388                 :             :                             .supported_by = 0,
     389                 :             :                             .parse = _bf_hookopts_family_parse,
     390                 :             :                             .dump = _bf_hookopts_family_dump},
     391                 :             :     [BF_HOOKOPTS_PRIORITIES] = {.name = "priorities",
     392                 :             :                                 .type = BF_HOOKOPTS_PRIORITIES,
     393                 :             :                                 .required_by = BF_FLAGS(BF_FLAVOR_NF),
     394                 :             :                                 .supported_by = 0,
     395                 :             :                                 .parse = _bf_hookopts_priorities_parse,
     396                 :             :                                 .dump = _bf_hookopts_priorities_dump},
     397                 :             : };
     398                 :             : 
     399                 :             : static_assert_enum_mapping(_bf_hookopts_ops, _BF_HOOKOPTS_MAX);
     400                 :             : 
     401                 :             : #define _bf_hookopts_is_required(type, flavor)                                 \
     402                 :             :     (_bf_hookopts_ops[type].required_by & BF_FLAG(flavor))
     403                 :             : 
     404                 :             : #define _bf_hookopts_is_supported(type, flavor)                                \
     405                 :             :     ((_bf_hookopts_ops[type].supported_by & BF_FLAG(flavor)) ||                \
     406                 :             :      _bf_hookopts_is_required((type), (flavor)))
     407                 :             : 
     408                 :         178 : static struct bf_hookopts_ops *_bf_hookopts_get_ops(const char *key)
     409                 :             : {
     410                 :             :     assert(key);
     411                 :             : 
     412         [ +  + ]:         366 :     for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
     413         [ +  + ]:         364 :         if (bf_streq(_bf_hookopts_ops[type].name, key))
     414                 :         176 :             return &_bf_hookopts_ops[type];
     415                 :             :     }
     416                 :             : 
     417                 :             :     return NULL;
     418                 :             : }
     419                 :             : 
     420                 :         318 : int bf_hookopts_new(struct bf_hookopts **hookopts)
     421                 :             : {
     422                 :             :     assert(hookopts);
     423                 :             : 
     424                 :         318 :     *hookopts = calloc(1, sizeof(struct bf_hookopts));
     425         [ -  + ]:         318 :     if (!*hookopts)
     426         [ #  # ]:           0 :         return bf_err_r(-ENOMEM, "failed to allocate a new bf_hookopts object");
     427                 :             : 
     428                 :             :     return 0;
     429                 :             : }
     430                 :             : 
     431                 :         180 : int bf_hookopts_new_from_pack(struct bf_hookopts **hookopts,
     432                 :             :                               bf_rpack_node_t node)
     433                 :             : {
     434                 :         180 :     _free_bf_hookopts_ struct bf_hookopts *_hookopts = NULL;
     435                 :             :     bf_rpack_node_t child;
     436                 :             :     int r;
     437                 :             : 
     438                 :             :     assert(hookopts);
     439                 :             : 
     440                 :         180 :     r = bf_hookopts_new(&_hookopts);
     441         [ -  + ]:         180 :     if (r)
     442         [ #  # ]:           0 :         return bf_err_r(r, "failed to create bf_hookopts from pack");
     443                 :             : 
     444                 :         180 :     r = bf_rpack_kv_node(node, "ifindex", &child);
     445         [ -  + ]:         180 :     if (r)
     446         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_hookopts.ifindex");
     447         [ +  + ]:         180 :     if (!bf_rpack_is_nil(child)) {
     448                 :         106 :         r = bf_rpack_int(child, &_hookopts->ifindex);
     449         [ -  + ]:         106 :         if (r)
     450         [ #  # ]:           0 :             return bf_rpack_key_err(r, "bf_hookopt.ifindex");
     451                 :             : 
     452                 :         106 :         _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_IFINDEX);
     453                 :             :     }
     454                 :             : 
     455                 :         180 :     r = bf_rpack_kv_node(node, "cgpath", &child);
     456         [ -  + ]:         180 :     if (r)
     457         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_hookopts.cgpath");
     458         [ +  + ]:         180 :     if (!bf_rpack_is_nil(child)) {
     459                 :          57 :         r = bf_rpack_str(child, (char **)&_hookopts->cgpath);
     460         [ -  + ]:          57 :         if (r)
     461         [ #  # ]:           0 :             return bf_rpack_key_err(r, "bf_hookopts.cgpath");
     462                 :             : 
     463                 :          57 :         _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_CGPATH);
     464                 :             :     }
     465                 :             : 
     466                 :         180 :     r = bf_rpack_kv_node(node, "family", &child);
     467         [ -  + ]:         180 :     if (r)
     468         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_hookopts.family");
     469         [ -  + ]:         180 :     if (!bf_rpack_is_nil(child)) {
     470                 :           0 :         r = bf_rpack_uint(child, &_hookopts->family);
     471         [ #  # ]:           0 :         if (r)
     472         [ #  # ]:           0 :             return bf_rpack_key_err(r, "bf_hookopts.family");
     473                 :             : 
     474                 :           0 :         _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_FAMILY);
     475                 :             :     }
     476                 :             : 
     477                 :         180 :     r = bf_rpack_kv_node(node, "priorities", &child);
     478         [ -  + ]:         180 :     if (r)
     479         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_hookopts.priorities");
     480         [ +  + ]:         180 :     if (!bf_rpack_is_nil(child)) {
     481                 :             :         bf_rpack_node_t child, p_node;
     482                 :             : 
     483                 :          18 :         r = bf_rpack_kv_array(node, "priorities", &child);
     484         [ -  + ]:          18 :         if (r)
     485         [ #  # ]:           0 :             return bf_rpack_key_err(r, "bf_hookopts.priorities");
     486         [ -  + ]:          18 :         if (bf_rpack_array_count(child) != 2) {
     487         [ #  # ]:           0 :             return bf_err_r(
     488                 :             :                 -EINVAL, "bf_hookopts.priorities pack expects only 2 values");
     489                 :             :         }
     490                 :             : 
     491   [ +  -  +  +  :         108 :         bf_rpack_array_foreach (child, p_node) {
                   +  + ]
     492                 :          36 :             r = bf_rpack_int(p_node, &_hookopts->priorities[i]);
     493         [ -  + ]:          36 :             if (r) {
     494         [ #  # ]:           0 :                 return bf_rpack_key_err(
     495                 :             :                     r, "failed to unpack bf_hookopts.priorities value");
     496                 :             :             }
     497                 :             :         }
     498                 :             : 
     499                 :          18 :         _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_PRIORITIES);
     500                 :             :     }
     501                 :             : 
     502                 :         180 :     *hookopts = TAKE_PTR(_hookopts);
     503                 :             : 
     504                 :         180 :     return 0;
     505                 :             : }
     506                 :             : 
     507                 :        1767 : void bf_hookopts_clean(struct bf_hookopts *hookopts)
     508                 :             : {
     509                 :             :     freep((void *)&hookopts->cgpath);
     510                 :        1767 : }
     511                 :             : 
     512                 :        5681 : void bf_hookopts_free(struct bf_hookopts **hookopts)
     513                 :             : {
     514                 :             :     assert(hookopts);
     515                 :             : 
     516         [ +  + ]:        5681 :     if (!*hookopts)
     517                 :             :         return;
     518                 :             : 
     519                 :         461 :     bf_hookopts_clean(*hookopts);
     520                 :             :     freep((void *)hookopts);
     521                 :             : }
     522                 :             : 
     523                 :         130 : int bf_hookopts_pack(const struct bf_hookopts *hookopts, bf_wpack_t *pack)
     524                 :             : {
     525                 :             :     assert(hookopts);
     526                 :             :     assert(pack);
     527                 :             : 
     528         [ +  + ]:         130 :     if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_IFINDEX))
     529                 :          71 :         bf_wpack_kv_int(pack, "ifindex", hookopts->ifindex);
     530                 :             :     else
     531                 :             :         bf_wpack_kv_nil(pack, "ifindex");
     532                 :             : 
     533         [ +  + ]:         130 :     if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_CGPATH))
     534                 :          51 :         bf_wpack_kv_str(pack, "cgpath", hookopts->cgpath);
     535                 :             :     else
     536                 :             :         bf_wpack_kv_nil(pack, "cgpath");
     537                 :             : 
     538         [ -  + ]:         130 :     if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_FAMILY))
     539                 :           0 :         bf_wpack_kv_uint(pack, "family", hookopts->family);
     540                 :             :     else
     541                 :             :         bf_wpack_kv_nil(pack, "family");
     542                 :             : 
     543         [ +  + ]:         130 :     if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_PRIORITIES)) {
     544                 :           9 :         bf_wpack_open_array(pack, "priorities");
     545                 :           9 :         bf_wpack_int(pack, hookopts->priorities[0]);
     546                 :           9 :         bf_wpack_int(pack, hookopts->priorities[1]);
     547                 :           9 :         bf_wpack_close_array(pack);
     548                 :             :     } else {
     549                 :             :         bf_wpack_kv_nil(pack, "priorities");
     550                 :             :     }
     551                 :             : 
     552         [ -  + ]:         130 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     553                 :             : }
     554                 :             : 
     555                 :          19 : void bf_hookopts_dump(const struct bf_hookopts *hookopts, prefix_t *prefix)
     556                 :             : {
     557                 :             :     assert(hookopts);
     558                 :             :     assert(prefix);
     559                 :             : 
     560         [ -  + ]:          19 :     DUMP(prefix, "struct bf_hookopts at %p", hookopts);
     561                 :             : 
     562                 :          19 :     bf_dump_prefix_push(prefix);
     563         [ -  + ]:          19 :     DUMP(prefix, "used_opts: 0x%08x", hookopts->used_opts);
     564                 :             : 
     565         [ +  + ]:          95 :     for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
     566         [ +  + ]:          76 :         if (type == _BF_HOOKOPTS_MAX - 1)
     567                 :          19 :             bf_dump_prefix_last(prefix);
     568                 :             : 
     569         [ +  + ]:          76 :         if (bf_hookopts_is_used(hookopts, type))
     570                 :          20 :             _bf_hookopts_ops[type].dump(hookopts, prefix);
     571                 :             :         else
     572         [ -  + ]:          56 :             DUMP(prefix, "%s: <unset>", _bf_hookopts_ops[type].name);
     573                 :             :     }
     574                 :             : 
     575                 :          19 :     bf_dump_prefix_pop(prefix);
     576                 :          19 : }
     577                 :             : 
     578                 :         180 : int bf_hookopts_parse_opt(struct bf_hookopts *hookopts, const char *raw_opt)
     579                 :             : {
     580                 :             :     char *value;
     581                 :             :     struct bf_hookopts_ops *ops;
     582                 :             :     int r;
     583                 :             : 
     584                 :             :     assert(hookopts);
     585                 :             :     assert(raw_opt);
     586                 :             : 
     587                 :         180 :     value = strchr(raw_opt, '=');
     588         [ +  + ]:         180 :     if (!value)
     589                 :             :         return -ENOENT;
     590                 :             : 
     591                 :         178 :     *value = '\0';
     592                 :         178 :     ++value;
     593                 :             : 
     594                 :         178 :     ops = _bf_hookopts_get_ops(raw_opt);
     595         [ +  + ]:         178 :     if (!ops) {
     596         [ +  - ]:           2 :         return bf_err_r(-ENOTSUP, "unknown hook option '%s', ignoring",
     597                 :             :                         raw_opt);
     598                 :             :     }
     599                 :             : 
     600                 :         176 :     r = ops->parse(hookopts, value);
     601                 :         176 :     if (r < 0)
     602                 :             :         return r;
     603                 :             : 
     604                 :             :     return 0;
     605                 :             : }
     606                 :             : 
     607                 :           2 : int bf_hookopts_parse_opts(struct bf_hookopts *hookopts, bf_list *raw_opts)
     608                 :             : {
     609                 :             :     int r;
     610                 :             : 
     611                 :             :     assert(hookopts);
     612                 :             :     assert(raw_opts);
     613                 :             : 
     614         [ -  + ]:           2 :     if (!raw_opts)
     615                 :             :         return 0;
     616                 :             : 
     617   [ +  +  +  +  :          10 :     bf_list_foreach (raw_opts, raw_opt_node) {
                   +  + ]
     618                 :           3 :         r = bf_hookopts_parse_opt(hookopts,
     619                 :             :                                   bf_list_node_get_data(raw_opt_node));
     620         [ +  - ]:           3 :         if (r)
     621                 :             :             return r;
     622                 :             :     }
     623                 :             : 
     624                 :             :     return 0;
     625                 :             : }
     626                 :             : 
     627                 :         139 : int bf_hookopts_validate(const struct bf_hookopts *hookopts, enum bf_hook hook)
     628                 :             : {
     629                 :         139 :     enum bf_flavor flavor = bf_hook_to_flavor(hook);
     630                 :             : 
     631                 :             :     assert(hookopts);
     632                 :             : 
     633         [ +  + ]:         653 :     for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
     634                 :             :         struct bf_hookopts_ops *ops = &_bf_hookopts_ops[type];
     635                 :         528 :         bool is_used = bf_hookopts_is_used(hookopts, type);
     636                 :         528 :         bool is_required = _bf_hookopts_is_required(type, flavor);
     637   [ +  -  +  + ]:         528 :         bool is_supported = _bf_hookopts_is_supported(type, flavor);
     638                 :             : 
     639         [ +  + ]:         528 :         if (is_required && !is_used) {
     640         [ +  - ]:          11 :             return bf_err_r(-EINVAL,
     641                 :             :                             "hook option '%s' is required for '%s' chains",
     642                 :             :                             ops->name, bf_hook_to_str(hook));
     643                 :             :         }
     644                 :             : 
     645   [ +  +  +  + ]:         517 :         if (is_used && !(is_supported | is_required)) {
     646         [ +  - ]:           3 :             return bf_err_r(-ENOTSUP,
     647                 :             :                             "hook option '%s' is not supported for '%s' chains",
     648                 :             :                             ops->name, bf_hook_to_str(hook));
     649                 :             :         }
     650                 :             :     }
     651                 :             : 
     652                 :             :     return 0;
     653                 :             : }
        

Generated by: LCOV version 2.0-1