LCOV - code coverage report
Current view: top level - libbpfilter/cgen/matcher - meta.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 91.1 % 45 41
Test Date: 2026-10-13 09:41:22 Functions: 100.0 % 4 4
Branches: 52.9 % 51 27

             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                 :             : 
      13                 :             : #include <endian.h>
      14                 :             : #include <errno.h>
      15                 :             : #include <stddef.h>
      16                 :             : #include <stdint.h>
      17                 :             : 
      18                 :             : #include <bpfilter/elfstub.h>
      19                 :             : #include <bpfilter/logger.h>
      20                 :             : #include <bpfilter/matcher.h>
      21                 :             : 
      22                 :             : #include "cgen/jmp.h"
      23                 :             : #include "cgen/matcher/cmp.h"
      24                 :             : #include "cgen/program.h"
      25                 :             : #include "cgen/runtime.h"
      26                 :             : #include "filter.h"
      27                 :             : 
      28                 :             : /** @todo Add support for input and output interface filtering based on the
      29                 :             :  * program's hook. */
      30                 :          30 : static int _bf_matcher_generate_meta_iface(struct bf_program *program,
      31                 :             :                                            const struct bf_matcher *matcher)
      32                 :             : {
      33         [ +  - ]:          30 :     EMIT(program,
      34                 :             :          BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(ifindex)));
      35         [ -  + ]:          30 :     EMIT_FIXUP_JMP_NEXT_RULE(
      36                 :             :         program, BPF_JMP_IMM(bf_cmp_get_jmp_ins(matcher), BPF_REG_1,
      37                 :             :                              *(uint32_t *)bf_matcher_payload(matcher), 0));
      38                 :             : 
      39                 :          30 :     return 0;
      40                 :             : }
      41                 :             : 
      42                 :             : static int
      43                 :         143 : _bf_matcher_generate_meta_probability(struct bf_program *program,
      44                 :             :                                       const struct bf_matcher *matcher)
      45                 :             : {
      46                 :         143 :     float proba = *(float *)bf_matcher_payload(matcher);
      47                 :         143 :     uint32_t threshold = (uint32_t)((double)UINT32_MAX * (proba / 100.0));
      48                 :             : 
      49         [ +  - ]:         143 :     EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32));
      50                 :             : 
      51                 :             :     /* Compare with a 32-bit jump: struct bpf_insn.imm is signed, and the
      52                 :             :      * 64-bit BPF_JMP class sign-extends it before the comparison. Any
      53                 :             :      * threshold above INT32_MAX (every probability above 50%) would then
      54                 :             :      * compare r0 against 0xffffffff80000000 or more and never jump, so the
      55                 :             :      * rule would match 100% of the packets (and 0% when negated). BPF_JMP32
      56                 :             :      * compares the low 32 bits unsigned, which is what bpf_get_prandom_u32()
      57                 :             :      * returns. Same approach as meta.flow_probability below. */
      58         [ +  + ]:         143 :     if (bf_matcher_get_negate(matcher)) {
      59         [ -  + ]:          30 :         EMIT_FIXUP_JMP_NEXT_RULE(
      60                 :             :             program, BPF_JMP32_IMM(BPF_JLE, BPF_REG_0, threshold, 0));
      61                 :             :     } else {
      62         [ -  + ]:         113 :         EMIT_FIXUP_JMP_NEXT_RULE(
      63                 :             :             program, BPF_JMP32_IMM(BPF_JGT, BPF_REG_0, threshold, 0));
      64                 :             :     }
      65                 :             : 
      66                 :             :     return 0;
      67                 :             : }
      68                 :             : 
      69                 :             : static int
      70                 :          25 : _bf_matcher_generate_meta_flow_probability(struct bf_program *program,
      71                 :             :                                            const struct bf_matcher *matcher)
      72                 :             : {
      73                 :          25 :     float proba = *(float *)bf_matcher_payload(matcher);
      74                 :          25 :     uint32_t threshold = (uint32_t)((double)UINT32_MAX * (proba / 100.0));
      75                 :             : 
      76                 :             :     // Ensure L3 is IPv4 or IPv6, skip to next rule otherwise
      77         [ +  - ]:          25 :     EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_7, htobe16(ETH_P_IP), 2));
      78         [ +  - ]:          25 :     EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_7, htobe16(ETH_P_IPV6), 1));
      79         [ -  + ]:          25 :     EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
      80                 :             : 
      81                 :             :     // Ensure L4 is TCP or UDP, skip to next rule otherwise
      82         [ +  - ]:          25 :     EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, IPPROTO_TCP, 2));
      83         [ +  - ]:          25 :     EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, IPPROTO_UDP, 1));
      84         [ -  + ]:          25 :     EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
      85                 :             : 
      86                 :             :     /* Calculate flow hash using the bf_flow_hash elfstub.
      87                 :             :      *
      88                 :             :      * The elfstub computes a 32-bit hash from the packet's 5-tuple
      89                 :             :      * (src ip, dst ip, src port, dst port, protocol) plus IPv6 flow label.
      90                 :             :      * This ensures all packets in a flow get the same hash value, making
      91                 :             :      * the probability decision consistent per-flow rather than per-packet.
      92                 :             :      *
      93                 :             :      * Arguments:
      94                 :             :      * - r1: pointer to bf_runtime context
      95                 :             :      * - r2: L3 protocol ID (from r7, set by prologue)
      96                 :             :      * - r3: L4 protocol ID (from r8, set by prologue)
      97                 :             :      *
      98                 :             :      * Return: hash value in r0 */
      99         [ +  - ]:          25 :     EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
     100         [ +  - ]:          25 :     EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
     101                 :             :                                 -(int)sizeof(struct bf_runtime))); // r1 = ctx
     102         [ +  - ]:          25 :     EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_7)); // r2 = l3_proto
     103         [ +  - ]:          25 :     EMIT(program, BPF_MOV64_REG(BPF_REG_3, BPF_REG_8)); // r3 = l4_proto
     104                 :             : 
     105                 :             :     // Call the elfstub - result in r0
     106         [ +  - ]:          25 :     EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_FLOW_HASH);
     107                 :             : 
     108                 :             :     /* Compare the computed hash with the threshold based on probability.
     109                 :             :      * The hash is uniformly distributed across 32 bits, so we compare against
     110                 :             :      * UINT32_MAX * (proba / 100.0) to select the desired percentage of flows. */
     111         [ +  + ]:          25 :     if (bf_matcher_get_negate(matcher)) {
     112         [ -  + ]:          10 :         EMIT_FIXUP_JMP_NEXT_RULE(
     113                 :             :             program, BPF_JMP32_IMM(BPF_JLE, BPF_REG_0, threshold, 0));
     114                 :             :     } else {
     115         [ -  + ]:          15 :         EMIT_FIXUP_JMP_NEXT_RULE(
     116                 :             :             program, BPF_JMP32_IMM(BPF_JGT, BPF_REG_0, threshold, 0));
     117                 :             :     }
     118                 :             : 
     119                 :             :     return 0;
     120                 :             : }
     121                 :             : 
     122                 :         348 : int bf_matcher_generate_meta(struct bf_program *program,
     123                 :             :                              const struct bf_matcher *matcher)
     124                 :             : {
     125   [ +  +  +  +  :         348 :     switch (bf_matcher_get_type(matcher)) {
                +  -  - ]
     126                 :          30 :     case BF_MATCHER_META_IFACE:
     127                 :          30 :         return _bf_matcher_generate_meta_iface(program, matcher);
     128                 :          87 :     case BF_MATCHER_META_L3_PROTO: {
     129                 :          87 :         uint16_t be_val = htobe16(*(uint16_t *)bf_matcher_payload(matcher));
     130                 :          87 :         return bf_cmp_value(program, matcher, &be_val, 2, BPF_REG_7);
     131                 :             :     }
     132                 :          63 :     case BF_MATCHER_META_L4_PROTO:
     133                 :          63 :         return bf_cmp_value(program, matcher, bf_matcher_payload(matcher), 1,
     134                 :             :                             BPF_REG_8);
     135                 :         143 :     case BF_MATCHER_META_PROBABILITY:
     136                 :         143 :         return _bf_matcher_generate_meta_probability(program, matcher);
     137                 :          25 :     case BF_MATCHER_META_FLOW_PROBABILITY:
     138                 :          25 :         return _bf_matcher_generate_meta_flow_probability(program, matcher);
     139                 :           0 :     case BF_MATCHER_META_PID:
     140                 :             :     case BF_MATCHER_META_SPORT:
     141                 :             :     case BF_MATCHER_META_DPORT:
     142                 :             :     case BF_MATCHER_META_MARK:
     143                 :             :     case BF_MATCHER_META_FLOW_HASH:
     144         [ #  # ]:           0 :         return bf_err_r(-ENOTSUP,
     145                 :             :                         "matcher '%s' requires flavor-specific dispatch",
     146                 :             :                         bf_matcher_type_to_str(bf_matcher_get_type(matcher)));
     147                 :           0 :     default:
     148         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unknown matcher type %d",
     149                 :             :                         bf_matcher_get_type(matcher));
     150                 :             :     }
     151                 :             : }
        

Generated by: LCOV version 2.0-1