LCOV - code coverage report
Current view: top level - bpfilter/xlate/ipt - ipt.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 0.0 % 294 0
Test Date: 2025-03-25 15:17:39 Functions: 0.0 % 16 0

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

Generated by: LCOV version 2.0-1