LCOV - code coverage report
Current view: top level - libbpfilter - hook.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 34.1 % 232 79
Test Date: 2025-09-30 16:37:25 Functions: 42.3 % 26 11

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

Generated by: LCOV version 2.0-1