Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/cgen/stub.h"
7 :
8 : #include <linux/bpf.h>
9 : #include <linux/bpf_common.h>
10 : #include <linux/icmpv6.h>
11 : #include <linux/if_ether.h>
12 : #include <linux/in.h> // NOLINT
13 : #include <linux/in6.h>
14 : #include <linux/ip.h>
15 : #include <linux/ipv6.h>
16 : #include <linux/tcp.h>
17 : #include <linux/udp.h>
18 :
19 : #include <endian.h>
20 : #include <stddef.h>
21 :
22 : #include "bpfilter/cgen/fixup.h"
23 : #include "bpfilter/cgen/jmp.h"
24 : #include "bpfilter/cgen/printer.h"
25 : #include "bpfilter/cgen/program.h"
26 : #include "bpfilter/cgen/swich.h"
27 : #include "bpfilter/opts.h"
28 : #include "core/flavor.h"
29 : #include "core/helper.h"
30 : #include "core/verdict.h"
31 :
32 : #include "external/filter.h"
33 :
34 : /**
35 : * Generate stub to create a dynptr.
36 : *
37 : * @param program Program to generate the stub for. Must not be NULL.
38 : * @param arg_reg Register where the first argument to the dynptr creation
39 : * function is located (SKB or xdp_md structure).
40 : * @param kfunc Name of the kfunc to use to create the dynamic pointer.
41 : * @return 0 on success, or negative errno value on error.
42 : */
43 0 : static int _bf_stub_make_ctx_dynptr(struct bf_program *program, int arg_reg,
44 : const char *kfunc)
45 : {
46 0 : bf_assert(program && kfunc);
47 :
48 : // Call bpf_dynptr_from_xxx()
49 0 : if (arg_reg != BPF_REG_1)
50 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_1, arg_reg));
51 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, 0));
52 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_3, BPF_REG_10));
53 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, BF_PROG_CTX_OFF(dynptr)));
54 0 : EMIT_KFUNC_CALL(program, kfunc);
55 :
56 : // If the function call failed, quit the program
57 : {
58 0 : _clean_bf_jmpctx_ struct bf_jmpctx _ =
59 0 : bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0));
60 :
61 : // Update the error counter
62 0 : EMIT(program,
63 : BPF_MOV32_IMM(BPF_REG_1, bf_program_error_counter_idx(program)));
64 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10,
65 : BF_PROG_CTX_OFF(pkt_size)));
66 0 : EMIT_FIXUP_CALL(program, BF_FIXUP_FUNC_UPDATE_COUNTERS);
67 :
68 0 : if (bf_opts_is_verbose(BF_VERBOSE_BPF))
69 0 : EMIT_PRINT(program, "failed to create a new dynamic pointer");
70 :
71 0 : EMIT(program,
72 : BPF_MOV64_IMM(BPF_REG_0, program->runtime.ops->get_verdict(
73 : BF_VERDICT_ACCEPT)));
74 0 : EMIT(program, BPF_EXIT_INSN());
75 : }
76 :
77 0 : return 0;
78 : }
79 :
80 0 : int bf_stub_make_ctx_xdp_dynptr(struct bf_program *program, int md_reg)
81 : {
82 0 : bf_assert(program);
83 :
84 0 : return _bf_stub_make_ctx_dynptr(program, md_reg, "bpf_dynptr_from_xdp");
85 : }
86 :
87 0 : int bf_stub_make_ctx_skb_dynptr(struct bf_program *program, int skb_reg)
88 : {
89 0 : bf_assert(program);
90 :
91 0 : return _bf_stub_make_ctx_dynptr(program, skb_reg, "bpf_dynptr_from_skb");
92 : }
93 :
94 0 : int bf_stub_parse_l2_ethhdr(struct bf_program *program)
95 : {
96 0 : bf_assert(program);
97 :
98 : // Call bpf_dynptr_slice()
99 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
100 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(dynptr)));
101 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, 0));
102 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_3, BPF_REG_10));
103 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, BF_PROG_CTX_OFF(l2)));
104 0 : EMIT(program, BPF_MOV64_IMM(BPF_REG_4, sizeof(struct ethhdr)));
105 0 : EMIT_KFUNC_CALL(program, "bpf_dynptr_slice");
106 :
107 : // If the function call failed, quit the program
108 : {
109 0 : _clean_bf_jmpctx_ struct bf_jmpctx _ =
110 0 : bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0));
111 :
112 : // Update the error counter
113 0 : EMIT(program,
114 : BPF_MOV32_IMM(BPF_REG_1, bf_program_error_counter_idx(program)));
115 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10,
116 : BF_PROG_CTX_OFF(pkt_size)));
117 0 : EMIT_FIXUP_CALL(program, BF_FIXUP_FUNC_UPDATE_COUNTERS);
118 :
119 0 : if (bf_opts_is_verbose(BF_VERBOSE_BPF))
120 0 : EMIT_PRINT(program, "failed to create L2 dynamic pointer slice");
121 :
122 0 : EMIT(program,
123 : BPF_MOV64_IMM(BPF_REG_0, program->runtime.ops->get_verdict(
124 : BF_VERDICT_ACCEPT)));
125 0 : EMIT(program, BPF_EXIT_INSN());
126 : }
127 :
128 : // Store the L2 header address into the runtime context
129 0 : EMIT(program,
130 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, BF_PROG_CTX_OFF(l2_hdr)));
131 :
132 : // Store the L3 protocol ID in r7
133 0 : EMIT(program, BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_0,
134 : offsetof(struct ethhdr, h_proto)));
135 :
136 : // Set bf_program_context.l3_offset
137 0 : EMIT(program, BPF_ST_MEM(BPF_W, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset),
138 : sizeof(struct ethhdr)));
139 :
140 0 : return 0;
141 : }
142 :
143 0 : int bf_stub_parse_l3_hdr(struct bf_program *program)
144 : {
145 0 : _clean_bf_jmpctx_ struct bf_jmpctx _ = bf_jmpctx_default();
146 : int r;
147 :
148 0 : bf_assert(program);
149 :
150 : /* Store the size of the L3 protocol header in r4, depending on the protocol
151 : * ID stored in r7. If the protocol is not supported, we store 0 into r7
152 : * and we skip the instructions below. */
153 : {
154 0 : _clean_bf_swich_ struct bf_swich swich =
155 0 : bf_swich_get(program, BPF_REG_7);
156 :
157 0 : EMIT_SWICH_OPTION(&swich, htobe16(ETH_P_IP),
158 : BPF_MOV64_IMM(BPF_REG_4, sizeof(struct iphdr)));
159 0 : EMIT_SWICH_OPTION(&swich, htobe16(ETH_P_IPV6),
160 : BPF_MOV64_IMM(BPF_REG_4, sizeof(struct ipv6hdr)));
161 0 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_7, 0));
162 :
163 0 : r = bf_swich_generate(&swich);
164 0 : if (r)
165 : return r;
166 : }
167 0 : _ = bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_7, 0, 0));
168 :
169 : // Call bpf_dynptr_slice()
170 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
171 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(dynptr)));
172 0 : EMIT(program,
173 : BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_10, BF_PROG_CTX_OFF(l3_offset)));
174 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_3, BPF_REG_10));
175 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, BF_PROG_CTX_OFF(l2)));
176 0 : EMIT_KFUNC_CALL(program, "bpf_dynptr_slice");
177 :
178 : // If the function call failed, quit the program
179 : {
180 0 : _clean_bf_jmpctx_ struct bf_jmpctx _ =
181 0 : bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0));
182 :
183 : // Update the error counter
184 0 : EMIT(program,
185 : BPF_MOV32_IMM(BPF_REG_1, bf_program_error_counter_idx(program)));
186 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10,
187 : BF_PROG_CTX_OFF(pkt_size)));
188 0 : EMIT_FIXUP_CALL(program, BF_FIXUP_FUNC_UPDATE_COUNTERS);
189 :
190 0 : if (bf_opts_is_verbose(BF_VERBOSE_BPF))
191 0 : EMIT_PRINT(program, "failed to create L3 dynamic pointer slice");
192 :
193 0 : EMIT(program,
194 : BPF_MOV64_IMM(BPF_REG_0, program->runtime.ops->get_verdict(
195 : BF_VERDICT_ACCEPT)));
196 0 : EMIT(program, BPF_EXIT_INSN());
197 : }
198 :
199 : // Store the L3 header address into the runtime context
200 0 : EMIT(program,
201 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, BF_PROG_CTX_OFF(l3_hdr)));
202 :
203 : /* Unsupported L3 protocols have been filtered out at the beginning of this
204 : * function and would jump over the block below, so there is no need to
205 : * worry about them here. */
206 : {
207 0 : _clean_bf_swich_ struct bf_swich swich =
208 0 : bf_swich_get(program, BPF_REG_7);
209 :
210 0 : EMIT_SWICH_OPTION(&swich, htobe16(ETH_P_IP),
211 : BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
212 : BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0x0f),
213 : BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2),
214 : BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_10,
215 : BF_PROG_CTX_OFF(l3_offset)),
216 : BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2),
217 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1,
218 : BF_PROG_CTX_OFF(l4_offset)),
219 : BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_0,
220 : offsetof(struct iphdr, protocol)));
221 0 : EMIT_SWICH_OPTION(&swich, htobe16(ETH_P_IPV6),
222 : BPF_MOV64_IMM(BPF_REG_1, sizeof(struct ipv6hdr)),
223 : BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_10,
224 : BF_PROG_CTX_OFF(l3_offset)),
225 : BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2),
226 : BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1,
227 : BF_PROG_CTX_OFF(l4_offset)),
228 : BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_0,
229 : offsetof(struct ipv6hdr, nexthdr)));
230 :
231 0 : r = bf_swich_generate(&swich);
232 0 : if (r)
233 : return r;
234 : }
235 :
236 0 : return 0;
237 : }
238 :
239 0 : int bf_stub_parse_l4_hdr(struct bf_program *program)
240 : {
241 0 : _clean_bf_jmpctx_ struct bf_jmpctx _ = bf_jmpctx_default();
242 : int r;
243 :
244 0 : bf_assert(program);
245 :
246 : /* Parse the L4 protocol and handle unuspported protocol, similarly to
247 : * bf_stub_parse_l3_hdr() above. */
248 : {
249 0 : _clean_bf_swich_ struct bf_swich swich =
250 0 : bf_swich_get(program, BPF_REG_8);
251 :
252 0 : EMIT_SWICH_OPTION(&swich, IPPROTO_TCP,
253 : BPF_MOV64_IMM(BPF_REG_4, sizeof(struct tcphdr)));
254 0 : EMIT_SWICH_OPTION(&swich, IPPROTO_UDP,
255 : BPF_MOV64_IMM(BPF_REG_4, sizeof(struct udphdr)));
256 0 : EMIT_SWICH_OPTION(&swich, IPPROTO_ICMP,
257 : BPF_MOV64_IMM(BPF_REG_4, sizeof(struct udphdr)));
258 0 : EMIT_SWICH_OPTION(&swich, IPPROTO_ICMPV6,
259 : BPF_MOV64_IMM(BPF_REG_4, sizeof(struct icmp6hdr)));
260 0 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_8, 0));
261 :
262 0 : r = bf_swich_generate(&swich);
263 0 : if (r)
264 : return r;
265 : }
266 0 : _ = bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, 0, 0));
267 :
268 : // Call bpf_dynptr_slice()
269 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
270 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(dynptr)));
271 0 : EMIT(program,
272 : BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_10, BF_PROG_CTX_OFF(l4_offset)));
273 0 : EMIT(program, BPF_MOV64_REG(BPF_REG_3, BPF_REG_10));
274 0 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, BF_PROG_CTX_OFF(l4)));
275 0 : EMIT_KFUNC_CALL(program, "bpf_dynptr_slice");
276 :
277 : // If the function call failed, quit the program
278 : {
279 0 : _clean_bf_jmpctx_ struct bf_jmpctx _ =
280 0 : bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0));
281 :
282 : // Update the error counter
283 0 : EMIT(program,
284 : BPF_MOV32_IMM(BPF_REG_1, bf_program_error_counter_idx(program)));
285 0 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10,
286 : BF_PROG_CTX_OFF(pkt_size)));
287 0 : EMIT_FIXUP_CALL(program, BF_FIXUP_FUNC_UPDATE_COUNTERS);
288 :
289 0 : if (bf_opts_is_verbose(BF_VERBOSE_BPF))
290 0 : EMIT_PRINT(program, "failed to create L4 dynamic pointer slice");
291 :
292 0 : EMIT(program,
293 : BPF_MOV64_IMM(BPF_REG_0, program->runtime.ops->get_verdict(
294 : BF_VERDICT_ACCEPT)));
295 0 : EMIT(program, BPF_EXIT_INSN());
296 : }
297 :
298 : // Store the L4 header address into the runtime context
299 0 : EMIT(program,
300 : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, BF_PROG_CTX_OFF(l4_hdr)));
301 :
302 0 : return 0;
303 : }
|