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/in.h> // NOLINT
11 : : #include <linux/tcp.h>
12 : : #include <linux/udp.h>
13 : :
14 : : #include <endian.h>
15 : : #include <errno.h>
16 : : #include <stddef.h>
17 : : #include <stdint.h>
18 : :
19 : : #include <bpfilter/logger.h>
20 : : #include <bpfilter/matcher.h>
21 : :
22 : : #include "cgen/program.h"
23 : : #include "cgen/swich.h"
24 : : #include "filter.h"
25 : :
26 : : /** @todo Add support for input and output interface filtering based on the
27 : : * program's hook. */
28 : 20 : static int _bf_matcher_generate_meta_iface(struct bf_program *program,
29 : : const struct bf_matcher *matcher)
30 : : {
31 [ - + ]: 20 : EMIT(program,
32 : : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(ifindex)));
33 [ - + ]: 20 : EMIT_FIXUP_JMP_NEXT_RULE(
34 : : program, BPF_JMP_IMM(BPF_JNE, BPF_REG_1,
35 : : *(uint32_t *)bf_matcher_payload(matcher), 0));
36 : :
37 : 20 : return 0;
38 : : }
39 : :
40 : 60 : static int _bf_matcher_generate_meta_l3_proto(struct bf_program *program,
41 : : const struct bf_matcher *matcher)
42 : : {
43 [ - + ]: 60 : EMIT_FIXUP_JMP_NEXT_RULE(
44 : : program,
45 : : BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
46 : : htobe16(*(uint16_t *)bf_matcher_payload(matcher)), 0));
47 : :
48 : 60 : return 0;
49 : : }
50 : :
51 : 31 : static int _bf_matcher_generate_meta_l4_proto(struct bf_program *program,
52 : : const struct bf_matcher *matcher)
53 : : {
54 [ - + - + ]: 31 : EMIT_FIXUP_JMP_NEXT_RULE(
55 : : program,
56 : : BPF_JMP_IMM(bf_matcher_get_op(matcher) == BF_MATCHER_EQ ? BPF_JNE :
57 : : BPF_JEQ,
58 : : BPF_REG_8, *(uint8_t *)bf_matcher_payload(matcher), 0));
59 : :
60 : 31 : return 0;
61 : : }
62 : :
63 : : static int
64 : 30 : _bf_matcher_generate_meta_probability(struct bf_program *program,
65 : : const struct bf_matcher *matcher)
66 : : {
67 : 30 : uint8_t proba = *(uint8_t *)bf_matcher_payload(matcher);
68 : :
69 [ - + ]: 30 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32));
70 [ - + ]: 30 : EMIT_FIXUP_JMP_NEXT_RULE(
71 : : program,
72 : : BPF_JMP_IMM(BPF_JGT, BPF_REG_0,
73 : : (uint32_t)((uint64_t)UINT32_MAX * (proba / 100.0)), 0));
74 : :
75 : 30 : return 0;
76 : : }
77 : :
78 : 188 : static int _bf_matcher_generate_meta_port(struct bf_program *program,
79 : : const struct bf_matcher *matcher)
80 : : {
81 : 188 : _clean_bf_swich_ struct bf_swich swich;
82 : 188 : uint16_t *port = (uint16_t *)bf_matcher_payload(matcher);
83 : : int r;
84 : :
85 : : // Load L4 header address into r6
86 [ - + ]: 188 : EMIT(program,
87 : : BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l4_hdr)));
88 : :
89 : : // Get the packet's port into r1
90 [ - + ]: 188 : swich = bf_swich_get(program, BPF_REG_8);
91 [ + + - + ]: 296 : EMIT_SWICH_OPTION(
92 : : &swich, IPPROTO_TCP,
93 : : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
94 : : bf_matcher_get_type(matcher) == BF_MATCHER_META_SPORT ?
95 : : offsetof(struct tcphdr, source) :
96 : : offsetof(struct tcphdr, dest)));
97 [ + + - + ]: 296 : EMIT_SWICH_OPTION(
98 : : &swich, IPPROTO_UDP,
99 : : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
100 : : bf_matcher_get_type(matcher) == BF_MATCHER_META_SPORT ?
101 : : offsetof(struct udphdr, source) :
102 : : offsetof(struct udphdr, dest)));
103 [ - + ]: 188 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_1, 0));
104 : :
105 : 188 : r = bf_swich_generate(&swich);
106 [ - + ]: 188 : if (r)
107 [ # # ]: 0 : return bf_err_r(r, "failed to generate swich for meta.(s|d)port");
108 : :
109 : : // If r1 == 0: no TCP nor UDP header found, jump to the next rule
110 [ - + ]: 188 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0));
111 : :
112 [ + + + - ]: 188 : switch (bf_matcher_get_op(matcher)) {
113 : 88 : case BF_MATCHER_EQ:
114 [ - + ]: 88 : EMIT_FIXUP_JMP_NEXT_RULE(program,
115 : : BPF_JMP_IMM(BPF_JNE, BPF_REG_1, *port, 0));
116 : 88 : break;
117 : 60 : case BF_MATCHER_NE:
118 [ - + ]: 60 : EMIT_FIXUP_JMP_NEXT_RULE(program,
119 : : BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, *port, 0));
120 : 60 : break;
121 : 40 : case BF_MATCHER_RANGE:
122 : : /* Convert the big-endian value stored in the packet into a
123 : : * little-endian value for x86 and arm before comparing it to the
124 : : * reference value. This is a JLT/JGT comparison, we need to have the
125 : : * MSB where the machine expects then. */
126 [ - + ]: 40 : EMIT(program, BPF_BSWAP(BPF_REG_1, 16));
127 [ - + ]: 40 : EMIT_FIXUP_JMP_NEXT_RULE(program,
128 : : BPF_JMP_IMM(BPF_JLT, BPF_REG_1, port[0], 0));
129 [ - + ]: 40 : EMIT_FIXUP_JMP_NEXT_RULE(program,
130 : : BPF_JMP_IMM(BPF_JGT, BPF_REG_1, port[1], 0));
131 : 40 : break;
132 : 0 : default:
133 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher operator '%s' (%d)",
134 : : bf_matcher_op_to_str(bf_matcher_get_op(matcher)),
135 : : bf_matcher_get_op(matcher));
136 : : }
137 : :
138 : : return 0;
139 : : }
140 : :
141 : 18 : static int _bf_matcher_generate_meta_mark(struct bf_program *program,
142 : : const struct bf_matcher *matcher)
143 : : {
144 : 18 : uint32_t mark = *(uint32_t *)bf_matcher_payload(matcher);
145 : : int r;
146 : :
147 : 18 : r = program->runtime.ops->gen_inline_get_mark(program, BPF_REG_1);
148 [ - + ]: 18 : if (r)
149 [ # # ]: 0 : return bf_err_r(r, "failed to get inline mark");
150 : :
151 [ + - - ]: 18 : switch (bf_matcher_get_op(matcher)) {
152 : 18 : case BF_MATCHER_EQ:
153 [ - + ]: 18 : EMIT_FIXUP_JMP_NEXT_RULE(program,
154 : : BPF_JMP_IMM(BPF_JNE, BPF_REG_1, mark, 0));
155 : 18 : break;
156 : 0 : case BF_MATCHER_NE:
157 [ # # ]: 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
158 : : BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, mark, 0));
159 : 0 : break;
160 : 0 : default:
161 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher operator '%s' (%d)",
162 : : bf_matcher_op_to_str(bf_matcher_get_op(matcher)),
163 : : bf_matcher_get_op(matcher));
164 : : }
165 : :
166 : : return 0;
167 : : }
168 : :
169 : 28 : static int _bf_matcher_generate_meta_flow_hash(struct bf_program *program,
170 : : const struct bf_matcher *matcher)
171 : : {
172 : 28 : uint32_t *hash = (uint32_t *)bf_matcher_payload(matcher);
173 : : int r;
174 : :
175 : 28 : r = program->runtime.ops->gen_inline_get_skb(program, BPF_REG_1);
176 [ - + ]: 28 : if (r)
177 [ # # ]: 0 : return bf_err_r(r, "failed to get inline skb");
178 : :
179 [ - + ]: 28 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_hash_recalc));
180 : :
181 [ + + + - ]: 28 : switch (bf_matcher_get_op(matcher)) {
182 : 8 : case BF_MATCHER_EQ:
183 [ - + ]: 8 : EMIT_FIXUP_JMP_NEXT_RULE(program,
184 : : BPF_JMP_IMM(BPF_JNE, BPF_REG_0, hash[0], 0));
185 : 8 : break;
186 : 8 : case BF_MATCHER_NE:
187 [ - + ]: 8 : EMIT_FIXUP_JMP_NEXT_RULE(program,
188 : : BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, hash[0], 0));
189 : 8 : break;
190 : 12 : case BF_MATCHER_RANGE:
191 [ - + ]: 12 : EMIT_FIXUP_JMP_NEXT_RULE(program,
192 : : BPF_JMP_IMM(BPF_JLT, BPF_REG_0, hash[0], 0));
193 [ - + ]: 12 : EMIT_FIXUP_JMP_NEXT_RULE(program,
194 : : BPF_JMP_IMM(BPF_JGT, BPF_REG_0, hash[1], 0));
195 : 12 : break;
196 : 0 : default:
197 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher operator '%s' (%d)",
198 : : bf_matcher_op_to_str(bf_matcher_get_op(matcher)),
199 : : bf_matcher_get_op(matcher));
200 : : }
201 : :
202 : : return 0;
203 : : }
204 : :
205 : 375 : int bf_matcher_generate_meta(struct bf_program *program,
206 : : const struct bf_matcher *matcher)
207 : : {
208 : : int r;
209 : :
210 [ + + + + : 375 : switch (bf_matcher_get_type(matcher)) {
+ + + - ]
211 : 20 : case BF_MATCHER_META_IFACE:
212 : 20 : r = _bf_matcher_generate_meta_iface(program, matcher);
213 : 20 : break;
214 : 60 : case BF_MATCHER_META_L3_PROTO:
215 : 60 : r = _bf_matcher_generate_meta_l3_proto(program, matcher);
216 : 60 : break;
217 : 31 : case BF_MATCHER_META_L4_PROTO:
218 : 31 : r = _bf_matcher_generate_meta_l4_proto(program, matcher);
219 : 31 : break;
220 : 30 : case BF_MATCHER_META_PROBABILITY:
221 : 30 : r = _bf_matcher_generate_meta_probability(program, matcher);
222 : 30 : break;
223 : 188 : case BF_MATCHER_META_SPORT:
224 : : case BF_MATCHER_META_DPORT:
225 : 188 : r = _bf_matcher_generate_meta_port(program, matcher);
226 : 188 : break;
227 : 18 : case BF_MATCHER_META_MARK:
228 : 18 : r = _bf_matcher_generate_meta_mark(program, matcher);
229 : 18 : break;
230 : 28 : case BF_MATCHER_META_FLOW_HASH:
231 : 28 : r = _bf_matcher_generate_meta_flow_hash(program, matcher);
232 : 28 : break;
233 : 0 : default:
234 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher type %d",
235 : : bf_matcher_get_type(matcher));
236 : : };
237 : :
238 : : if (r)
239 : : return r;
240 : :
241 : : return 0;
242 : : }
|