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 : : }
|