Branch data 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 : : #pragma once
7 : :
8 : : #include <stddef.h>
9 : : #include <stdint.h>
10 : :
11 : : #include <bpfilter/chain.h>
12 : : #include <bpfilter/core/list.h>
13 : : #include <bpfilter/core/vector.h>
14 : : #include <bpfilter/dump.h>
15 : : #include <bpfilter/elfstub.h>
16 : : #include <bpfilter/flavor.h>
17 : : #include <bpfilter/helper.h>
18 : : #include <bpfilter/pack.h>
19 : :
20 : : #include "cgen/fixup.h"
21 : : #include "cgen/printer.h"
22 : : #include "cgen/runtime.h"
23 : : #include "filter.h"
24 : :
25 : : /**
26 : : * @file program.h
27 : : *
28 : : * @ref bf_program is used to represent a BPF program. It contains the BPF
29 : : * bytecode, as well as the required maps and metadata.
30 : : *
31 : : * **Workflow**
32 : : *
33 : : * The program is composed of different steps:
34 : : * 1. Initialize the generic context
35 : : * 2. Preprocess the packet's headers: gather information about the packet's
36 : : * size, the protocols available, the input interface...
37 : : * 3. Execute the filtering rules: execute all the rules defined in the program
38 : : * sequentially. If a rule matches the packet, apply its verdict and return.
39 : : * 4. Apply the policy if no rule matched: if no rule matched the packet, return
40 : : * the chain's policy (default action).
41 : : *
42 : : * **Memory layout**
43 : : *
44 : : * The program will use the BPF registers to following way:
45 : : * - @c r0 : return value
46 : : * - @c r1 to @c r5 (included): general purpose registers
47 : : * - @c r6 : address of the header currently filtered on
48 : : * - @c r7 : L3 protocol ID
49 : : * - @c r8 : L4 protocol ID
50 : : * - @c r9 : unused
51 : : * - @c r10 : frame pointer
52 : : *
53 : : * This convention is followed throughout the project and must be followed all
54 : : * the time to prevent incompatibilities. Debugging this kind of issues is not
55 : : * fun, so stick to it.
56 : : *
57 : : * @warning L3 and L4 protocol IDs **must** be stored in registers, no on the
58 : : * stack, as older verifier aren't able to keep track of scalar values located
59 : : * on the stack. This means the verification will fail because the verifier
60 : : * can't verify branches properly.
61 : : *
62 : : * `bf_runtime` is used to represent the layout of the first stack frame in the
63 : : * program. It is filled during preprocessing and contains data required for
64 : : * packet filtering.
65 : : *
66 : : * **About preprocessing**
67 : : *
68 : : * The packets are preprocessed according to the program type (i.e. BPF flavor).
69 : : * Each flavor needs to perform the following steps during preprocessing:
70 : : * - Store the packet size and the input interface index into the runtime context
71 : : * - Create a BPF dynamic pointer for the packet
72 : : * - Preprocess the L2, L3, and L4 headers
73 : : *
74 : : * The header's preprocessing is required to discover the protocols used in the
75 : : * packet: processing L2 will provide us with information about L3, and so on. The
76 : : * logic used to process layer X is responsible for discovering layer X+1: the L2
77 : : * header preprocessing logic will discover the L3 protocol ID. When processing
78 : : * layer X, if the protocol is not supported, the protocol ID is reset to 0 (so
79 : : * we won't execute the rules for this layer) and subsequent layers are not
80 : : * processed (because we can't discover their protocol).
81 : : *
82 : : * For example, assuming IPv6 and TCP are the only supported protocols:
83 : : * - L2 processing: discover the packet's ethertype (IPv6), and store it into
84 : : * @c r7 .
85 : : * - L3 processing: the protocol ID in @c r7 is supported (IPv6), so a slice is
86 : : * created, and the L4 protocol ID is read from the IPV6 header into @c r8 .
87 : : * - L4 processing: the protocol ID in @c r8 is supported (TCP), so a slice
88 : : * is created.
89 : : * - The program can now start executing the rules.
90 : : *
91 : : * However, assuming only IPv6 and UDP are supported:
92 : : * - L2 processing: discover the packet's ethertype (IPv6), and store it into
93 : : * @c r7 .
94 : : * - L3 processing: the protocol ID in @c r7 is supported (IPv6), so a slice is
95 : : * created, and the L4 protocol ID is read from the IPV6 header into @c r8 .
96 : : * - L4 processing: the protocol ID in @c r8 is no supported (TCP), @c r8 is
97 : : * set to 0 and we stop processing this layer.
98 : : * - The program can now start executing the rules. No layer 4 rule will be
99 : : * executed as @c r8 won't match any protocol ID.
100 : : */
101 : :
102 : : #define EMIT(program, x) \
103 : : ({ \
104 : : int __r = bf_program_emit((program), (x)); \
105 : : if (__r < 0) \
106 : : return __r; \
107 : : })
108 : :
109 : : #define EMIT_KFUNC_CALL(program, function) \
110 : : ({ \
111 : : int __r = bf_program_emit_kfunc_call((program), (function)); \
112 : : if (__r < 0) \
113 : : return __r; \
114 : : })
115 : :
116 : : #define EMIT_FIXUP(program, type, insn) \
117 : : ({ \
118 : : int __r = bf_program_emit_fixup((program), (type), (insn), NULL); \
119 : : if (__r < 0) \
120 : : return __r; \
121 : : })
122 : :
123 : : #define EMIT_FIXUP_ELFSTUB(program, elfstub_id) \
124 : : ({ \
125 : : int __r = bf_program_emit_fixup_elfstub((program), (elfstub_id)); \
126 : : if (__r < 0) \
127 : : return __r; \
128 : : })
129 : :
130 : : #define EMIT_FIXUP_JMP_NEXT_RULE(program, insn) \
131 : : ({ \
132 : : int __r = bf_program_emit_fixup( \
133 : : (program), BF_FIXUP_TYPE_JMP_NEXT_RULE, (insn), NULL); \
134 : : if (__r < 0) \
135 : : return __r; \
136 : : })
137 : :
138 : : #define EMIT_LOAD_COUNTERS_FD_FIXUP(program, reg) \
139 : : ({ \
140 : : const struct bpf_insn ld_insn[2] = {BPF_LD_MAP_FD(reg, 0)}; \
141 : : int __r = bf_program_emit_fixup( \
142 : : (program), BF_FIXUP_TYPE_COUNTERS_MAP_FD, ld_insn[0], NULL); \
143 : : if (__r < 0) \
144 : : return __r; \
145 : : __r = bf_program_emit((program), ld_insn[1]); \
146 : : if (__r < 0) \
147 : : return __r; \
148 : : })
149 : :
150 : : #define EMIT_LOAD_LOG_FD_FIXUP(program, reg) \
151 : : ({ \
152 : : const struct bpf_insn ld_insn[2] = {BPF_LD_MAP_FD(reg, 0)}; \
153 : : int __r = bf_program_emit_fixup((program), BF_FIXUP_TYPE_LOG_MAP_FD, \
154 : : ld_insn[0], NULL); \
155 : : if (__r < 0) \
156 : : return __r; \
157 : : __r = bf_program_emit((program), ld_insn[1]); \
158 : : if (__r < 0) \
159 : : return __r; \
160 : : })
161 : :
162 : : /**
163 : : * Load a specific set's file descriptor.
164 : : *
165 : : * @note Similarly to every @c EMIT_* macro, it must be called from a function
166 : : * returning an @c int , if the call fails, the macro will return a negative
167 : : * errno value.
168 : : *
169 : : * @param program Program to generate the bytecode for. Can't be NULL.
170 : : * @param reg Register to store the set file descriptor in.
171 : : * @param index Index of the set in the program.
172 : : */
173 : : #define EMIT_LOAD_SET_FD_FIXUP(program, reg, index) \
174 : : ({ \
175 : : union bf_fixup_attr __attr; \
176 : : memset(&__attr, 0, sizeof(__attr)); \
177 : : __attr.set_index = (index); \
178 : : const struct bpf_insn ld_insn[2] = {BPF_LD_MAP_FD(reg, 0)}; \
179 : : int __r = bf_program_emit_fixup((program), BF_FIXUP_TYPE_SET_MAP_FD, \
180 : : ld_insn[0], &__attr); \
181 : : if (__r < 0) \
182 : : return __r; \
183 : : __r = bf_program_emit((program), ld_insn[1]); \
184 : : if (__r < 0) \
185 : : return __r; \
186 : : })
187 : :
188 : : struct bf_chain;
189 : : struct bf_counter;
190 : : struct bf_hookopts;
191 : : struct bf_handle;
192 : :
193 : : struct bf_program
194 : : {
195 : : enum bf_flavor flavor;
196 : :
197 : : /// Log messages printer
198 : : struct bf_printer *printer;
199 : :
200 : : /** Handle containing BPF object references (prog_fd, maps, link).
201 : : * Non-owning pointer, passed in from `bf_cgen` via `bf_program_new()`.
202 : : * Populated during load/attach. */
203 : : struct bf_handle *handle;
204 : :
205 : : /* Bytecode */
206 : : uint32_t elfstubs_location[_BF_ELFSTUB_MAX];
207 : : bf_vector img;
208 : : bf_list fixups;
209 : :
210 : : /** Runtime data used to interact with the program and cache information.
211 : : * This data is not serialized. */
212 : : struct
213 : : {
214 : : /** Hook-specific ops to use to generate the program. */
215 : : const struct bf_flavor_ops *ops;
216 : :
217 : : /** Chain the program is generated from. This is a non-owning pointer:
218 : : * the @ref bf_program doesn't have to manage its lifetime. */
219 : : const struct bf_chain *chain;
220 : : } runtime;
221 : : };
222 : :
223 : : #define _free_bf_program_ __attribute__((__cleanup__(bf_program_free)))
224 : :
225 : : /**
226 : : * @brief Allocate and initialize a new `bf_program` object.
227 : : *
228 : : * @param program `bf_program` object to allocate and initialize. Can't be NULL.
229 : : * @param chain Chain the program is generated from. Can't be NULL.
230 : : * @param handle Handle to store BPF object references in. The program borrows
231 : : * this pointer (non-owning). Can't be NULL.
232 : : * @return 0 on success, or a negative error value on failure.
233 : : */
234 : : int bf_program_new(struct bf_program **program, const struct bf_chain *chain,
235 : : struct bf_handle *handle);
236 : :
237 : : void bf_program_free(struct bf_program **program);
238 : :
239 : : void bf_program_dump(const struct bf_program *program, prefix_t *prefix);
240 : :
241 : : static inline int bf_program_emit(struct bf_program *program,
242 : : struct bpf_insn insn)
243 : : {
244 : : assert(program);
245 : :
246 : 269870 : return bf_vector_add(&program->img, &insn);
247 : : }
248 : :
249 : : int bf_program_emit_kfunc_call(struct bf_program *program, const char *name);
250 : : int bf_program_emit_fixup(struct bf_program *program, enum bf_fixup_type type,
251 : : struct bpf_insn insn,
252 : : const union bf_fixup_attr *attr);
253 : : int bf_program_emit_fixup_elfstub(struct bf_program *program,
254 : : enum bf_elfstub_id id);
255 : : int bf_program_generate(struct bf_program *program);
256 : :
257 : : /**
258 : : * Load the BPF program into the kernel.
259 : : *
260 : : * Prior to loading the BPF program, multiple BPF maps are created to store
261 : : * the counters, the debug strings, and the sets. If the program can't be
262 : : * loaded, all the maps are destroyed.
263 : : *
264 : : * Once the loading succeeds, the program and the maps are pinned to the
265 : : * filesystem. If the BPF objects can't be pinned, the program is unloaded and
266 : : * the maps destroyed.
267 : : *
268 : : * @param prog Program to load into the kernel. Can't be NULL and must contain
269 : : * instructions.
270 : : * @return 0 on success, or negative errno value on failure.
271 : : */
272 : : int bf_program_load(struct bf_program *prog);
273 : :
274 : : int bf_program_get_counter(const struct bf_program *program,
275 : : uint32_t counter_idx, struct bf_counter *counter);
276 : : int bf_program_set_counters(struct bf_program *program,
277 : : const struct bf_counter *counters);
278 : :
279 : : size_t bf_program_chain_counter_idx(const struct bf_program *program);
280 : : size_t bf_program_error_counter_idx(const struct bf_program *program);
|