LCOV - code coverage report
Current view: top level - bpfilter/cgen/matcher - meta.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 62.7 % 59 37
Test Date: 2026-03-19 16:00:55 Functions: 80.0 % 5 4
Branches: 33.8 % 68 23

             Branch data     Line data    Source code
       1                 :             : /* SPDX-License-Identifier: GPL-2.0-only */
       2                 :             : /*
       3                 :             :  * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
       4                 :             :  */
       5                 :             : 
       6                 :             : #include "cgen/matcher/meta.h"
       7                 :             : 
       8                 :             : #include <linux/bpf.h>
       9                 :             : #include <linux/bpf_common.h>
      10                 :             : #include <linux/if_ether.h>
      11                 :             : #include <linux/in.h> // NOLINT
      12                 :             : #include <linux/tcp.h>
      13                 :             : #include <linux/udp.h>
      14                 :             : 
      15                 :             : #include <endian.h>
      16                 :             : #include <errno.h>
      17                 :             : #include <stddef.h>
      18                 :             : #include <stdint.h>
      19                 :             : 
      20                 :             : #include <bpfilter/logger.h>
      21                 :             : #include <bpfilter/matcher.h>
      22                 :             : 
      23                 :             : #include "cgen/elfstub.h"
      24                 :             : #include "cgen/matcher/cmp.h"
      25                 :             : #include "cgen/program.h"
      26                 :             : #include "cgen/runtime.h"
      27                 :             : #include "cgen/swich.h"
      28                 :             : #include "filter.h"
      29                 :             : 
      30                 :             : /** @todo Add support for input and output interface filtering based on the
      31                 :             :  * program's hook. */
      32                 :          20 : static int _bf_matcher_generate_meta_iface(struct bf_program *program,
      33                 :             :                                            const struct bf_matcher *matcher)
      34                 :             : {
      35         [ -  + ]:          20 :     EMIT(program,
      36                 :             :          BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(ifindex)));
      37         [ -  + ]:          20 :     EMIT_FIXUP_JMP_NEXT_RULE(
      38                 :             :         program, BPF_JMP_IMM(BPF_JNE, BPF_REG_1,
      39                 :             :                              *(uint32_t *)bf_matcher_payload(matcher), 0));
      40                 :             : 
      41                 :          20 :     return 0;
      42                 :             : }
      43                 :             : 
      44                 :             : static int
      45                 :          38 : _bf_matcher_generate_meta_probability(struct bf_program *program,
      46                 :             :                                       const struct bf_matcher *matcher)
      47                 :             : {
      48                 :          38 :     float proba = *(float *)bf_matcher_payload(matcher);
      49                 :             : 
      50         [ -  + ]:          38 :     EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32));
      51         [ -  + ]:          38 :     EMIT_FIXUP_JMP_NEXT_RULE(
      52                 :             :         program,
      53                 :             :         BPF_JMP_IMM(BPF_JGT, BPF_REG_0,
      54                 :             :                     (uint32_t)((double)UINT32_MAX * (proba / 100.0)), 0));
      55                 :             : 
      56                 :          38 :     return 0;
      57                 :             : }
      58                 :             : 
      59                 :         188 : static int _bf_matcher_generate_meta_port(struct bf_program *program,
      60                 :             :                                           const struct bf_matcher *matcher)
      61                 :             : {
      62                 :         188 :     _clean_bf_swich_ struct bf_swich swich;
      63                 :         188 :     uint16_t *port = (uint16_t *)bf_matcher_payload(matcher);
      64                 :             :     int r;
      65                 :             : 
      66                 :             :     // Load L4 header address into r6
      67         [ -  + ]:         188 :     EMIT(program,
      68                 :             :          BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l4_hdr)));
      69                 :             : 
      70                 :             :     // Get the packet's port into r1
      71         [ -  + ]:         188 :     swich = bf_swich_get(program, BPF_REG_8);
      72   [ +  +  -  + ]:         296 :     EMIT_SWICH_OPTION(
      73                 :             :         &swich, IPPROTO_TCP,
      74                 :             :         BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
      75                 :             :                     bf_matcher_get_type(matcher) == BF_MATCHER_META_SPORT ?
      76                 :             :                         offsetof(struct tcphdr, source) :
      77                 :             :                         offsetof(struct tcphdr, dest)));
      78   [ +  +  -  + ]:         296 :     EMIT_SWICH_OPTION(
      79                 :             :         &swich, IPPROTO_UDP,
      80                 :             :         BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
      81                 :             :                     bf_matcher_get_type(matcher) == BF_MATCHER_META_SPORT ?
      82                 :             :                         offsetof(struct udphdr, source) :
      83                 :             :                         offsetof(struct udphdr, dest)));
      84         [ -  + ]:         188 :     EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_1, 0));
      85                 :             : 
      86                 :         188 :     r = bf_swich_generate(&swich);
      87         [ -  + ]:         188 :     if (r)
      88         [ #  # ]:           0 :         return bf_err_r(r, "failed to generate swich for meta.(s|d)port");
      89                 :             : 
      90                 :             :     // If r1 == 0: no TCP nor UDP header found, jump to the next rule
      91         [ -  + ]:         188 :     EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0));
      92                 :             : 
      93         [ +  + ]:         188 :     if (bf_matcher_get_op(matcher) == BF_MATCHER_RANGE) {
      94         [ -  + ]:          40 :         EMIT(program, BPF_BSWAP(BPF_REG_1, 16));
      95                 :          40 :         return bf_cmp_range(program, port[0], port[1], BPF_REG_1);
      96                 :             :     }
      97                 :             : 
      98                 :         148 :     return bf_cmp_value(program, bf_matcher_get_op(matcher), port, 2,
      99                 :             :                         BPF_REG_1);
     100                 :             : }
     101                 :             : 
     102                 :             : static int
     103                 :           0 : _bf_matcher_generate_meta_flow_probability(struct bf_program *program,
     104                 :             :                                            const struct bf_matcher *matcher)
     105                 :             : {
     106                 :           0 :     float proba = *(float *)bf_matcher_payload(matcher);
     107                 :             : 
     108                 :             :     // Ensure L3 is IPv4 or IPv6, skip to next rule otherwise
     109         [ #  # ]:           0 :     EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_7, htobe16(ETH_P_IP), 2));
     110         [ #  # ]:           0 :     EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_7, htobe16(ETH_P_IPV6), 1));
     111         [ #  # ]:           0 :     EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
     112                 :             : 
     113                 :             :     // Ensure L4 is TCP or UDP, skip to next rule otherwise
     114         [ #  # ]:           0 :     EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, IPPROTO_TCP, 2));
     115         [ #  # ]:           0 :     EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, IPPROTO_UDP, 1));
     116         [ #  # ]:           0 :     EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
     117                 :             : 
     118                 :             :     /* Calculate flow hash using the bf_flow_hash elfstub.
     119                 :             :      *
     120                 :             :      * The elfstub computes a 32-bit hash from the packet's 5-tuple
     121                 :             :      * (src ip, dst ip, src port, dst port, protocol) plus IPv6 flow label.
     122                 :             :      * This ensures all packets in a flow get the same hash value, making
     123                 :             :      * the probability decision consistent per-flow rather than per-packet.
     124                 :             :      *
     125                 :             :      * Arguments:
     126                 :             :      * - r1: pointer to bf_runtime context
     127                 :             :      * - r2: L3 protocol ID (from r7, set by prologue)
     128                 :             :      * - r3: L4 protocol ID (from r8, set by prologue)
     129                 :             :      *
     130                 :             :      * Return: hash value in r0 */
     131         [ #  # ]:           0 :     EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
     132         [ #  # ]:           0 :     EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
     133                 :             :                                 -(int)sizeof(struct bf_runtime))); // r1 = ctx
     134         [ #  # ]:           0 :     EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_7)); // r2 = l3_proto
     135         [ #  # ]:           0 :     EMIT(program, BPF_MOV64_REG(BPF_REG_3, BPF_REG_8)); // r3 = l4_proto
     136                 :             : 
     137                 :             :     // Call the elfstub - result in r0
     138         [ #  # ]:           0 :     EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_FLOW_HASH);
     139                 :             : 
     140                 :             :     /* Compare the computed hash with the threshold based on probability.
     141                 :             :      * The hash is uniformly distributed across 32 bits, so we compare against
     142                 :             :      * UINT32_MAX * (proba / 100.0) to select the desired percentage of flows. */
     143         [ #  # ]:           0 :     EMIT_FIXUP_JMP_NEXT_RULE(
     144                 :             :         program,
     145                 :             :         BPF_JMP32_IMM(BPF_JGT, BPF_REG_0,
     146                 :             :                       (uint32_t)((double)UINT32_MAX * (proba / 100.0)), 0));
     147                 :             : 
     148                 :           0 :     return 0;
     149                 :             : }
     150                 :             : 
     151                 :         350 : int bf_matcher_generate_meta(struct bf_program *program,
     152                 :             :                              const struct bf_matcher *matcher)
     153                 :             : {
     154   [ +  +  +  +  :         350 :     switch (bf_matcher_get_type(matcher)) {
             +  -  -  - ]
     155                 :          20 :     case BF_MATCHER_META_IFACE:
     156                 :          20 :         return _bf_matcher_generate_meta_iface(program, matcher);
     157                 :          64 :     case BF_MATCHER_META_L3_PROTO: {
     158                 :          64 :         uint16_t be_val = htobe16(*(uint16_t *)bf_matcher_payload(matcher));
     159                 :          64 :         return bf_cmp_value(program, BF_MATCHER_EQ, &be_val, 2, BPF_REG_7);
     160                 :             :     }
     161                 :          40 :     case BF_MATCHER_META_L4_PROTO:
     162                 :          40 :         return bf_cmp_value(program, bf_matcher_get_op(matcher),
     163                 :             :                             bf_matcher_payload(matcher), 1, BPF_REG_8);
     164                 :          38 :     case BF_MATCHER_META_PROBABILITY:
     165                 :          38 :         return _bf_matcher_generate_meta_probability(program, matcher);
     166                 :         188 :     case BF_MATCHER_META_SPORT:
     167                 :             :     case BF_MATCHER_META_DPORT:
     168                 :         188 :         return _bf_matcher_generate_meta_port(program, matcher);
     169                 :           0 :     case BF_MATCHER_META_FLOW_PROBABILITY:
     170                 :           0 :         return _bf_matcher_generate_meta_flow_probability(program, matcher);
     171                 :           0 :     case BF_MATCHER_META_MARK:
     172                 :             :     case BF_MATCHER_META_FLOW_HASH:
     173         [ #  # ]:           0 :         return bf_err_r(-ENOTSUP,
     174                 :             :                         "matcher '%s' requires flavor-specific dispatch",
     175                 :             :                         bf_matcher_type_to_str(bf_matcher_get_type(matcher)));
     176                 :           0 :     default:
     177         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unknown matcher type %d",
     178                 :             :                         bf_matcher_get_type(matcher));
     179                 :             :     }
     180                 :             : }
        

Generated by: LCOV version 2.0-1