LCOV - code coverage report
Current view: top level - core - hook.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 35.7 % 221 79
Test Date: 2025-05-09 22:39:08 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 "core/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 "core/dump.h"
      21              : #include "core/flavor.h"
      22              : #include "core/helper.h"
      23              : #include "core/list.h"
      24              : #include "core/logger.h"
      25              : #include "core/marsh.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           14 : 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           14 :     return flavors[hook];
      78              : }
      79              : 
      80           10 : enum bpf_attach_type bf_hook_to_bpf_attach_type(enum bf_hook hook)
      81              : {
      82              :     static const enum bpf_attach_type attach_types[] = {
      83              :         [BF_HOOK_XDP] = 0,
      84              :         [BF_HOOK_TC_INGRESS] = BPF_TCX_INGRESS,
      85              :         [BF_HOOK_NF_PRE_ROUTING] = BPF_NETFILTER,
      86              :         [BF_HOOK_NF_LOCAL_IN] = BPF_NETFILTER,
      87              :         [BF_HOOK_CGROUP_INGRESS] = BPF_CGROUP_INET_INGRESS,
      88              :         [BF_HOOK_CGROUP_EGRESS] = BPF_CGROUP_INET_EGRESS,
      89              :         [BF_HOOK_NF_FORWARD] = BPF_NETFILTER,
      90              :         [BF_HOOK_NF_LOCAL_OUT] = BPF_NETFILTER,
      91              :         [BF_HOOK_NF_POST_ROUTING] = BPF_NETFILTER,
      92              :         [BF_HOOK_TC_EGRESS] = BPF_TCX_EGRESS,
      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 bpf_prog_type bf_hook_to_bpf_prog_type(enum bf_hook hook)
     102              : {
     103              :     static const enum bpf_prog_type prog_types[] = {
     104              :         [BF_HOOK_XDP] = BPF_PROG_TYPE_XDP,
     105              :         [BF_HOOK_TC_INGRESS] = BPF_PROG_TYPE_SCHED_CLS,
     106              :         [BF_HOOK_NF_PRE_ROUTING] = BPF_PROG_TYPE_NETFILTER,
     107              :         [BF_HOOK_NF_LOCAL_IN] = BPF_PROG_TYPE_NETFILTER,
     108              :         [BF_HOOK_CGROUP_INGRESS] = BPF_PROG_TYPE_CGROUP_SKB,
     109              :         [BF_HOOK_CGROUP_EGRESS] = BPF_PROG_TYPE_CGROUP_SKB,
     110              :         [BF_HOOK_NF_FORWARD] = BPF_PROG_TYPE_NETFILTER,
     111              :         [BF_HOOK_NF_LOCAL_OUT] = BPF_PROG_TYPE_NETFILTER,
     112              :         [BF_HOOK_NF_POST_ROUTING] = BPF_PROG_TYPE_NETFILTER,
     113              :         [BF_HOOK_TC_EGRESS] = 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 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 NF_INET_PRE_ROUTING;
     127            1 :     case BF_HOOK_NF_LOCAL_IN:
     128            1 :         return NF_INET_LOCAL_IN;
     129            1 :     case BF_HOOK_NF_FORWARD:
     130            1 :         return NF_INET_FORWARD;
     131            1 :     case BF_HOOK_NF_LOCAL_OUT:
     132            1 :         return NF_INET_LOCAL_OUT;
     133            1 :     case BF_HOOK_NF_POST_ROUTING:
     134            1 :         return NF_INET_POST_ROUTING;
     135            2 :     default:
     136            2 :         bf_warn("bf_hook %s (%d) is not an 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 nf_inet_hooks hook)
     143              : {
     144              :     switch (hook) {
     145              :     case NF_INET_PRE_ROUTING:
     146              :         return BF_HOOK_NF_PRE_ROUTING;
     147              :     case NF_INET_LOCAL_IN:
     148              :         return BF_HOOK_NF_LOCAL_IN;
     149              :     case NF_INET_FORWARD:
     150              :         return BF_HOOK_NF_FORWARD;
     151              :     case NF_INET_LOCAL_OUT:
     152              :         return BF_HOOK_NF_LOCAL_OUT;
     153              :     case 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 nf_inet_hooks hook)
     163              : {
     164            6 :     switch (hook) {
     165              :     case NF_INET_PRE_ROUTING:
     166              :         return "nf_prerouting";
     167            1 :     case NF_INET_LOCAL_IN:
     168            1 :         return "nf_input";
     169            1 :     case NF_INET_FORWARD:
     170            1 :         return "nf_forward";
     171            1 :     case NF_INET_LOCAL_OUT:
     172            1 :         return "nf_output";
     173            1 :     case 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_marsh(struct bf_hookopts **hookopts,
     393              :                                const struct bf_marsh *marsh)
     394              : {
     395            0 :     _free_bf_hookopts_ struct bf_hookopts *_hookopts = NULL;
     396              :     struct bf_marsh *child = NULL;
     397              :     int r;
     398              : 
     399            0 :     bf_assert(hookopts && marsh);
     400              : 
     401            0 :     r = bf_hookopts_new(&_hookopts);
     402            0 :     if (r)
     403              :         return r;
     404              : 
     405            0 :     if (!(child = bf_marsh_next_child(marsh, child)))
     406            0 :         return bf_err_r(-EINVAL, "bf_hookopts: missing used_opts field");
     407            0 :     memcpy(&_hookopts->used_opts, child->data, sizeof(_hookopts->used_opts));
     408              : 
     409            0 :     if (!(child = bf_marsh_next_child(marsh, child)))
     410            0 :         return bf_err_r(-EINVAL, "bf_hookopts: missing ifindex field");
     411            0 :     memcpy(&_hookopts->ifindex, child->data, sizeof(_hookopts->ifindex));
     412              : 
     413            0 :     if (!(child = bf_marsh_next_child(marsh, child)))
     414            0 :         return bf_err_r(-EINVAL, "bf_hookopts: missing cgpath field");
     415            0 :     if (child->data_len) {
     416            0 :         _hookopts->cgpath = strdup(child->data);
     417            0 :         if (!_hookopts->cgpath)
     418              :             return -ENOMEM;
     419              :     }
     420              : 
     421            0 :     if (!(child = bf_marsh_next_child(marsh, child)))
     422            0 :         return bf_err_r(-EINVAL, "bf_hookopts: missing family field");
     423            0 :     memcpy(&_hookopts->family, child->data, sizeof(_hookopts->family));
     424              : 
     425            0 :     if (!(child = bf_marsh_next_child(marsh, child)))
     426            0 :         return bf_err_r(-EINVAL, "bf_hookopts: missing priorities field");
     427            0 :     memcpy(&_hookopts->priorities, child->data, sizeof(_hookopts->priorities));
     428              : 
     429            0 :     if (bf_marsh_next_child(marsh, child))
     430            0 :         return bf_err_r(-E2BIG, "too many serialized fields for bf_hookopts");
     431              : 
     432            0 :     *hookopts = TAKE_PTR(_hookopts);
     433              : 
     434            0 :     return 0;
     435              : }
     436              : 
     437            0 : void bf_hookopts_clean(struct bf_hookopts *hookopts)
     438              : {
     439              :     freep((void *)&hookopts->cgpath);
     440            0 : }
     441              : 
     442            4 : void bf_hookopts_free(struct bf_hookopts **hookopts)
     443              : {
     444            4 :     bf_assert(hookopts);
     445              : 
     446            4 :     if (!*hookopts)
     447              :         return;
     448              : 
     449            0 :     bf_hookopts_clean(*hookopts);
     450              :     freep((void *)hookopts);
     451              : }
     452              : 
     453            0 : int bf_hookopts_marsh(const struct bf_hookopts *hookopts,
     454              :                       struct bf_marsh **marsh)
     455              : {
     456            0 :     _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
     457              :     int r = 0;
     458              : 
     459            0 :     bf_assert(hookopts && marsh);
     460              : 
     461            0 :     r = bf_marsh_new(&_marsh, NULL, 0);
     462            0 :     if (r)
     463            0 :         return bf_err_r(r, "failed to create a new marsh for bf_hookopts");
     464              : 
     465            0 :     r = bf_marsh_add_child_raw(&_marsh, &hookopts->used_opts,
     466              :                                sizeof(hookopts->used_opts));
     467            0 :     if (r)
     468            0 :         return bf_err_r(r, "failed to marsh bf_hookopts.used_opts");
     469              : 
     470            0 :     r = bf_marsh_add_child_raw(&_marsh, &hookopts->ifindex,
     471              :                                sizeof(hookopts->ifindex));
     472            0 :     if (r)
     473            0 :         return bf_err_r(r, "failed to marsh bf_hookopts.ifindex");
     474              : 
     475            0 :     r = bf_marsh_add_child_raw(&_marsh, hookopts->cgpath,
     476            0 :                                hookopts->cgpath ? strlen(hookopts->cgpath) + 1 :
     477              :                                                   0);
     478            0 :     if (r)
     479            0 :         return bf_err_r(r, "failed to marsh bf_hookopts.cgpath");
     480              : 
     481            0 :     r = bf_marsh_add_child_raw(&_marsh, &hookopts->family,
     482              :                                sizeof(hookopts->family));
     483            0 :     if (r)
     484            0 :         return bf_err_r(r, "failed to marsh bf_hookopts.family");
     485              : 
     486            0 :     r = bf_marsh_add_child_raw(&_marsh, hookopts->priorities,
     487              :                                sizeof(hookopts->priorities));
     488            0 :     if (r)
     489            0 :         return bf_err_r(r, "failed to marsh bf_hookopts.priorities");
     490              : 
     491            0 :     *marsh = TAKE_PTR(_marsh);
     492              : 
     493            0 :     return 0;
     494              : }
     495              : 
     496            0 : void bf_hookopts_dump(const struct bf_hookopts *hookopts, prefix_t *prefix)
     497              : {
     498            0 :     bf_assert(hookopts && prefix);
     499              : 
     500            0 :     DUMP(prefix, "struct bf_hookopts at %p", hookopts);
     501              : 
     502            0 :     bf_dump_prefix_push(prefix);
     503            0 :     DUMP(prefix, "used_opts: 0x%08x", hookopts->used_opts);
     504              : 
     505            0 :     for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
     506            0 :         if (type == _BF_HOOKOPTS_MAX - 1)
     507            0 :             bf_dump_prefix_last(prefix);
     508              : 
     509            0 :         if (bf_hookopts_is_used(hookopts, type))
     510            0 :             _bf_hookopts_ops[type].dump(hookopts, prefix);
     511              :         else
     512            0 :             DUMP(prefix, "%s: <unset>", _bf_hookopts_ops[type].name);
     513              :     }
     514              : 
     515            0 :     bf_dump_prefix_pop(prefix);
     516            0 : }
     517              : 
     518            0 : int bf_hookopts_parse_opt(struct bf_hookopts *hookopts, const char *raw_opt)
     519              : {
     520              :     char *value;
     521              :     struct bf_hookopts_ops *ops;
     522              :     int r;
     523              : 
     524            0 :     bf_assert(hookopts && raw_opt);
     525              : 
     526            0 :     value = strchr(raw_opt, '=');
     527            0 :     if (!value)
     528              :         return -ENOENT;
     529              : 
     530            0 :     *value = '\0';
     531            0 :     ++value;
     532              : 
     533            0 :     ops = _bf_hookopts_get_ops(raw_opt);
     534            0 :     if (!ops) {
     535            0 :         return bf_err_r(-ENOTSUP, "unknown hook option '%s', ignoring",
     536              :                         raw_opt);
     537              :     }
     538              : 
     539            0 :     r = ops->parse(hookopts, value);
     540            0 :     if (r < 0)
     541              :         return r;
     542              : 
     543              :     return 0;
     544              : }
     545              : 
     546            0 : int bf_hookopts_parse_opts(struct bf_hookopts *hookopts, bf_list *raw_opts)
     547              : {
     548              :     int r;
     549              : 
     550            0 :     bf_assert(hookopts && raw_opts);
     551              : 
     552            0 :     if (!raw_opts)
     553              :         return 0;
     554              : 
     555            0 :     bf_list_foreach (raw_opts, raw_opt_node) {
     556            0 :         r = bf_hookopts_parse_opt(hookopts,
     557            0 :                                   bf_list_node_get_data(raw_opt_node));
     558            0 :         if (r)
     559              :             return r;
     560              :     }
     561              : 
     562              :     return 0;
     563              : }
     564              : 
     565            0 : int bf_hookopts_validate(const struct bf_hookopts *hookopts, enum bf_hook hook)
     566              : {
     567            0 :     enum bf_flavor flavor = bf_hook_to_flavor(hook);
     568              : 
     569            0 :     bf_assert(hookopts);
     570              : 
     571            0 :     for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
     572              :         struct bf_hookopts_ops *ops = &_bf_hookopts_ops[type];
     573            0 :         bool is_used = bf_hookopts_is_used(hookopts, type);
     574            0 :         bool is_required = _bf_hookopts_is_required(type, flavor);
     575            0 :         bool is_supported = _bf_hookopts_is_supported(type, flavor);
     576              : 
     577            0 :         if (is_required && !is_used) {
     578            0 :             return bf_err_r(-EINVAL,
     579              :                             "hook option '%s' is required for '%s' chains",
     580              :                             ops->name, bf_hook_to_str(hook));
     581              :         }
     582              : 
     583            0 :         if (is_used && !(is_supported | is_required)) {
     584            0 :             return bf_err_r(-ENOTSUP,
     585              :                             "hook option '%s' is not supported for '%s' chains",
     586              :                             ops->name, bf_hook_to_str(hook));
     587              :         }
     588              :     }
     589              : 
     590              :     return 0;
     591              : }
        

Generated by: LCOV version 2.0-1