LCOV - code coverage report
Current view: top level - bpfilter/xlate/ipt - ipt.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 2.5 % 314 8
Test Date: 2026-02-10 17:55:44 Functions: 25.0 % 16 4
Branches: 0.0 % 241 0

             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 <linux/in.h>
       7                 :             : #include <linux/netfilter.h>
       8                 :             : #include <linux/netfilter/x_tables.h>
       9                 :             : #include <linux/netfilter_ipv4/ip_tables.h>
      10                 :             : 
      11                 :             : #include <errno.h>
      12                 :             : #include <stdbool.h>
      13                 :             : #include <stddef.h>
      14                 :             : #include <stdint.h>
      15                 :             : #include <stdio.h>
      16                 :             : #include <stdlib.h>
      17                 :             : #include <string.h>
      18                 :             : 
      19                 :             : #include <bpfilter/chain.h>
      20                 :             : #include <bpfilter/counter.h>
      21                 :             : #include <bpfilter/dump.h>
      22                 :             : #include <bpfilter/front.h>
      23                 :             : #include <bpfilter/helper.h>
      24                 :             : #include <bpfilter/hook.h>
      25                 :             : #include <bpfilter/list.h>
      26                 :             : #include <bpfilter/logger.h>
      27                 :             : #include <bpfilter/matcher.h>
      28                 :             : #include <bpfilter/request.h>
      29                 :             : #include <bpfilter/response.h>
      30                 :             : #include <bpfilter/rule.h>
      31                 :             : #include <bpfilter/verdict.h>
      32                 :             : 
      33                 :             : #include "bpfilter/runtime.h"
      34                 :             : #include "cgen/cgen.h"
      35                 :             : #include "cgen/program.h"
      36                 :             : #include "ctx.h"
      37                 :             : #include "opts.h"
      38                 :             : #include "xlate/front.h"
      39                 :             : #include "xlate/ipt/dump.h"
      40                 :             : #include "xlate/ipt/helpers.h"
      41                 :             : 
      42                 :             : /**
      43                 :             :  * @file ipt.c
      44                 :             :  *
      45                 :             :  * @c iptables front-end for @c bpfilter .
      46                 :             :  *
      47                 :             :  * This front-end provides support for @c iptables command to @c bpfilter .
      48                 :             :  *
      49                 :             :  * @c iptables requires the @c INPUT , @c FORWARD , and @c OUTPUT chains to
      50                 :             :  * be defined with the @c ACCEPT policy by default, which mean they have no
      51                 :             :  * effect except counting the packets. @c bpfilter doesn't define those chains
      52                 :             :  * by default, even with this front-end enabled. Instead, it emulates then if
      53                 :             :  * they are not defined when @c iptables request the ruleset.
      54                 :             :  * See @ref _bf_ipt_gen_get_ruleset .
      55                 :             :  *
      56                 :             :  * Before running the requests command, @c iptables will send two requests to
      57                 :             :  * @c bpfilter to populate a local cache:
      58                 :             :  * - @c IPT_SO_GET_INFO : fetch the ruleset size, enabled hooks, number of
      59                 :             :  *   rules, and offset of the rules.
      60                 :             :  * - @c IPT_SO_GET_ENTRIES : same information as @c IPT_SO_GET_INFO plus the
      61                 :             :  *   ruleset.
      62                 :             :  * @c iptables always sends the whole ruleset to @c bpfilter , even if only a
      63                 :             :  * single rule has changed.
      64                 :             :  *
      65                 :             :  * @c bpfilter will generate the ruleset in @c iptables format on demand, as
      66                 :             :  * long as the rules have been defined by @c iptables previously. @c iptables
      67                 :             :  * ruleset is defined as an @c ipt_replace structure with the following fields:
      68                 :             :  * - @c name : name of the table, only "filter" is supported.
      69                 :             :  * - @c valid_hooks : flags of the enabled hooks (hooks with a ruleset defined).
      70                 :             :  * - @c num_entries : number of @c ipt_entry in the structure (hanging off the
      71                 :             :  *   end in a flexible array member).
      72                 :             :  * - @c size : total size of the @c ipt_entry structures.
      73                 :             :  * - @c hook_entry : offset of each chain's first @c ipt_entry starting from
      74                 :             :  *   @c ipt_replace.entries .
      75                 :             :  * - @c underflow : offset of each chain's policy @c ipt_entry starting from
      76                 :             :  *   @c ipt_replace.entries .
      77                 :             :  * - @c num_counters : identical to @c ipt_replace.num_entries .
      78                 :             :  * - @c counters : unused.
      79                 :             :  * - @c entries : flexible array member of @c ipt_entry for the chains.
      80                 :             :  *
      81                 :             :  * The @ref bf_rule of each chain are translated into @c ipt_entry structures.
      82                 :             :  * This structure is documented in the Linux kernel sources. All the
      83                 :             :  * @c ipt_entry structures defined for @ref bf_rule will have the same size
      84                 :             :  * because none of them will contain any matcher ( @c iptables matchers are not
      85                 :             :  * supported by @c bpfilter ), however after each @c ipt_entry is located an
      86                 :             :  * @c ipt_entry_target to define the rule's verdict. @c ipt_entry_target have
      87                 :             :  * different sizes depending on the exact type of target (verdict, jump, ...):
      88                 :             :  * @c bpfilter only supports verdict ( @c ipt_standard_target ).
      89                 :             :  *
      90                 :             :  * Then, a last @c ipt_entry is added for the error target, which is expected
      91                 :             :  * by @c iptables .
      92                 :             :  */
      93                 :             : 
      94                 :             : #define BF_IPT_PRIO_0 1000
      95                 :             : #define BF_IPT_PRIO_1 1001
      96                 :             : 
      97                 :             : /**
      98                 :             :  * Get size of an ipt_replace structure.
      99                 :             :  *
     100                 :             :  * @param ipt_replace_ptr Pointer to a valid ipt_replace structure.
     101                 :             :  * @return Size of the structure, including variable length entries field.
     102                 :             :  */
     103                 :             : #define bf_ipt_replace_size(ipt_replace_ptr)                                   \
     104                 :             :     (sizeof(struct ipt_replace) + (ipt_replace_ptr)->size)
     105                 :             : 
     106                 :             : /**
     107                 :             :  * Convert an iptables target to a bpfilter verdict.
     108                 :             :  *
     109                 :             :  * Only the NF_ACCEPT and NF_DROP standard target are supported, other targets
     110                 :             :  * and user-defined chains jumps will be rejected.
     111                 :             :  *
     112                 :             :  * @param ipt_tgt @c iptables target to convert.
     113                 :             :  * @param verdict @c bpfilter verdict, corresponding to @p ipt_tgt .
     114                 :             :  * @return 0 on success, or na egative errno value on error.
     115                 :             :  */
     116                 :           0 : static int _bf_ipt_target_to_verdict(struct ipt_entry_target *ipt_tgt,
     117                 :             :                                      enum bf_verdict *verdict)
     118                 :             : {
     119                 :             :     assert(ipt_tgt);
     120                 :             :     assert(verdict);
     121                 :             : 
     122         [ #  # ]:           0 :     if (bf_streq("", ipt_tgt->u.user.name)) {
     123                 :             :         struct ipt_standard_target *std_tgt =
     124                 :             :             (struct xt_standard_target *)ipt_tgt;
     125                 :             : 
     126         [ #  # ]:           0 :         if (std_tgt->verdict >= 0) {
     127         [ #  # ]:           0 :             return bf_err_r(
     128                 :             :                 -ENOTSUP,
     129                 :             :                 "iptables user-defined chains are not supported, rejecting target");
     130                 :             :         }
     131                 :             : 
     132      [ #  #  # ]:           0 :         switch (-std_tgt->verdict - 1) {
     133                 :           0 :         case NF_ACCEPT:
     134                 :           0 :             *verdict = BF_VERDICT_ACCEPT;
     135                 :           0 :             break;
     136                 :           0 :         case NF_DROP:
     137                 :           0 :             *verdict = BF_VERDICT_DROP;
     138                 :           0 :             break;
     139                 :           0 :         default:
     140         [ #  # ]:           0 :             return bf_err_r(-ENOTSUP, "unsupported iptables verdict: %d",
     141                 :             :                             std_tgt->verdict);
     142                 :             :         }
     143                 :             :     } else {
     144         [ #  # ]:           0 :         return bf_err_r(-ENOTSUP, "unsupported iptables target '%s', rejecting",
     145                 :             :                         ipt_tgt->u.user.name);
     146                 :             :     }
     147                 :             : 
     148                 :             :     return 0;
     149                 :             : }
     150                 :             : 
     151                 :           0 : static int _bf_verdict_to_ipt_target(enum bf_verdict verdict,
     152                 :             :                                      struct ipt_entry_target *ipt_tgt)
     153                 :             : {
     154                 :             :     struct ipt_standard_target *std_tgt = (struct xt_standard_target *)ipt_tgt;
     155                 :             : 
     156                 :             :     assert(ipt_tgt);
     157                 :             : 
     158      [ #  #  # ]:           0 :     switch (verdict) {
     159                 :           0 :     case BF_VERDICT_ACCEPT:
     160                 :           0 :         std_tgt->verdict = -2;
     161                 :           0 :         break;
     162                 :           0 :     case BF_VERDICT_DROP:
     163                 :           0 :         std_tgt->verdict = -1;
     164                 :           0 :         break;
     165                 :           0 :     default:
     166         [ #  # ]:           0 :         return bf_err_r(-ENOTSUP, "unsupported verdict %d", verdict);
     167                 :             :     }
     168                 :             : 
     169                 :           0 :     ipt_tgt->u.target_size = sizeof(*std_tgt);
     170                 :             : 
     171                 :           0 :     return 0;
     172                 :             : }
     173                 :             : 
     174                 :             : /**
     175                 :             :  * Translate an @c iptables rule into a @c bpfilter rule.
     176                 :             :  *
     177                 :             :  * @param entry @c iptables rule. Can't be NULL.
     178                 :             :  * @param rule @c bpfilter rule. Can't be NULL. On success, points to a
     179                 :             :  *        valid rule.
     180                 :             :  * @return 0 on success, or a negative errno value on error.
     181                 :             :  */
     182                 :           0 : static int _bf_ipt_entry_to_rule(const struct ipt_entry *entry,
     183                 :             :                                  struct bf_rule **rule)
     184                 :             : {
     185                 :           0 :     _free_bf_rule_ struct bf_rule *_rule = NULL;
     186                 :             :     int r;
     187                 :             : 
     188                 :             :     assert(entry);
     189                 :             :     assert(rule);
     190                 :             : 
     191         [ #  # ]:           0 :     if (sizeof(*entry) < entry->target_offset)
     192         [ #  # ]:           0 :         return bf_err_r(-ENOTSUP, "iptables modules are not supported");
     193                 :             : 
     194                 :           0 :     r = bf_rule_new(&_rule);
     195         [ #  # ]:           0 :     if (r)
     196                 :             :         return r;
     197                 :             : 
     198   [ #  #  #  # ]:           0 :     if (entry->ip.iniface[0] != '\0' || entry->ip.outiface[0] != '\0') {
     199         [ #  # ]:           0 :         return bf_err_r(
     200                 :             :             -ENOTSUP,
     201                 :             :             "filtering on input/output interface with iptables is not supported");
     202                 :             :     }
     203                 :             : 
     204                 :             :     // iptables always has counters enabled
     205                 :           0 :     _rule->counters = true;
     206                 :             : 
     207                 :             :     // Match on source IPv4 address
     208   [ #  #  #  # ]:           0 :     if (entry->ip.src.s_addr || entry->ip.smsk.s_addr) {
     209         [ #  # ]:           0 :         if (entry->ip.smsk.s_addr == ~(uint32_t)0) {
     210                 :           0 :             r = bf_rule_add_matcher(
     211                 :             :                 _rule, BF_MATCHER_IP4_SADDR,
     212                 :           0 :                 entry->ip.invflags & IPT_INV_SRCIP ? BF_MATCHER_NE :
     213                 :             :                                                      BF_MATCHER_EQ,
     214                 :           0 :                 &entry->ip.src.s_addr, sizeof(entry->ip.src.s_addr));
     215                 :             :         } else {
     216                 :           0 :             struct bf_ip4_lpm_key key = {
     217                 :             :                 .data = entry->ip.src.s_addr,
     218                 :           0 :                 .prefixlen = __builtin_ctz(entry->ip.smsk.s_addr),
     219                 :             :             };
     220                 :           0 :             r = bf_rule_add_matcher(_rule, BF_MATCHER_IP4_SNET,
     221                 :           0 :                                     entry->ip.invflags & IPT_INV_SRCIP ?
     222                 :           0 :                                         BF_MATCHER_NE :
     223                 :             :                                         BF_MATCHER_EQ,
     224                 :             :                                     &key, sizeof(key));
     225                 :             :         }
     226                 :             : 
     227         [ #  # ]:           0 :         if (r)
     228                 :             :             return r;
     229                 :             :     }
     230                 :             : 
     231                 :             :     // Match on destination IPv4 address
     232   [ #  #  #  # ]:           0 :     if (entry->ip.dst.s_addr || entry->ip.dmsk.s_addr) {
     233         [ #  # ]:           0 :         if (entry->ip.dmsk.s_addr == ~(uint32_t)0) {
     234                 :           0 :             r = bf_rule_add_matcher(
     235                 :             :                 _rule, BF_MATCHER_IP4_DADDR,
     236                 :           0 :                 entry->ip.invflags & IPT_INV_DSTIP ? BF_MATCHER_NE :
     237                 :             :                                                      BF_MATCHER_EQ,
     238                 :           0 :                 &entry->ip.dst.s_addr, sizeof(entry->ip.dst.s_addr));
     239                 :             :         } else {
     240                 :           0 :             struct bf_ip4_lpm_key key = {
     241                 :             :                 .data = entry->ip.dst.s_addr,
     242                 :           0 :                 .prefixlen = __builtin_ctz(entry->ip.dmsk.s_addr),
     243                 :             :             };
     244                 :           0 :             r = bf_rule_add_matcher(_rule, BF_MATCHER_IP4_DNET,
     245                 :           0 :                                     entry->ip.invflags & IPT_INV_DSTIP ?
     246                 :           0 :                                         BF_MATCHER_NE :
     247                 :             :                                         BF_MATCHER_EQ,
     248                 :             :                                     &key, sizeof(key));
     249                 :             :         }
     250                 :             : 
     251         [ #  # ]:           0 :         if (r)
     252                 :             :             return r;
     253                 :             :     }
     254                 :             : 
     255                 :             :     /* Match on the protocol field of the IPv4 packet (and not the L4 protocol,
     256                 :             :      * as this implies L3 is IPv4). */
     257         [ #  # ]:           0 :     if (entry->ip.proto) {
     258                 :           0 :         uint8_t proto = entry->ip.proto;
     259                 :             : 
     260                 :             :         // Ensure we didn't cast away data, as we should not
     261         [ #  # ]:           0 :         if (proto != entry->ip.proto) {
     262         [ #  # ]:           0 :             return bf_err_r(
     263                 :             :                 -EINVAL,
     264                 :             :                 "protocol '%d' is an invalid protocol for IPv4's protocol field",
     265                 :             :                 entry->ip.proto);
     266                 :             :         }
     267                 :             : 
     268                 :           0 :         r = bf_rule_add_matcher(_rule, BF_MATCHER_IP4_PROTO, BF_MATCHER_EQ,
     269                 :             :                                 &proto, sizeof(proto));
     270         [ #  # ]:           0 :         if (r)
     271                 :             :             return r;
     272                 :             :     }
     273                 :             : 
     274                 :           0 :     r = _bf_ipt_target_to_verdict(ipt_get_target(entry), &_rule->verdict);
     275         [ #  # ]:           0 :     if (r)
     276                 :             :         return r;
     277                 :             : 
     278                 :           0 :     *rule = TAKE_PTR(_rule);
     279                 :             : 
     280                 :           0 :     return 0;
     281                 :             : }
     282                 :             : 
     283                 :             : /**
     284                 :             :  * Translates a @ref bf_rule object into an @c ipt_entry .
     285                 :             :  *
     286                 :             :  * @param rule @ref bf_rule to translate. Can't be NULL.
     287                 :             :  * @param entry @c ipt_entry created from the @ref bf_rule . Can't be NULL.
     288                 :             :  * @return 0 on success, or a negative errno value on error.
     289                 :             :  */
     290                 :           0 : static int _bf_rule_to_ipt_entry(const struct bf_rule *rule,
     291                 :             :                                  struct ipt_entry *entry)
     292                 :             : {
     293                 :             :     const struct bf_ip4_lpm_key *net;
     294                 :             : 
     295                 :             :     assert(entry);
     296                 :             :     assert(rule);
     297                 :             : 
     298   [ #  #  #  #  :           0 :     bf_list_foreach (&rule->matchers, matcher_node) {
                   #  # ]
     299                 :             :         struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
     300                 :             : 
     301   [ #  #  #  #  :           0 :         switch (bf_matcher_get_type(matcher)) {
                   #  # ]
     302                 :           0 :         case BF_MATCHER_IP4_SADDR:
     303         [ #  # ]:           0 :             if (bf_matcher_get_op(matcher) == BF_MATCHER_NE)
     304                 :           0 :                 entry->ip.invflags |= IPT_INV_SRCIP;
     305                 :           0 :             entry->ip.src.s_addr = *(uint32_t *)bf_matcher_payload(matcher);
     306                 :           0 :             entry->ip.smsk.s_addr = ~(uint32_t)0;
     307                 :           0 :             break;
     308                 :           0 :         case BF_MATCHER_IP4_SNET:
     309         [ #  # ]:           0 :             if (bf_matcher_get_op(matcher) == BF_MATCHER_NE)
     310                 :           0 :                 entry->ip.invflags |= IPT_INV_SRCIP;
     311                 :           0 :             net = bf_matcher_payload(matcher);
     312                 :           0 :             entry->ip.src.s_addr = net->data;
     313                 :           0 :             entry->ip.smsk.s_addr = (~(uint32_t)0) << (32 - net->prefixlen);
     314                 :           0 :             break;
     315                 :           0 :         case BF_MATCHER_IP4_DADDR:
     316         [ #  # ]:           0 :             if (bf_matcher_get_op(matcher) == BF_MATCHER_NE)
     317                 :           0 :                 entry->ip.invflags |= IPT_INV_DSTIP;
     318                 :           0 :             entry->ip.dst.s_addr = *(uint32_t *)bf_matcher_payload(matcher);
     319                 :           0 :             entry->ip.dmsk.s_addr = ~(uint32_t)0;
     320                 :           0 :             break;
     321                 :           0 :         case BF_MATCHER_IP4_DNET:
     322         [ #  # ]:           0 :             if (bf_matcher_get_op(matcher) == BF_MATCHER_NE)
     323                 :           0 :                 entry->ip.invflags |= IPT_INV_DSTIP;
     324                 :           0 :             net = bf_matcher_payload(matcher);
     325                 :           0 :             entry->ip.dst.s_addr = net->data;
     326                 :           0 :             entry->ip.dmsk.s_addr = (~(uint32_t)0) << (32 - net->prefixlen);
     327                 :           0 :             break;
     328                 :           0 :         case BF_MATCHER_IP4_PROTO:
     329                 :           0 :             entry->ip.proto = *(uint8_t *)bf_matcher_payload(matcher);
     330                 :           0 :             break;
     331                 :           0 :         default:
     332         [ #  # ]:           0 :             return bf_err_r(
     333                 :             :                 -ENOTSUP, "unsupported matcher %s for BF_FRONT_IPT",
     334                 :             :                 bf_matcher_type_to_str(bf_matcher_get_type(matcher)));
     335                 :             :         }
     336                 :             :     }
     337                 :             : 
     338                 :           0 :     return _bf_verdict_to_ipt_target(rule->verdict, ipt_get_target(entry));
     339                 :             : }
     340                 :             : 
     341                 :           0 : static int _bf_ipt_entries_to_chain(struct bf_chain **chain, int ipt_hook,
     342                 :             :                                     struct ipt_entry *first,
     343                 :             :                                     struct ipt_entry *last)
     344                 :             : {
     345                 :           0 :     _free_bf_chain_ struct bf_chain *_chain = NULL;
     346                 :             :     enum bf_verdict policy;
     347                 :             :     int r;
     348                 :             : 
     349                 :             :     assert(chain);
     350                 :             :     assert(first);
     351                 :             :     assert(last);
     352                 :             : 
     353                 :             :     // The last rule of the chain is the policy.
     354                 :           0 :     r = _bf_ipt_target_to_verdict(ipt_get_target(last), &policy);
     355         [ #  # ]:           0 :     if (r)
     356                 :             :         return r;
     357                 :             : 
     358                 :           0 :     r = bf_chain_new(&_chain, bf_nf_hook_to_str(ipt_hook),
     359                 :             :                      bf_hook_from_nf_hook(ipt_hook), policy, NULL, NULL);
     360         [ #  # ]:           0 :     if (r)
     361                 :             :         return r;
     362                 :             : 
     363         [ #  # ]:           0 :     while (first < last) {
     364                 :           0 :         _free_bf_rule_ struct bf_rule *rule = NULL;
     365                 :             : 
     366                 :           0 :         r = _bf_ipt_entry_to_rule(first, &rule);
     367         [ #  # ]:           0 :         if (r)
     368         [ #  # ]:           0 :             return bf_err_r(r, "failed to create rule from ipt_entry");
     369                 :             : 
     370                 :           0 :         r = bf_chain_add_rule(_chain, rule);
     371         [ #  # ]:           0 :         if (r)
     372                 :             :             return r;
     373                 :             : 
     374                 :           0 :         TAKE_PTR(rule);
     375                 :           0 :         first = ipt_get_next_rule(first);
     376                 :             :     }
     377                 :             : 
     378                 :           0 :     *chain = TAKE_PTR(_chain);
     379                 :             : 
     380                 :           0 :     return 0;
     381                 :             : }
     382                 :             : 
     383                 :             : struct bf_ipt_gen_ruleset_entry
     384                 :             : {
     385                 :             :     struct bf_cgen *cgen;
     386                 :             :     struct bf_chain *chain;
     387                 :             : };
     388                 :             : 
     389                 :             : /**
     390                 :             :  * Get the list of chains and codegens for @c BF_FRONT_IPT .
     391                 :             :  *
     392                 :             :  * @param ruleset Array of size @c NF_INET_NUMHOOKS to be filled with the
     393                 :             :  *        codegen and chain for every hook (if defined). Mandatory chains will
     394                 :             :  *        be allocated and their pointer added to this array if they are
     395                 :             :  *        not yet defined. Can't be NULL.
     396                 :             :  * @param nrules On success, contain the total number of rules associated with
     397                 :             :  *        the @c BF_FRONT_IPT front-end. This is the number of rules from
     398                 :             :  *        iptables' perspective: each chain has an extra rule for the policy.
     399                 :             :  *        Can't be NULL.
     400                 :             :  * @param dummy_chains On success, this list will contain pointers to the
     401                 :             :  *        mandatory chains created to comply with iptables' behaviour. The
     402                 :             :  *        caller will own this list and the pointers contained in it. Can't
     403                 :             :  *        be NULL.
     404                 :             :  * @return 0 on success, or a negative errno value on failure.
     405                 :             :  */
     406                 :           0 : static int _bf_ipt_gen_get_ruleset(struct bf_ipt_gen_ruleset_entry *ruleset,
     407                 :             :                                    size_t *nrules, bf_list *dummy_chains)
     408                 :             : {
     409                 :           0 :     _clean_bf_list_ bf_list cgens = bf_list_default(NULL, NULL);
     410                 :             :     size_t _nrules = 0;
     411                 :             :     int r;
     412                 :             : 
     413                 :             :     assert(ruleset);
     414                 :             : 
     415                 :           0 :     r = bf_ctx_get_cgens_for_front(&cgens, BF_FRONT_IPT);
     416         [ #  # ]:           0 :     if (r)
     417         [ #  # ]:           0 :         return bf_err_r(r, "failed to collect codegens for BF_FRONT_IPT");
     418                 :             : 
     419   [ #  #  #  # ]:           0 :     bf_list_foreach (&cgens, cgen_node) {
     420                 :             :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     421                 :             : 
     422                 :           0 :         ruleset[bf_hook_to_nf_hook(cgen->chain->hook)].cgen = cgen;
     423         [ #  # ]:           0 :         ruleset[bf_hook_to_nf_hook(cgen->chain->hook)].chain = cgen->chain;
     424                 :             : 
     425                 :             :         /* Add the number of rules of the chain to the total number of rules,
     426                 :             :          * don't forget about the chain's policy, which is a rule from
     427                 :             :          * iptables' point of view. */
     428         [ #  # ]:           0 :         _nrules += bf_list_size(&cgen->chain->rules) + 1;
     429                 :             :     }
     430                 :             : 
     431                 :             :     /* iptables requires at least the INPUT, FORWARD, and OUTPUT chains. If
     432                 :             :      * those chains are not defined, we created dummy ones just to fill the
     433                 :             :      * ipt_replace structure. */
     434                 :             :     for (enum bf_nf_inet_hooks hook = BF_NF_INET_LOCAL_IN;
     435         [ #  # ]:           0 :          hook <= BF_NF_INET_LOCAL_OUT; ++hook) {
     436                 :           0 :         _free_bf_chain_ struct bf_chain *chain = NULL;
     437                 :             : 
     438         [ #  # ]:           0 :         if (ruleset[hook].cgen)
     439                 :             :             continue;
     440                 :             : 
     441                 :           0 :         r = bf_chain_new(&chain, bf_nf_hook_to_str(hook),
     442                 :             :                          bf_hook_from_nf_hook(hook), BF_VERDICT_ACCEPT, NULL,
     443                 :             :                          NULL);
     444         [ #  # ]:           0 :         if (r)
     445         [ #  # ]:           0 :             return bf_err_r(r,
     446                 :             :                             "failed to create a dummy chain for BF_FRONT_IPT");
     447                 :             : 
     448                 :           0 :         r = bf_list_add_tail(dummy_chains, chain);
     449         [ #  # ]:           0 :         if (r)
     450         [ #  # ]:           0 :             return bf_err_r(r,
     451                 :             :                             "failed to add BF_FRONT_IPT dummy chain to list");
     452                 :             : 
     453                 :           0 :         ruleset[hook].chain = TAKE_PTR(chain);
     454                 :             : 
     455                 :             :         // The dummy chains only contain the chain policy
     456                 :           0 :         ++_nrules;
     457                 :             :     }
     458                 :             : 
     459                 :           0 :     *nrules = _nrules;
     460                 :             : 
     461                 :           0 :     return 0;
     462                 :             : }
     463                 :             : 
     464                 :             : /**
     465                 :             :  * Generate the @c ipt_replace structure for the current ruleset.
     466                 :             :  *
     467                 :             :  * @param replace @c ipt_replace structure to allocate and fill. Can't be NULL.
     468                 :             :  * @param with_counters If true, the rule counters in @p replace will be filled
     469                 :             :  *        with the correct values. Otherwise, the counters will default to 0.
     470                 :             :  * @return 0 on success, or a negative errno value on failure.
     471                 :             :  */
     472                 :           0 : static int _bf_ipt_gen_ipt_replace(struct ipt_replace **replace,
     473                 :             :                                    bool with_counters)
     474                 :             : {
     475                 :             :     _cleanup_free_ struct ipt_replace *_replace = NULL;
     476                 :           0 :     _clean_bf_list_ bf_list dummy_chains = bf_list_default(bf_chain_free, NULL);
     477                 :           0 :     struct bf_ipt_gen_ruleset_entry ruleset[NF_INET_NUMHOOKS] = {};
     478                 :             :     struct ipt_entry *entry;
     479                 :             :     size_t next_chain_off = 0;
     480                 :           0 :     size_t nrules = 0;
     481                 :             :     size_t rule_size =
     482                 :             :         sizeof(struct ipt_entry) + sizeof(struct xt_standard_target);
     483                 :             :     size_t err_size = sizeof(struct ipt_entry) + sizeof(struct xt_error_target);
     484                 :             :     struct xt_error_target *err_tgt;
     485                 :             :     int r;
     486                 :             : 
     487                 :             :     assert(replace);
     488                 :             : 
     489                 :           0 :     r = _bf_ipt_gen_get_ruleset(ruleset, &nrules, &dummy_chains);
     490         [ #  # ]:           0 :     if (r)
     491         [ #  # ]:           0 :         return bf_err_r(r, "failed to collect the BF_FRONT_IPT ruleset");
     492                 :             : 
     493                 :           0 :     _replace = calloc(1, sizeof(*_replace) + (nrules * rule_size) + err_size);
     494         [ #  # ]:           0 :     if (!_replace)
     495                 :             :         return -ENOMEM;
     496                 :             : 
     497                 :             :     // Total number of rules, chain policies, and error entry
     498                 :           0 :     _replace->num_entries = nrules + 1;
     499                 :           0 :     _replace->num_counters = nrules + 1;
     500                 :           0 :     _replace->size = (nrules * rule_size) + err_size;
     501                 :             : 
     502                 :           0 :     entry = (struct ipt_entry *)(_replace + 1);
     503                 :           0 :     strncpy(_replace->name, "filter", XT_TABLE_MAXNAMELEN);
     504                 :             : 
     505         [ #  # ]:           0 :     for (int hook = 0; hook < NF_INET_NUMHOOKS; ++hook) {
     506                 :           0 :         struct bf_chain *chain = ruleset[hook].chain;
     507                 :           0 :         struct bf_cgen *cgen = ruleset[hook].cgen;
     508                 :             : 
     509         [ #  # ]:           0 :         if (!chain)
     510                 :           0 :             continue;
     511                 :             : 
     512                 :             :         /* Rules (struct ipt_entry) always have the same size:
     513                 :             :          *   sizeof(ipt_entry) + sizeof(ipt_standard_target)
     514                 :             :          * Matchers and user-defined chains are not supported. */
     515                 :             : 
     516                 :           0 :         _replace->valid_hooks |= BF_FLAG(hook);
     517         [ #  # ]:           0 :         _replace->hook_entry[hook] = next_chain_off;
     518                 :           0 :         _replace->underflow[hook] =
     519                 :           0 :             next_chain_off + (bf_list_size(&chain->rules) * rule_size);
     520                 :             : 
     521   [ #  #  #  # ]:           0 :         bf_list_foreach (&chain->rules, rule_node) {
     522                 :             :             struct bf_rule *rule = bf_list_node_get_data(rule_node);
     523                 :             : 
     524                 :           0 :             entry->target_offset = sizeof(struct ipt_entry);
     525                 :           0 :             entry->next_offset = rule_size;
     526                 :             : 
     527                 :           0 :             r = _bf_rule_to_ipt_entry(rule, entry);
     528         [ #  # ]:           0 :             if (r) {
     529         [ #  # ]:           0 :                 return bf_err_r(r,
     530                 :             :                                 "failed to translate bf_rule into ipt_entry");
     531                 :             :             }
     532                 :             : 
     533         [ #  # ]:           0 :             if (with_counters && cgen) {
     534                 :             :                 struct bf_counter counters;
     535                 :             : 
     536                 :           0 :                 r = bf_cgen_get_counter(cgen, rule->index, &counters);
     537         [ #  # ]:           0 :                 if (r) {
     538         [ #  # ]:           0 :                     return bf_err_r(r,
     539                 :             :                                     "failed to get counters for iptables rule");
     540                 :             :                 }
     541                 :             : 
     542                 :           0 :                 entry->counters.bcnt = counters.bytes;
     543                 :           0 :                 entry->counters.pcnt = counters.packets;
     544                 :             :             }
     545                 :             : 
     546         [ #  # ]:           0 :             entry = (void *)entry + rule_size;
     547                 :             :         }
     548                 :             : 
     549                 :             :         // Fill the ipt_entry for the chain policy
     550         [ #  # ]:           0 :         if (with_counters && cgen) {
     551                 :             :             struct bf_counter counters;
     552                 :             : 
     553                 :           0 :             r = bf_cgen_get_counter(cgen, BF_COUNTER_POLICY, &counters);
     554         [ #  # ]:           0 :             if (r) {
     555         [ #  # ]:           0 :                 return bf_err_r(
     556                 :             :                     r, "failed to get policy counters for iptables chain");
     557                 :             :             }
     558                 :             : 
     559                 :           0 :             entry->counters.bcnt = counters.bytes;
     560                 :           0 :             entry->counters.pcnt = counters.packets;
     561                 :             :         }
     562                 :             : 
     563                 :           0 :         entry->target_offset = sizeof(struct ipt_entry);
     564                 :           0 :         entry->next_offset = rule_size;
     565                 :             : 
     566                 :           0 :         r = _bf_verdict_to_ipt_target(chain->policy, ipt_get_target(entry));
     567         [ #  # ]:           0 :         if (r) {
     568         [ #  # ]:           0 :             return bf_err_r(
     569                 :             :                 r, "failed to convert chain policy to iptables verdict");
     570                 :             :         }
     571                 :             : 
     572                 :           0 :         entry = (void *)entry + rule_size;
     573                 :           0 :         next_chain_off += (bf_list_size(&chain->rules) + 1) * rule_size;
     574                 :             :     }
     575                 :             : 
     576                 :             :     // There is one last entry after the chains for the error target.
     577                 :           0 :     entry->target_offset = sizeof(struct ipt_entry);
     578                 :           0 :     entry->next_offset = err_size;
     579                 :             : 
     580                 :             :     err_tgt = (struct xt_error_target *)(entry + 1);
     581                 :           0 :     strcpy(err_tgt->errorname, "ERROR");
     582                 :           0 :     err_tgt->target.u.target_size = sizeof(struct xt_error_target);
     583                 :             :     err_tgt->target.u.user.target_size = sizeof(struct xt_error_target);
     584                 :           0 :     strcpy(err_tgt->target.u.user.name, "ERROR");
     585                 :             : 
     586                 :           0 :     *replace = TAKE_PTR(_replace);
     587                 :             : 
     588                 :           0 :     bf_ipt_dump_replace(*replace, EMPTY_PREFIX);
     589                 :             : 
     590                 :           0 :     return 0;
     591                 :             : }
     592                 :             : 
     593                 :             : /**
     594                 :             :  * Translate iptables rules into bpfilter format.
     595                 :             :  *
     596                 :             :  * @param ipt iptables rules.
     597                 :             :  * @param chains Array of chains. The array is big enough to fit one chain per
     598                 :             :  *        hook. Can't be NULL.
     599                 :             :  * @return 0 on success, negative error code on failure.
     600                 :             :  */
     601                 :             : static int
     602                 :           0 : _bf_ipt_xlate_ruleset_set(const struct ipt_replace *ipt,
     603                 :             :                           struct bf_chain *(*chains)[NF_INET_NUMHOOKS])
     604                 :             : {
     605                 :             :     int r;
     606                 :             : 
     607                 :             :     assert(ipt);
     608                 :             :     assert(chains);
     609                 :             : 
     610         [ #  # ]:           0 :     for (int i = 0; i < NF_INET_NUMHOOKS; ++i) {
     611                 :           0 :         _free_bf_chain_ struct bf_chain *chain = NULL;
     612                 :             : 
     613         [ #  # ]:           0 :         if (!ipt_is_hook_enabled(ipt, i)) {
     614         [ #  # ]:           0 :             bf_dbg("iptables hook %d is not enabled, skipping", i);
     615                 :             :             continue;
     616                 :             :         }
     617                 :             : 
     618                 :           0 :         r = _bf_ipt_entries_to_chain(&chain, i, ipt_get_first_rule(ipt, i),
     619                 :           0 :                                      ipt_get_last_rule(ipt, i));
     620         [ #  # ]:           0 :         if (r) {
     621         [ #  # ]:           0 :             return bf_err_r(r, "failed to create chain for iptables hook %d",
     622                 :             :                             i);
     623                 :             :         }
     624                 :             : 
     625                 :           0 :         (*chains)[i] = TAKE_PTR(chain);
     626                 :             :     }
     627                 :             : 
     628                 :             :     return 0;
     629                 :             : }
     630                 :             : 
     631                 :             : /**
     632                 :             :  * Modify existing iptables rules.
     633                 :             :  *
     634                 :             :  * @param req The request sent to bpfilter. Can't be NULL.
     635                 :             :  * @return 0 on success, negative error code on failure.
     636                 :             :  */
     637                 :           0 : static int _bf_ipt_ruleset_set(const struct bf_request *req)
     638                 :             : {
     639                 :             :     const struct ipt_replace *replace;
     640                 :           0 :     struct bf_chain *chains[NF_INET_NUMHOOKS] = {};
     641                 :           0 :     _clean_bf_list_ bf_list _cur_cgens = bf_list_default(NULL, NULL);
     642                 :           0 :     struct bf_cgen *cur_cgens[NF_INET_NUMHOOKS] = {};
     643                 :             :     int r;
     644                 :             : 
     645                 :             :     assert(req);
     646                 :             : 
     647                 :           0 :     replace = bf_request_data(req);
     648         [ #  # ]:           0 :     if (bf_ipt_replace_size(replace) != bf_request_data_len(req))
     649                 :             :         return -EINVAL;
     650                 :             : 
     651                 :           0 :     bf_ipt_dump_replace(replace, EMPTY_PREFIX);
     652                 :             : 
     653                 :           0 :     r = _bf_ipt_xlate_ruleset_set(replace, &chains);
     654         [ #  # ]:           0 :     if (r)
     655         [ #  # ]:           0 :         return bf_err_r(r, "failed to translate iptables ruleset");
     656                 :             : 
     657                 :           0 :     r = bf_ctx_get_cgens_for_front(&_cur_cgens, BF_FRONT_IPT);
     658         [ #  # ]:           0 :     if (r)
     659         [ #  # ]:           0 :         return bf_err_r(r, "failed to get existing bf_cgen for BF_FRONT_IPT");
     660                 :             : 
     661   [ #  #  #  # ]:           0 :     bf_list_foreach (&_cur_cgens, cgen_node) {
     662                 :             :         struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
     663                 :           0 :         enum bf_nf_inet_hooks hook = bf_hook_to_nf_hook(cgen->chain->hook);
     664                 :             : 
     665         [ #  # ]:           0 :         if (cur_cgens[hook])
     666         [ #  # ]:           0 :             return bf_err_r(-EEXIST,
     667                 :             :                             "found 2 bf_cgen for the same BF_FRONT_IPT hook!");
     668         [ #  # ]:           0 :         cur_cgens[hook] = cgen;
     669                 :             :     }
     670                 :             : 
     671         [ #  # ]:           0 :     for (int i = 0; i < NF_INET_NUMHOOKS; i++) {
     672                 :           0 :         _free_bf_cgen_ struct bf_cgen *cgen = cur_cgens[i];
     673                 :           0 :         _free_bf_chain_ struct bf_chain *chain = TAKE_PTR(chains[i]);
     674                 :             : 
     675                 :           0 :         _free_bf_hookopts_ struct bf_hookopts *hookopts = NULL;
     676                 :             : 
     677         [ #  # ]:           0 :         if (!chain)
     678                 :           0 :             continue;
     679                 :             : 
     680         [ #  # ]:           0 :         if (!cgen) {
     681                 :           0 :             r = bf_cgen_new(&cgen, BF_FRONT_IPT, &chain);
     682         [ #  # ]:           0 :             if (r)
     683                 :             :                 return r;
     684                 :             : 
     685                 :           0 :             r = bf_cgen_load(cgen);
     686         [ #  # ]:           0 :             if (r) {
     687         [ #  # ]:           0 :                 bf_err(
     688                 :             :                     "failed to generate a program for iptables hook %d, skipping",
     689                 :             :                     i);
     690                 :           0 :                 continue;
     691                 :             :             }
     692                 :             : 
     693                 :           0 :             r = bf_hookopts_new(&hookopts);
     694         [ #  # ]:           0 :             if (r)
     695                 :             :                 return r;
     696                 :             : 
     697                 :           0 :             hookopts->family = PF_INET;
     698                 :           0 :             hookopts->priorities[0] = BF_IPT_PRIO_0;
     699                 :           0 :             hookopts->priorities[1] = BF_IPT_PRIO_1;
     700                 :           0 :             hookopts->used_opts |=
     701                 :             :                 BF_FLAGS(BF_HOOKOPTS_FAMILY, BF_HOOKOPTS_PRIORITIES);
     702                 :             : 
     703                 :           0 :             r = bf_cgen_attach(cgen, bf_request_ns(req), &hookopts);
     704         [ #  # ]:           0 :             if (r) {
     705         [ #  # ]:           0 :                 bf_err(
     706                 :             :                     "failed to load a program for iptables hook %d, skipping",
     707                 :             :                     i);
     708                 :           0 :                 continue;
     709                 :             :             }
     710                 :             : 
     711                 :           0 :             r = bf_ctx_set_cgen(cgen);
     712         [ #  # ]:           0 :             if (r) {
     713         [ #  # ]:           0 :                 bf_err_r(
     714                 :             :                     r, "failed to store codegen for iptables hook %d, skipping",
     715                 :             :                     i);
     716                 :           0 :                 continue;
     717                 :             :             }
     718                 :             : 
     719                 :           0 :             TAKE_PTR(cgen);
     720                 :             :         } else {
     721                 :           0 :             r = bf_cgen_update(cgen, &chain);
     722         [ #  # ]:           0 :             if (r) {
     723                 :           0 :                 TAKE_PTR(cgen);
     724         [ #  # ]:           0 :                 bf_err_r(
     725                 :             :                     r,
     726                 :             :                     "failed to update codegen for iptables hook %d, skipping",
     727                 :             :                     i);
     728                 :           0 :                 continue;
     729                 :             :             }
     730                 :           0 :             TAKE_PTR(cgen);
     731                 :             :         }
     732                 :             :     }
     733                 :             : 
     734                 :             :     return r;
     735                 :             : }
     736                 :             : 
     737                 :             : /**
     738                 :             :  * Set counters for a rule.
     739                 :             :  *
     740                 :             :  * @todo Actually update the counters.
     741                 :             :  *
     742                 :             :  * @param counters iptables structure containing the counters and their value.
     743                 :             :  * @param len Length of the counters structure.
     744                 :             :  * @return 0 on success, negative error code on failure.
     745                 :             :  */
     746                 :             : static int _bf_ipt_set_counters_handler(const struct xt_counters_info *counters,
     747                 :             :                                         size_t len)
     748                 :             : {
     749                 :             :     (void)counters;
     750                 :             :     (void)len;
     751                 :             : 
     752                 :             :     return 0;
     753                 :             : }
     754                 :             : 
     755                 :           0 : int _bf_ipt_get_info_handler(const struct bf_request *request,
     756                 :             :                              struct bf_response **response)
     757                 :             : {
     758                 :           0 :     _cleanup_free_ struct ipt_replace *replace = NULL;
     759                 :             :     _cleanup_free_ struct ipt_getinfo *info = NULL;
     760                 :             :     int r;
     761                 :             : 
     762                 :             :     assert(request);
     763                 :             :     assert(sizeof(*info) == bf_request_data_len(request));
     764                 :             : 
     765                 :           0 :     info = bf_memdup(bf_request_data(request), bf_request_data_len(request));
     766         [ #  # ]:           0 :     if (!info) {
     767                 :             :         return -ENOMEM;
     768                 :             :     }
     769                 :             : 
     770         [ #  # ]:           0 :     if (!bf_streq(info->name, "filter")) {
     771         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "can't process IPT_SO_GET_INFO for table %s",
     772                 :             :                         info->name);
     773                 :             :     }
     774                 :             : 
     775                 :           0 :     r = _bf_ipt_gen_ipt_replace(&replace, false);
     776         [ #  # ]:           0 :     if (r)
     777                 :             :         return r;
     778                 :             : 
     779                 :           0 :     info->valid_hooks = replace->valid_hooks;
     780                 :           0 :     memcpy(info->hook_entry, replace->hook_entry, sizeof(replace->hook_entry));
     781                 :           0 :     memcpy(info->underflow, replace->underflow, sizeof(replace->underflow));
     782                 :           0 :     info->num_entries = replace->num_entries;
     783                 :           0 :     info->size = replace->size;
     784                 :             : 
     785                 :           0 :     return bf_response_new_success(response, (const char *)info,
     786                 :             :                                    sizeof(struct ipt_getinfo));
     787                 :             : }
     788                 :             : 
     789                 :             : /**
     790                 :             :  * Get the entries of a table, including counters.
     791                 :             :  *
     792                 :             :  * @param request
     793                 :             :  * @param response
     794                 :             :  * @return 0 on success, negative errno value on failure.
     795                 :             :  */
     796                 :           0 : int _bf_ipt_get_entries_handler(const struct bf_request *request,
     797                 :             :                                 struct bf_response **response)
     798                 :             : {
     799                 :           0 :     _cleanup_free_ struct ipt_replace *replace = NULL;
     800                 :             :     _cleanup_free_ struct ipt_get_entries *entries = NULL;
     801                 :             :     int r;
     802                 :             : 
     803                 :             :     assert(request);
     804                 :             :     assert(response);
     805                 :             : 
     806                 :           0 :     entries = bf_memdup(bf_request_data(request), bf_request_data_len(request));
     807         [ #  # ]:           0 :     if (!entries)
     808                 :             :         return -ENOMEM;
     809                 :             : 
     810         [ #  # ]:           0 :     if (!bf_streq(entries->name, "filter")) {
     811         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "can't process IPT_SO_GET_INFO for table %s",
     812                 :             :                         entries->name);
     813                 :             :     }
     814                 :             : 
     815                 :           0 :     r = _bf_ipt_gen_ipt_replace(&replace, true);
     816         [ #  # ]:           0 :     if (r)
     817                 :             :         return r;
     818                 :             : 
     819         [ #  # ]:           0 :     if (entries->size != replace->size) {
     820         [ #  # ]:           0 :         return bf_err_r(
     821                 :             :             -EINVAL,
     822                 :             :             "not enough space to store entries: %u available, %u required",
     823                 :             :             entries->size, replace->size);
     824                 :             :     }
     825                 :             : 
     826                 :           0 :     memcpy(entries->entrytable, replace->entries, replace->size);
     827                 :             : 
     828                 :           0 :     return bf_response_new_success(response, (const char *)entries,
     829                 :           0 :                                    sizeof(*entries) + entries->size);
     830                 :             : }
     831                 :             : 
     832                 :          26 : static int _bf_ipt_setup(void)
     833                 :             : {
     834                 :          26 :     return 0;
     835                 :             : }
     836                 :             : 
     837                 :          25 : static int _bf_ipt_teardown(void)
     838                 :             : {
     839                 :          25 :     return 0;
     840                 :             : }
     841                 :             : 
     842                 :           0 : static int _bf_ipt_request_handler(const struct bf_request *request,
     843                 :             :                                    struct bf_response **response)
     844                 :             : {
     845                 :             :     int r;
     846                 :             : 
     847   [ #  #  #  # ]:           0 :     switch (bf_request_cmd(request)) {
     848                 :           0 :     case BF_REQ_RULESET_SET:
     849                 :           0 :         r = _bf_ipt_ruleset_set(request);
     850         [ #  # ]:           0 :         if (r < 0)
     851                 :             :             return r;
     852                 :             : 
     853                 :           0 :         return bf_response_new_success(response, bf_request_data(request),
     854                 :             :                                        bf_request_data_len(request));
     855                 :           0 :     case BF_REQ_COUNTERS_SET:
     856                 :           0 :         r = _bf_ipt_set_counters_handler(bf_request_data(request),
     857                 :             :                                          bf_request_data_len(request));
     858                 :             :         if (r < 0)
     859                 :             :             return r;
     860                 :             : 
     861                 :           0 :         return bf_response_new_success(response, bf_request_data(request),
     862                 :             :                                        bf_request_data_len(request));
     863                 :           0 :     case BF_REQ_CUSTOM:
     864      [ #  #  # ]:           0 :         switch (bf_request_ipt_cmd(request)) {
     865                 :           0 :         case IPT_SO_GET_INFO:
     866                 :           0 :             return _bf_ipt_get_info_handler(request, response);
     867                 :           0 :         case IPT_SO_GET_ENTRIES:
     868                 :           0 :             return _bf_ipt_get_entries_handler(request, response);
     869                 :           0 :         default:
     870         [ #  # ]:           0 :             return bf_warn_r(-ENOTSUP,
     871                 :             :                              "unsupported custom ipt request type: %d",
     872                 :             :                              bf_request_ipt_cmd(request));
     873                 :             :         };
     874                 :           0 :     default:
     875         [ #  # ]:           0 :         return bf_warn_r(-ENOTSUP, "unsupported ipt request type: %d",
     876                 :             :                          bf_request_ipt_cmd(request));
     877                 :             :     };
     878                 :             : 
     879                 :             :     return 0;
     880                 :             : }
     881                 :             : 
     882                 :         106 : static int _bf_ipt_pack(bf_wpack_t *pack)
     883                 :             : {
     884                 :             :     (void)pack;
     885                 :             : 
     886                 :         106 :     return 0;
     887                 :             : }
     888                 :             : 
     889                 :           3 : static int _bf_ipt_unpack(bf_rpack_node_t node)
     890                 :             : {
     891                 :             :     (void)node;
     892                 :             : 
     893                 :           3 :     return 0;
     894                 :             : }
     895                 :             : 
     896                 :             : const struct bf_front_ops ipt_front = {
     897                 :             :     .setup = _bf_ipt_setup,
     898                 :             :     .teardown = _bf_ipt_teardown,
     899                 :             :     .request_handler = _bf_ipt_request_handler,
     900                 :             :     .pack = _bf_ipt_pack,
     901                 :             :     .unpack = _bf_ipt_unpack,
     902                 :             : };
        

Generated by: LCOV version 2.0-1