LCOV - code coverage report
Current view: top level - libbpfilter - chain.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 92.5 % 280 259
Test Date: 2026-11-05 13:56:16 Functions: 100.0 % 17 17
Branches: 69.1 % 388 268

             Branch data     Line data    Source code
       1                 :             : /* SPDX-License-Identifier: GPL-2.0-only */
       2                 :             : /*
       3                 :             :  * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
       4                 :             :  */
       5                 :             : 
       6                 :             : #include "bpfilter/chain.h"
       7                 :             : 
       8                 :             : #include <errno.h>
       9                 :             : #include <limits.h>
      10                 :             : #include <stdlib.h>
      11                 :             : #include <string.h>
      12                 :             : 
      13                 :             : #include "bpfilter/core/hashset.h"
      14                 :             : #include "bpfilter/core/list.h"
      15                 :             : #include "bpfilter/dump.h"
      16                 :             : #include "bpfilter/helper.h"
      17                 :             : #include "bpfilter/hook.h"
      18                 :             : #include "bpfilter/logger.h"
      19                 :             : #include "bpfilter/matcher.h"
      20                 :             : #include "bpfilter/pack.h"
      21                 :             : #include "bpfilter/rule.h"
      22                 :             : #include "bpfilter/set.h"
      23                 :             : #include "bpfilter/verdict.h"
      24                 :             : 
      25                 :             : /**
      26                 :             :  * @brief Check if a rule can't match because it references an unmatchable set.
      27                 :             :  *
      28                 :             :  * Check if a rule contains a matcher on a set that can't match any packet:
      29                 :             :  * - `... in {}`: the reference value can't be found in the set, because the
      30                 :             :  *   set is empty. The rule will never match.
      31                 :             :  * - `... not in {}`: the set is empty, but we test for the negative: the
      32                 :             :  *   reference value won't be in the set. The rule can match.
      33                 :             :  *
      34                 :             :  * @param chain Chain containing the sets. Can't be NULL.
      35                 :             :  * @param rule Rule to check. Can't be NULL.
      36                 :             :  * @return 0 if the rule has no set matcher, or a set matcher that might match
      37                 :             :  *         a packet. 1 if the rule contains a set matcher that can't match any
      38                 :             :  *         packet. A negative errno value on failure.
      39                 :             :  */
      40                 :        9967 : static int _bf_rule_has_unmatchable_set_matcher(const struct bf_chain *chain,
      41                 :             :                                                 const struct bf_rule *rule)
      42                 :             : {
      43                 :             :     assert(chain);
      44                 :             :     assert(rule);
      45                 :             : 
      46   [ +  +  +  +  :       44960 :     bf_list_foreach (&rule->matchers, matcher_node) {
                   +  + ]
      47                 :             :         struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
      48                 :             :         const struct bf_set *set;
      49                 :             : 
      50         [ +  + ]:       12552 :         if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
      51                 :       10376 :             continue;
      52                 :             : 
      53                 :        2176 :         set = bf_chain_get_set_for_matcher(chain, matcher);
      54         [ +  + ]:        2176 :         if (!set) {
      55         [ +  - ]:           2 :             return bf_err_r(-EINVAL, "rule %u references non-existent set",
      56                 :             :                             rule->index);
      57                 :             :         }
      58                 :             : 
      59   [ +  +  +  + ]:        2174 :         if (bf_set_is_empty(set) && !bf_matcher_get_negate(matcher)) {
      60         [ +  - ]:          37 :             bf_warn("rule %u references empty set, rule will be disabled",
      61                 :             :                     rule->index);
      62                 :             :             return 1;
      63                 :             :         }
      64                 :             :     }
      65                 :             :     return 0;
      66                 :             : }
      67                 :             : 
      68                 :             : /**
      69                 :             :  * @brief Check a single matcher type against per-layer header tracking.
      70                 :             :  *
      71                 :             :  * @param type Matcher type to check.
      72                 :             :  * @param layer_hdr_id Per-layer header ID tracking array.
      73                 :             :  * @return 0 if compatible, 1 if incompatible, negative errno on error.
      74                 :             :  */
      75                 :       12810 : static int _bf_rule_check_layer(enum bf_matcher_type type,
      76                 :             :                                 uint32_t layer_hdr_id[_BF_MATCHER_LAYER_MAX])
      77                 :             : {
      78                 :             :     const struct bf_matcher_meta *meta;
      79                 :             : 
      80                 :             :     assert(layer_hdr_id);
      81                 :             : 
      82                 :       12810 :     meta = bf_matcher_get_meta(type);
      83         [ -  + ]:       12810 :     if (!meta) {
      84         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "missing meta for '%s'",
      85                 :             :                         bf_matcher_type_to_str(type));
      86                 :             :     }
      87                 :             : 
      88         [ +  + ]:       12810 :     if (meta->layer <= BF_MATCHER_NO_LAYER)
      89                 :             :         return 0;
      90                 :             : 
      91         [ +  + ]:        9781 :     if (layer_hdr_id[meta->layer] == UINT32_MAX) {
      92                 :        7781 :         layer_hdr_id[meta->layer] = meta->hdr_id;
      93                 :        7781 :         return 0;
      94                 :             :     }
      95                 :             : 
      96         [ +  + ]:        2000 :     if (layer_hdr_id[meta->layer] != meta->hdr_id)
      97                 :          24 :         return 1;
      98                 :             : 
      99                 :             :     return 0;
     100                 :             : }
     101                 :             : 
     102                 :             : /**
     103                 :             :  * @brief Check if a rule has matchers on the same layer but different
     104                 :             :  * protocols.
     105                 :             :  *
     106                 :             :  * A rule matching on e.g. both IPv4 source address and IPv6 destination
     107                 :             :  * address can never match a packet (a packet is either IPv4 or IPv6, not
     108                 :             :  * both). Same for layer 4 (e.g. TCP vs UDP). Such rules should be disabled.
     109                 :             :  *
     110                 :             :  * @param chain Chain containing the sets.
     111                 :             :  * @param rule Rule to check.
     112                 :             :  * @return 0 if no conflict, 1 if the rule contains incompatible matchers, or
     113                 :             :  *         a negative errno value on failure.
     114                 :             :  */
     115                 :        9928 : static int _bf_rule_has_incompatible_matchers(const struct bf_chain *chain,
     116                 :             :                                               const struct bf_rule *rule)
     117                 :             : {
     118                 :             :     uint32_t layer_hdr_id[_BF_MATCHER_LAYER_MAX];
     119                 :             : 
     120                 :             :     assert(chain);
     121                 :             :     assert(rule);
     122                 :             : 
     123         [ +  + ]:       59568 :     for (int i = 0; i < _BF_MATCHER_LAYER_MAX; ++i)
     124                 :       49640 :         layer_hdr_id[i] = UINT32_MAX;
     125                 :             : 
     126   [ +  +  +  +  :       44752 :     bf_list_foreach (&rule->matchers, matcher_node) {
                   +  + ]
     127                 :             :         struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
     128                 :             :         int r = 0;
     129                 :             : 
     130         [ +  + ]:       12472 :         if (bf_matcher_get_type(matcher) == BF_MATCHER_SET) {
     131                 :             :             const struct bf_set *set =
     132                 :        2109 :                 bf_chain_get_set_for_matcher(chain, matcher);
     133                 :             : 
     134         [ -  + ]:        2109 :             if (!set) {
     135         [ #  # ]:           0 :                 return bf_err_r(-ENOENT, "rule %u references non-existent set",
     136                 :             :                                 rule->index);
     137                 :             :             }
     138                 :             : 
     139   [ +  +  +  - ]:        4556 :             for (size_t i = 0; i < set->n_comps && !r; ++i)
     140                 :        2447 :                 r = _bf_rule_check_layer(set->key[i], layer_hdr_id);
     141                 :             :         } else {
     142                 :       10363 :             r = _bf_rule_check_layer(bf_matcher_get_type(matcher),
     143                 :             :                                      layer_hdr_id);
     144                 :             :         }
     145                 :             : 
     146         [ +  - ]:       12472 :         if (r < 0)
     147                 :             :             return r;
     148         [ +  + ]:       12472 :         if (r) {
     149         [ +  - ]:          24 :             bf_warn(
     150                 :             :                 "rule %u has incompatible matchers on the same layer, rule will be disabled",
     151                 :             :                 rule->index);
     152                 :             :             return 1;
     153                 :             :         }
     154                 :             :     }
     155                 :             : 
     156                 :             :     return 0;
     157                 :             : }
     158                 :             : 
     159                 :        9967 : static int _bf_chain_check_rule(struct bf_chain *chain, struct bf_rule *rule)
     160                 :             : {
     161                 :             :     const uint8_t tuple = BF_FLAG(BF_LOG_OPT_5_TUPLE);
     162                 :             :     int r;
     163                 :             : 
     164                 :             :     assert(rule);
     165                 :             : 
     166                 :        9967 :     r = _bf_rule_has_unmatchable_set_matcher(chain, rule);
     167         [ +  + ]:        9967 :     if (r < 0)
     168                 :             :         return r;
     169                 :        9965 :     rule->disabled = r;
     170                 :             : 
     171         [ +  + ]:        9965 :     if (!rule->disabled) {
     172                 :        9928 :         r = _bf_rule_has_incompatible_matchers(chain, rule);
     173         [ +  - ]:        9928 :         if (r < 0)
     174                 :             :             return r;
     175                 :             : 
     176                 :        9928 :         rule->disabled = r;
     177                 :             :     }
     178                 :             : 
     179   [ +  +  +  +  :        9965 :     if (rule->log != BF_LOG_OPT_DEFAULT && (rule->log & tuple) &&
                   +  + ]
     180                 :             :         rule->log != tuple) {
     181         [ +  - ]:           3 :         return bf_err_r(
     182                 :             :             -EINVAL,
     183                 :             :             "5-tuple logging can't be combined with packet layer options");
     184                 :             :     }
     185                 :             : 
     186   [ +  +  +  + ]:       10114 :     if (rule->log && rule->log != BF_LOG_OPT_DEFAULT &&
     187                 :         152 :         bf_hook_to_flavor(chain->hook) == BF_FLAVOR_CGROUP_SOCK_ADDR) {
     188         [ +  - ]:           6 :         return bf_err_r(
     189                 :             :             -ENOTSUP,
     190                 :             :             "per-field log options are not supported by %s, use 'log'",
     191                 :             :             bf_hook_to_str(chain->hook));
     192                 :             :     }
     193                 :             : 
     194   [ +  +  +  + ]:        9956 :     if (rule->log && !rule->disabled)
     195                 :         200 :         chain->flags |= BF_FLAG(BF_CHAIN_LOG);
     196                 :             : 
     197   [ +  +  +  +  :        9956 :     if (rule->log && rule->log_rate_ns && !rule->disabled)
                   +  + ]
     198                 :          54 :         chain->flags |= BF_FLAG(BF_CHAIN_LOG_RATELIMIT);
     199                 :             : 
     200   [ +  +  +  + ]:        9981 :     if (bf_rule_mark_is_set(rule) &&
     201         [ +  + ]:          36 :         bf_hook_to_flavor(chain->hook) != BF_FLAVOR_TC &&
     202                 :          11 :         bf_hook_to_flavor(chain->hook) != BF_FLAVOR_CGROUP_SKB) {
     203         [ +  - ]:           3 :         return bf_err_r(-EINVAL, "%s chains can't set packet mark",
     204                 :             :                         bf_hook_to_str(chain->hook));
     205                 :             :     }
     206                 :             : 
     207   [ +  +  +  +  :       44822 :     bf_list_foreach (&rule->matchers, matcher_node) {
                   +  + ]
     208                 :             :         struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
     209                 :             :         const struct bf_matcher_meta *meta;
     210                 :             : 
     211                 :             :         // Track if the chain uses IPv6 nexthdr matcher.
     212         [ +  + ]:       12548 :         if (bf_matcher_get_type(matcher) == BF_MATCHER_IP6_NEXTHDR &&
     213         [ +  + ]:         305 :             !rule->disabled)
     214                 :         303 :             chain->flags |= BF_FLAG(BF_CHAIN_STORE_NEXTHDR);
     215                 :             : 
     216                 :             :         // Ensure the matcher is compatible with the chain's hook.
     217                 :       12548 :         meta = bf_matcher_get_meta(bf_matcher_get_type(matcher));
     218         [ -  + ]:       12548 :         if (!meta) {
     219         [ #  # ]:           0 :             return bf_err_r(-EINVAL, "unknown matcher type %d in rule",
     220                 :             :                             bf_matcher_get_type(matcher));
     221                 :             :         }
     222                 :             : 
     223         [ +  + ]:       12548 :         if (meta->unsupported_hooks & BF_FLAG(chain->hook)) {
     224         [ +  - ]:          76 :             return bf_err_r(
     225                 :             :                 -ENOTSUP, "matcher %s is not compatible with %s",
     226                 :             :                 bf_matcher_type_to_str(bf_matcher_get_type(matcher)),
     227                 :             :                 bf_hook_to_str(chain->hook));
     228                 :             :         }
     229                 :             : 
     230         [ +  + ]:       12472 :         if (bf_matcher_get_type(matcher) == BF_MATCHER_SET) {
     231                 :             :             const struct bf_set *set =
     232                 :        2178 :                 bf_chain_get_set_for_matcher(chain, matcher);
     233                 :             : 
     234         [ -  + ]:        2178 :             if (!set) {
     235         [ #  # ]:           0 :                 return bf_err_r(-ENOENT, "rule %u references non-existent set",
     236                 :             :                                 rule->index);
     237                 :             :             }
     238                 :             : 
     239         [ +  + ]:        4681 :             for (size_t i = 0; i < set->n_comps; ++i) {
     240                 :             :                 const struct bf_matcher_meta *comp_meta =
     241                 :        2517 :                     bf_matcher_get_meta(set->key[i]);
     242                 :             : 
     243         [ -  + ]:        2517 :                 if (!comp_meta) {
     244         [ #  # ]:           0 :                     return bf_err_r(-EINVAL,
     245                 :             :                                     "missing meta for set component '%s'",
     246                 :             :                                     bf_matcher_type_to_str(set->key[i]));
     247                 :             :                 }
     248                 :             : 
     249         [ +  + ]:        2517 :                 if (comp_meta->unsupported_hooks & BF_FLAG(chain->hook)) {
     250         [ +  - ]:          14 :                     return bf_err_r(-ENOTSUP,
     251                 :             :                                     "set component '%s' not compatible with %s",
     252                 :             :                                     bf_matcher_type_to_str(set->key[i]),
     253                 :             :                                     bf_hook_to_str(chain->hook));
     254                 :             :                 }
     255                 :             :             }
     256                 :             :         }
     257                 :             :     }
     258                 :             : 
     259                 :             :     return 0;
     260                 :             : }
     261                 :             : 
     262                 :        9443 : static int _bf_chain_validate_rules(struct bf_chain *chain)
     263                 :             : {
     264                 :             :     int r;
     265                 :             : 
     266                 :             :     assert(chain);
     267                 :             : 
     268                 :        9443 :     chain->flags = 0;
     269                 :             : 
     270   [ +  +  +  +  :       32562 :     bf_list_foreach (&chain->rules, rule_node) {
                   +  + ]
     271                 :             :         struct bf_rule *rule = bf_list_node_get_data(rule_node);
     272                 :             : 
     273                 :        6940 :         r = _bf_chain_check_rule(chain, rule);
     274         [ +  + ]:        6940 :         if (r)
     275                 :             :             return r;
     276                 :             :     }
     277                 :             : 
     278                 :             :     return 0;
     279                 :             : }
     280                 :             : 
     281                 :        9425 : int bf_chain_new(struct bf_chain **chain, const char *name, enum bf_hook hook,
     282                 :             :                  enum bf_verdict policy, bf_hashset *sets, bf_list *rules)
     283                 :             : {
     284                 :        9425 :     _free_bf_chain_ struct bf_chain *_chain = NULL;
     285                 :             :     size_t ridx = 0;
     286                 :             :     int r;
     287                 :             : 
     288                 :             :     assert(chain && name);
     289         [ -  + ]:        9425 :     if (hook >= _BF_HOOK_MAX)
     290         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unknown hook type");
     291         [ +  + ]:        9425 :     if (!bf_verdict_is_valid_policy(policy))
     292         [ +  - ]:           2 :         return bf_err_r(-EINVAL, "invalid policy '%s'",
     293                 :             :                         bf_verdict_to_str(policy));
     294                 :             : 
     295         [ +  + ]:        9423 :     if (sets) {
     296   [ +  +  +  +  :       13730 :         bf_hashset_foreach (sets, set_node) {
                   +  + ]
     297                 :         183 :             const struct bf_set *set = set_node->data;
     298                 :             : 
     299         [ -  + ]:         183 :             if (!bf_set_is_valid_name(set->name))
     300         [ #  # ]:           0 :                 return bf_err_r(-EINVAL, "chain sets must have valid names");
     301                 :             :         }
     302                 :             :     }
     303                 :             : 
     304                 :        9423 :     _chain = calloc(1, sizeof(*_chain));
     305         [ +  - ]:        9423 :     if (!_chain)
     306                 :             :         return -ENOMEM;
     307                 :             : 
     308                 :        9423 :     _chain->name = strdup(name);
     309         [ +  - ]:        9423 :     if (!_chain->name)
     310                 :             :         return -ENOMEM;
     311                 :             : 
     312                 :        9423 :     _chain->hook = hook;
     313                 :        9423 :     _chain->policy = policy;
     314                 :             : 
     315                 :        9423 :     _chain->sets = bf_set_hashset_default();
     316         [ +  + ]:        9423 :     if (sets)
     317                 :        6682 :         _chain->sets = bf_hashset_move(sets);
     318                 :             : 
     319                 :        9423 :     _chain->rules = bf_list_default(bf_rule_free, bf_rule_pack);
     320         [ +  + ]:        9423 :     if (rules)
     321                 :        6639 :         _chain->rules = bf_list_move(rules);
     322   [ +  +  +  + ]:       26098 :     bf_list_foreach (&_chain->rules, rule_node) {
     323                 :             :         struct bf_rule *rule = bf_list_node_get_data(rule_node);
     324                 :             : 
     325         [ +  + ]:        6918 :         rule->index = ridx++;
     326                 :             :     }
     327                 :             : 
     328                 :        9423 :     r = _bf_chain_validate_rules(_chain);
     329         [ +  + ]:        9423 :     if (r) {
     330         [ +  - ]:         102 :         if (rules)
     331                 :         102 :             *rules = bf_list_move(&_chain->rules);
     332         [ +  + ]:         102 :         if (sets)
     333                 :          99 :             *sets = bf_hashset_move(&_chain->sets);
     334                 :             :         return r;
     335                 :             :     }
     336                 :             : 
     337                 :        9321 :     *chain = TAKE_PTR(_chain);
     338                 :             : 
     339                 :        9321 :     return 0;
     340                 :             : }
     341                 :             : 
     342                 :        5805 : int bf_chain_new_from_pack(struct bf_chain **chain, bf_rpack_node_t node)
     343                 :             : {
     344                 :        5805 :     _free_bf_chain_ struct bf_chain *_chain = NULL;
     345                 :        5805 :     _cleanup_free_ char *name = NULL;
     346                 :             :     enum bf_hook hook;
     347                 :             :     enum bf_verdict policy;
     348                 :             :     bf_rpack_node_t array, array_node;
     349                 :       11610 :     _clean_bf_hashset_ bf_hashset sets = bf_set_hashset_default();
     350                 :        5805 :     _clean_bf_list_ bf_list rules = bf_list_default(bf_rule_free, bf_rule_pack);
     351                 :             :     int r;
     352                 :             : 
     353                 :        5805 :     r = bf_rpack_kv_str(node, "name", &name);
     354         [ -  + ]:        5805 :     if (r)
     355         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_chain.name");
     356                 :             : 
     357   [ -  +  -  +  :        5805 :     r = bf_rpack_kv_enum(node, "hook", &hook, 0, _BF_HOOK_MAX);
                   -  - ]
     358                 :             :     if (r)
     359         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_chain.hook");
     360                 :             : 
     361   [ -  +  -  +  :        5805 :     r = bf_rpack_kv_enum(node, "policy", &policy, 0, _BF_VERDICT_MAX);
                   -  - ]
     362                 :             :     if (r)
     363         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_chain.policy");
     364                 :             : 
     365                 :        5805 :     r = bf_rpack_kv_array(node, "sets", &array);
     366         [ -  + ]:        5805 :     if (r)
     367         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_chain.sets");
     368   [ +  +  +  +  :        6021 :     bf_rpack_array_foreach (array, array_node) {
                   +  + ]
     369                 :         109 :         _free_bf_set_ struct bf_set *set = NULL;
     370                 :             : 
     371                 :         109 :         r = bf_set_new_from_pack(&set, array_node);
     372         [ -  + ]:         109 :         if (r)
     373         [ #  # ]:           0 :             return bf_err_r(r, "failed to unpack bf_set into bf_chain.sets");
     374                 :             : 
     375         [ -  + ]:         109 :         if (bf_set_is_anonymous(set))
     376         [ #  # ]:           0 :             return bf_err_r(-EINVAL, "anonymous set outside a matcher");
     377                 :             : 
     378                 :         109 :         r = bf_hashset_add(&sets, (void **)&set);
     379         [ +  + ]:         109 :         if (r)
     380         [ +  - ]:           1 :             return bf_err_r(r, "failed to add unpacked set to chain");
     381                 :             :     }
     382                 :             : 
     383                 :        5804 :     r = bf_rpack_kv_array(node, "rules", &array);
     384         [ -  + ]:        5804 :     if (r)
     385         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_chain.rules");
     386   [ +  +  +  +  :       17362 :     bf_rpack_array_foreach (array, array_node) {
                   +  + ]
     387                 :       11558 :         _free_bf_rule_ struct bf_rule *rule = NULL;
     388                 :             : 
     389   [ +  -  +  -  :        5779 :         r = bf_list_emplace(&rules, bf_rule_new_from_pack, rule, array_node,
             -  -  -  - ]
     390                 :             :                             &sets);
     391                 :             :         if (r) {
     392         [ #  # ]:           0 :             return bf_err_r(r, "failed to unpack bf_rule into bf_chain.rules");
     393                 :             :         }
     394                 :             :     }
     395                 :             : 
     396                 :        5804 :     r = bf_chain_new(&_chain, name, hook, policy, &sets, &rules);
     397         [ -  + ]:        5804 :     if (r)
     398         [ #  # ]:           0 :         return bf_err_r(r, "failed to create bf_chain from pack");
     399                 :             : 
     400                 :        5804 :     *chain = TAKE_PTR(_chain);
     401                 :             : 
     402                 :        5804 :     return 0;
     403                 :             : }
     404                 :             : 
     405                 :       30910 : void bf_chain_free(struct bf_chain **chain)
     406                 :             : {
     407                 :             :     assert(chain);
     408                 :             : 
     409         [ +  + ]:       30910 :     if (!*chain)
     410                 :             :         return;
     411                 :             : 
     412                 :        9423 :     bf_list_clean(&(*chain)->rules);
     413                 :        9423 :     bf_hashset_clean(&(*chain)->sets);
     414                 :        9423 :     BF_FREEP(&(*chain)->name);
     415                 :        9423 :     BF_FREEP(chain);
     416                 :             : }
     417                 :             : 
     418                 :        1494 : int bf_chain_pack(const struct bf_chain *chain, bf_wpack_t *pack)
     419                 :             : {
     420                 :             :     int r;
     421                 :             : 
     422                 :             :     assert(chain);
     423                 :             :     assert(pack);
     424                 :             : 
     425                 :        1494 :     bf_wpack_kv_str(pack, "name", chain->name);
     426                 :        1494 :     bf_wpack_kv_enum(pack, "hook", chain->hook);
     427                 :        1494 :     bf_wpack_kv_enum(pack, "policy", chain->policy);
     428                 :             : 
     429                 :        1494 :     bf_wpack_open_array(pack, "sets");
     430   [ +  +  +  +  :        3194 :     bf_hashset_foreach (&chain->sets, set_node) {
                   +  + ]
     431                 :         103 :         bf_wpack_open_object(pack, NULL);
     432                 :         103 :         r = bf_set_pack(set_node->data, pack);
     433         [ +  - ]:         103 :         if (r)
     434                 :             :             return r;
     435                 :         103 :         bf_wpack_close_object(pack);
     436                 :             :     }
     437                 :        1494 :     bf_wpack_close_array(pack);
     438                 :        1494 :     bf_wpack_kv_list(pack, "rules", &chain->rules);
     439                 :             : 
     440         [ -  + ]:        1494 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     441                 :             : }
     442                 :             : 
     443                 :           2 : void bf_chain_dump(const struct bf_chain *chain, prefix_t *prefix)
     444                 :             : {
     445                 :             :     assert(chain);
     446                 :             :     assert(prefix);
     447                 :             : 
     448         [ -  + ]:           2 :     DUMP(prefix, "struct bf_chain at %p", chain);
     449                 :           2 :     bf_dump_prefix_push(prefix);
     450                 :             : 
     451         [ -  + ]:           2 :     DUMP(prefix, "name: %s", chain->name);
     452         [ -  + ]:           2 :     DUMP(prefix, "flags: %02x", chain->flags);
     453         [ -  + ]:           2 :     DUMP(prefix, "hook: %s", bf_hook_to_str(chain->hook));
     454         [ -  + ]:           2 :     DUMP(prefix, "policy: %s", bf_verdict_to_str(chain->policy));
     455                 :             : 
     456         [ -  + ]:           2 :     DUMP(prefix, "sets: bf_hashset<bf_set>[%lu]",
     457                 :             :          bf_hashset_size(&chain->sets));
     458                 :           2 :     bf_dump_prefix_push(prefix);
     459   [ +  +  +  +  :          10 :     bf_hashset_foreach (&chain->sets, set_node) {
                   +  + ]
     460         [ +  + ]:           3 :         if (!set_node->next)
     461                 :           1 :             bf_dump_prefix_last(prefix);
     462                 :             : 
     463                 :           3 :         bf_set_dump(set_node->data, prefix);
     464                 :             :     }
     465                 :           2 :     bf_dump_prefix_pop(prefix);
     466                 :             : 
     467         [ -  + ]:           2 :     DUMP(bf_dump_prefix_last(prefix), "rules: bf_list<bf_rule>[%lu]",
     468                 :             :          bf_list_size(&chain->rules));
     469                 :           2 :     bf_dump_prefix_push(prefix);
     470   [ +  +  +  +  :          16 :     bf_list_foreach (&chain->rules, rule_node) {
                   +  + ]
     471         [ +  + ]:           6 :         if (bf_list_is_tail(&chain->rules, rule_node))
     472                 :           1 :             bf_dump_prefix_last(prefix);
     473                 :             : 
     474                 :           6 :         bf_rule_dump(bf_list_node_get_data(rule_node), prefix);
     475                 :             :     }
     476                 :           2 :     bf_dump_prefix_pop(prefix);
     477                 :             : 
     478         [ -  + ]:           2 :     DUMP(prefix, "policy_counters: struct bf_counter");
     479                 :           2 :     bf_dump_prefix_push(prefix);
     480         [ -  + ]:           2 :     DUMP(prefix, "count: %lu", chain->policy_counters.count);
     481         [ -  + ]:           2 :     DUMP(prefix, "size: %lu", chain->policy_counters.size);
     482                 :           2 :     bf_dump_prefix_pop(prefix);
     483                 :             : 
     484         [ -  + ]:           2 :     DUMP(prefix, "error_counters: struct bf_counter");
     485                 :           2 :     bf_dump_prefix_push(prefix);
     486         [ -  + ]:           2 :     DUMP(prefix, "count: %lu", chain->error_counters.count);
     487         [ -  + ]:           2 :     DUMP(prefix, "size: %lu", chain->error_counters.size);
     488                 :           2 :     bf_dump_prefix_pop(prefix);
     489                 :             : 
     490                 :           2 :     bf_dump_prefix_pop(prefix);
     491                 :           2 : }
     492                 :             : 
     493                 :        3027 : int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule)
     494                 :             : {
     495                 :             :     int r;
     496                 :             : 
     497                 :             :     assert(chain);
     498                 :             :     assert(rule);
     499                 :             : 
     500                 :        3027 :     rule->index = bf_list_size(&chain->rules);
     501                 :        3027 :     r = _bf_chain_check_rule(chain, rule);
     502         [ +  + ]:        3027 :     if (r)
     503                 :             :         return r;
     504                 :             : 
     505                 :        3025 :     return bf_list_add_tail(&chain->rules, rule);
     506                 :             : }
     507                 :             : 
     508                 :          77 : int bf_chain_add_set(struct bf_chain *chain, struct bf_set *set)
     509                 :             : {
     510                 :             :     assert(chain && set);
     511                 :             : 
     512         [ +  + ]:          77 :     if (!bf_set_is_valid_name(set->name))
     513         [ +  - ]:           1 :         return bf_err_r(-EINVAL, "only named sets can be added to a chain");
     514                 :             : 
     515                 :          76 :     return bf_hashset_add(&chain->sets, (void **)&set);
     516                 :             : }
     517                 :             : 
     518                 :             : const struct bf_set *
     519                 :        8273 : bf_chain_get_set_for_matcher(const struct bf_chain *chain,
     520                 :             :                              const struct bf_matcher *matcher)
     521                 :             : {
     522                 :             :     const struct bf_set *set;
     523                 :             : 
     524                 :             :     assert(chain);
     525                 :             :     assert(matcher);
     526                 :             : 
     527         [ +  + ]:        8273 :     if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
     528                 :             :         return NULL;
     529                 :             : 
     530                 :        8272 :     memcpy(&set, bf_matcher_payload(matcher), sizeof(struct bf_set *));
     531         [ +  + ]:        8272 :     if (bf_set_is_anonymous(set))
     532                 :             :         return set;
     533                 :             : 
     534         [ +  + ]:        1115 :     return bf_hashset_get(&chain->sets, set) == set ? set : NULL;
     535                 :             : }
     536                 :             : 
     537                 :         128 : static struct bf_set *_bf_chain_get_set_by_name(struct bf_chain *chain,
     538                 :             :                                                 const char *set_name)
     539                 :             : {
     540                 :         128 :     struct bf_set key = {.name = set_name};
     541                 :             : 
     542                 :             :     assert(chain);
     543                 :             :     assert(set_name);
     544                 :             : 
     545                 :         128 :     return bf_hashset_get(&chain->sets, &key);
     546                 :             : }
     547                 :             : 
     548                 :         106 : const struct bf_set *bf_chain_get_set_by_name(const struct bf_chain *chain,
     549                 :             :                                               const char *set_name)
     550                 :             : {
     551                 :         106 :     return _bf_chain_get_set_by_name((struct bf_chain *)chain, set_name);
     552                 :             : }
     553                 :             : 
     554                 :          23 : int bf_chain_apply_set_delta(struct bf_chain *chain, const char *set_name,
     555                 :             :                              const struct bf_set *to_add,
     556                 :             :                              const struct bf_set *to_remove)
     557                 :             : {
     558                 :          23 :     _free_bf_set_ struct bf_set *add_copy = NULL;
     559                 :          23 :     _free_bf_set_ struct bf_set *remove_copy = NULL;
     560                 :             :     struct bf_set *set;
     561                 :             :     int r;
     562                 :             : 
     563                 :             :     assert(chain);
     564                 :             :     assert(set_name);
     565                 :             : 
     566         [ +  + ]:          23 :     if (!to_add && !to_remove)
     567         [ +  - ]:           1 :         return bf_err_r(-EINVAL, "to_add and to_remove can't both be NULL");
     568                 :             : 
     569                 :          22 :     set = _bf_chain_get_set_by_name(chain, set_name);
     570         [ +  + ]:          22 :     if (!set)
     571         [ +  - ]:           1 :         return bf_err_r(-ENOENT, "set '%s' does not exist", set_name);
     572                 :             : 
     573   [ +  +  +  +  :          21 :     if ((to_add && !bf_set_same_key(set, to_add)) ||
                   +  + ]
     574         [ -  + ]:          18 :         (to_remove && !bf_set_same_key(set, to_remove)))
     575         [ +  - ]:           1 :         return bf_err_r(-EINVAL, "set key format mismatch");
     576                 :             : 
     577         [ +  + ]:          20 :     if (to_add) {
     578                 :          19 :         r = bf_set_new_from_copy(&add_copy, to_add);
     579         [ +  - ]:          19 :         if (r)
     580                 :             :             return r;
     581                 :             : 
     582                 :          19 :         r = bf_set_add_many(set, &add_copy);
     583         [ -  + ]:          19 :         if (r)
     584         [ #  # ]:           0 :             return bf_err_r(r, "failed to calculate set union");
     585                 :             :     }
     586                 :             : 
     587         [ +  + ]:          20 :     if (to_remove) {
     588                 :          18 :         r = bf_set_new_from_copy(&remove_copy, to_remove);
     589         [ +  - ]:          18 :         if (r)
     590                 :             :             return r;
     591                 :             : 
     592                 :          18 :         r = bf_set_remove_many(set, &remove_copy);
     593         [ -  + ]:          18 :         if (r)
     594         [ #  # ]:           0 :             return bf_err_r(r, "failed to calculate set difference");
     595                 :             :     }
     596                 :             : 
     597                 :          20 :     r = _bf_chain_validate_rules(chain);
     598         [ -  + ]:          20 :     if (r)
     599         [ #  # ]:           0 :         return bf_err_r(r, "failed to validate chain after set update");
     600                 :             : 
     601                 :             :     return 0;
     602                 :             : }
     603                 :             : 
     604                 :        1473 : int bf_chain_new_from_copy(struct bf_chain **dest, const struct bf_chain *src)
     605                 :             : {
     606                 :        1473 :     _free_bf_chain_ struct bf_chain *copy = NULL;
     607                 :             :     int r;
     608                 :             : 
     609                 :             :     assert(dest);
     610                 :             :     assert(src);
     611                 :             : 
     612         [ -  + ]:        1473 :     if (src->hook < 0 || src->hook >= _BF_HOOK_MAX)
     613         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unknown source chain hook type");
     614                 :             : 
     615                 :        1473 :     r = bf_chain_new(&copy, src->name, src->hook, src->policy, NULL, NULL);
     616         [ +  - ]:        1473 :     if (r)
     617                 :             :         return r;
     618                 :             : 
     619                 :        1473 :     r = bf_hashset_reserve(&copy->sets, bf_hashset_size(&src->sets));
     620         [ +  - ]:        1473 :     if (r)
     621                 :             :         return r;
     622                 :             : 
     623   [ +  +  +  +  :        3118 :     bf_hashset_foreach (&src->sets, set_node) {
                   +  + ]
     624                 :          86 :         _free_bf_set_ struct bf_set *set = NULL;
     625                 :             : 
     626                 :          86 :         r = bf_set_new_from_copy(&set, set_node->data);
     627         [ +  - ]:          86 :         if (r)
     628                 :             :             return r;
     629                 :             : 
     630                 :          86 :         r = bf_hashset_add(&copy->sets, (void **)&set);
     631         [ +  - ]:          86 :         if (r)
     632                 :             :             return r;
     633                 :             :     }
     634                 :             : 
     635   [ +  +  +  +  :        6432 :     bf_list_foreach (&src->rules, rule_node) {
                   +  + ]
     636                 :        1745 :         _free_bf_rule_ struct bf_rule *rule = NULL;
     637                 :             : 
     638                 :        1744 :         r = bf_rule_new_from_copy(&rule, bf_list_node_get_data(rule_node));
     639         [ +  - ]:        1744 :         if (r)
     640                 :             :             return r;
     641                 :             : 
     642   [ +  -  +  +  :        9450 :         bf_list_foreach (&rule->matchers, matcher_node) {
                   +  + ]
     643                 :             :             struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
     644                 :             :             const struct bf_set *set;
     645                 :             : 
     646         [ +  + ]:        2982 :             if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
     647                 :        2574 :                 continue;
     648                 :             : 
     649                 :         408 :             set = bf_chain_get_set_for_matcher(src, matcher);
     650         [ +  + ]:         408 :             if (!set)
     651         [ +  - ]:           1 :                 return bf_err_r(-ENOENT,
     652                 :             :                                 "matcher set not found in source chain");
     653         [ +  + ]:         407 :             if (!set->name)
     654                 :         331 :                 continue;
     655                 :             : 
     656                 :          76 :             set = bf_chain_get_set_by_name(copy, set->name);
     657         [ -  + ]:          76 :             if (!set)
     658         [ #  # ]:           0 :                 return bf_err_r(-ENOENT,
     659                 :             :                                 "matcher set not found in copied chain");
     660                 :             : 
     661                 :          76 :             memcpy((void *)bf_matcher_payload(matcher), &set,
     662                 :             :                    sizeof(struct bf_set *));
     663                 :             :         }
     664                 :             : 
     665                 :        1743 :         r = bf_chain_add_rule(copy, rule);
     666         [ +  - ]:        1743 :         if (r)
     667                 :             :             return r;
     668                 :             : 
     669                 :        1743 :         TAKE_PTR(rule);
     670                 :             :     }
     671                 :             : 
     672                 :        1472 :     *dest = TAKE_PTR(copy);
     673                 :             : 
     674                 :        1472 :     return 0;
     675                 :             : }
        

Generated by: LCOV version 2.0-1