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

Generated by: LCOV version 2.0-1