LCOV - code coverage report
Current view: top level - bpfilter/cgen - nf.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 0.0 % 69 0
Test Date: 2025-02-26 17:59:59 Functions: 0.0 % 5 0

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-2.0-only */
       2              : /*
       3              :  * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
       4              :  */
       5              : 
       6              : #include "bpfilter/cgen/nf.h"
       7              : 
       8              : #include <linux/bpf.h>
       9              : #include <linux/bpf_common.h>
      10              : #include <linux/if_ether.h>
      11              : #include <linux/netfilter.h>
      12              : 
      13              : #include <stdbool.h>
      14              : #include <stddef.h>
      15              : #include <stdint.h>
      16              : #include <sys/socket.h>
      17              : 
      18              : #include "bpfilter/cgen/jmp.h"
      19              : #include "bpfilter/cgen/prog/link.h"
      20              : #include "bpfilter/cgen/program.h"
      21              : #include "bpfilter/cgen/stub.h"
      22              : #include "bpfilter/cgen/swich.h"
      23              : #include "core/btf.h"
      24              : #include "core/flavor.h"
      25              : #include "core/helper.h"
      26              : #include "core/hook.h"
      27              : #include "core/list.h"
      28              : #include "core/logger.h"
      29              : #include "core/verdict.h"
      30              : 
      31              : #include "external/filter.h"
      32              : 
      33              : #define BF_NF_PRIO_EVEN 2
      34              : #define BF_NF_PRIO_ODD 1
      35              : 
      36              : static int _bf_nf_gen_inline_prologue(struct bf_program *program);
      37              : static int _bf_nf_gen_inline_epilogue(struct bf_program *program);
      38              : static int _bf_nf_get_verdict(enum bf_verdict verdict);
      39              : static int _bf_nf_attach_prog(
      40              :     struct bf_program *new_prog, struct bf_program *old_prog,
      41              :     int (*get_new_link_cb)(struct bf_program *prog, struct bf_link *old_link,
      42              :                            struct bf_link **new_link));
      43              : static int _bf_nf_detach_prog(struct bf_program *program);
      44              : 
      45              : const struct bf_flavor_ops bf_flavor_ops_nf = {
      46              :     .gen_inline_prologue = _bf_nf_gen_inline_prologue,
      47              :     .gen_inline_epilogue = _bf_nf_gen_inline_epilogue,
      48              :     .get_verdict = _bf_nf_get_verdict,
      49              :     .attach_prog = _bf_nf_attach_prog,
      50              :     .detach_prog = _bf_nf_detach_prog,
      51              : };
      52              : 
      53              : // Forward definition to avoid headers clusterfuck.
      54              : uint16_t htons(uint16_t hostshort);
      55              : 
      56              : static inline bool _bf_nf_hook_is_ingress(enum bf_hook hook)
      57              : {
      58            0 :     return hook == BF_HOOK_NF_PRE_ROUTING || hook == BF_HOOK_NF_LOCAL_IN ||
      59              :            hook == BF_HOOK_NF_FORWARD;
      60              : }
      61              : 
      62            0 : static int _bf_nf_gen_inline_prologue(struct bf_program *program)
      63              : {
      64              :     int r;
      65              :     int offset;
      66              : 
      67            0 :     bf_assert(program);
      68              : 
      69              :     // Copy the ifindex from to bpf_nf_ctx.state.{in,out}.ifindex the runtime context
      70            0 :     if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "state")) < 0)
      71              :         return offset;
      72            0 :     EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, offset));
      73            0 :     if (_bf_nf_hook_is_ingress(program->hook)) {
      74            0 :         if ((offset = bf_btf_get_field_off("nf_hook_state", "in")) < 0)
      75              :             return offset;
      76            0 :         EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, offset));
      77              :     } else {
      78            0 :         if ((offset = bf_btf_get_field_off("nf_hook_state", "out")) < 0)
      79              :             return offset;
      80            0 :         EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, offset));
      81              :     }
      82              : 
      83            0 :     if ((offset = bf_btf_get_field_off("net_device", "ifindex")) < 0)
      84              :         return offset;
      85            0 :     EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_3, offset));
      86            0 :     EMIT(program,
      87              :          BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, BF_PROG_CTX_OFF(ifindex)));
      88              : 
      89              :     /* BPF_PROG_TYPE_CGROUP_SKB doesn't provide access the the Ethernet header,
      90              :      * so we can't parse it and discover the L3 protocol ID.
      91              :      * Instead, we use the __sk_buff.family value and convert it to the
      92              :      * corresponding ethertype. */
      93            0 :     if ((offset = bf_btf_get_field_off("nf_hook_state", "pf")) < 0)
      94              :         return offset;
      95            0 :     EMIT(program, BPF_LDX_MEM(BPF_B, BPF_REG_3, BPF_REG_2, offset));
      96              : 
      97              :     {
      98            0 :         _cleanup_bf_swich_ struct bf_swich swich =
      99            0 :             bf_swich_get(program, BPF_REG_3);
     100              : 
     101            0 :         EMIT_SWICH_OPTION(&swich, AF_INET,
     102              :                           BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IP)));
     103            0 :         EMIT_SWICH_OPTION(&swich, AF_INET6,
     104              :                           BPF_MOV64_IMM(BPF_REG_7, htons(ETH_P_IPV6)));
     105            0 :         EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
     106              : 
     107            0 :         r = bf_swich_generate(&swich);
     108            0 :         if (r)
     109              :             return r;
     110              :     }
     111              : 
     112            0 :     EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset), 0));
     113              : 
     114              :     // Calculate the packet size (+ETH_HLEN) and store it into the runtime context
     115            0 :     if ((offset = bf_btf_get_field_off("bpf_nf_ctx", "skb")) < 0)
     116              :         return offset;
     117            0 :     EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offset));
     118            0 :     if ((offset = bf_btf_get_field_off("sk_buff", "len")) < 0)
     119              :         return offset;
     120            0 :     EMIT(program, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, offset));
     121            0 :     EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, ETH_HLEN));
     122            0 :     EMIT(program,
     123              :          BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, BF_PROG_CTX_OFF(pkt_size)));
     124              : 
     125            0 :     r = bf_stub_make_ctx_skb_dynptr(program, BPF_REG_1);
     126            0 :     if (r)
     127              :         return r;
     128              : 
     129            0 :     r = bf_stub_parse_l3_hdr(program);
     130            0 :     if (r)
     131              :         return r;
     132              : 
     133            0 :     r = bf_stub_parse_l4_hdr(program);
     134              :     if (r)
     135              :         return r;
     136              : 
     137              :     return 0;
     138              : }
     139              : 
     140            0 : static int _bf_nf_gen_inline_epilogue(struct bf_program *program)
     141              : {
     142              :     UNUSED(program);
     143              : 
     144            0 :     return 0;
     145              : }
     146              : 
     147              : /**
     148              :  * Convert a standard verdict into a return value.
     149              :  *
     150              :  * @param verdict Verdict to convert. Must be valid.
     151              :  * @return TC return code corresponding to the verdict, as an integer.
     152              :  */
     153            0 : static int _bf_nf_get_verdict(enum bf_verdict verdict)
     154              : {
     155            0 :     bf_assert(0 <= verdict && verdict < _BF_TERMINAL_VERDICT_MAX);
     156              : 
     157              :     static const int verdicts[] = {
     158              :         [BF_VERDICT_ACCEPT] = NF_ACCEPT,
     159              :         [BF_VERDICT_DROP] = NF_DROP,
     160              :     };
     161              : 
     162              :     static_assert(ARRAY_SIZE(verdicts) == _BF_TERMINAL_VERDICT_MAX);
     163              : 
     164            0 :     return verdicts[verdict];
     165              : }
     166              : 
     167            0 : static int _bf_nf_attach_prog(struct bf_program *new_prog,
     168              :                               struct bf_program *old_prog,
     169              :                               int (*get_new_link_cb)(struct bf_program *prog,
     170              :                                                      struct bf_link *old_link,
     171              :                                                      struct bf_link **new_link))
     172              : {
     173              :     int r;
     174              : 
     175            0 :     bf_assert(new_prog && get_new_link_cb);
     176              : 
     177            0 :     if (old_prog && !bf_list_is_empty(&old_prog->links)) {
     178              :         /* BPF Netfilter programs can't be attached to NFPROTO_INET to filter
     179              :          * on both IPv4 and IPv6 at the same time. As a workaround, we attach
     180              :          * them to **both** NFPROTO_IPV4 and NFPROTO_IPV6. */
     181            0 :         bf_list_foreach (&old_prog->links, old_link_node) {
     182              :             struct bf_link *link;
     183            0 :             struct bpf_link_info info = {};
     184            0 :             struct bf_link *old_link = bf_list_node_get_data(old_link_node);
     185              : 
     186            0 :             r = bf_link_get_info(old_link, &info);
     187            0 :             if (r)
     188            0 :                 return bf_err_r(r, "failed to get old Netfilter link info");
     189              : 
     190            0 :             r = get_new_link_cb(new_prog, NULL, &link);
     191            0 :             if (r)
     192              :                 return r;
     193              : 
     194              :             /* BPF Netfilter programs can't be updated, so we need to create a
     195              :              * new link every time we want to attach a new program at the same
     196              :              * location. However, we can't create a new link with the same
     197              :              * priority. Hence, we use 100 and 101 successively. */
     198            0 :             r = bf_link_attach_nf(
     199              :                 link, new_prog->runtime.prog_fd, info.netfilter.pf,
     200            0 :                 info.netfilter.priority == BF_NF_PRIO_EVEN ? BF_NF_PRIO_ODD :
     201              :                                                              BF_NF_PRIO_EVEN);
     202            0 :             if (r)
     203            0 :                 return bf_err_r(r, "failed to attach Netfilter program");
     204              :         }
     205              :     } else {
     206              :         struct bf_link *link;
     207              : 
     208            0 :         r = get_new_link_cb(new_prog, NULL, &link);
     209            0 :         if (r)
     210            0 :             return r;
     211              : 
     212            0 :         r = bf_link_attach_nf(link, new_prog->runtime.prog_fd, NFPROTO_IPV4,
     213              :                               BF_NF_PRIO_EVEN);
     214            0 :         if (r)
     215            0 :             return bf_err_r(r, "failed to attach Netfilter IPv4 program");
     216              : 
     217            0 :         r = get_new_link_cb(new_prog, NULL, &link);
     218            0 :         if (r)
     219              :             return r;
     220              : 
     221            0 :         r = bf_link_attach_nf(link, new_prog->runtime.prog_fd, NFPROTO_IPV6,
     222              :                               BF_NF_PRIO_EVEN);
     223            0 :         if (r)
     224            0 :             return bf_err_r(r, "failed to attach Netfilter IPv6 program");
     225              :     }
     226              : 
     227              :     return 0;
     228              : }
     229              : 
     230              : /**
     231              :  * Unload the Netfilter BPF bytecode image.
     232              :  *
     233              :  * @param program Codegen containing the image to unload. Can't be NULL.
     234              :  * @return 0 on success, negative error code on failure.
     235              :  */
     236            0 : static int _bf_nf_detach_prog(struct bf_program *program)
     237              : {
     238            0 :     bf_assert(program);
     239              : 
     240            0 :     return bf_link_detach(
     241            0 :         bf_list_node_get_data(bf_list_get_head(&program->links)));
     242              : }
        

Generated by: LCOV version 2.0-1