LCOV - code coverage report
Current view: top level - libbpfilter/cgen/matcher - meta.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 92.1 % 63 58
Test Date: 2026-05-11 12:01:08 Functions: 100.0 % 5 5
Branches: 55.3 % 76 42

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

Generated by: LCOV version 2.0-1