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/cgen.h"
7 :
8 : #include <errno.h>
9 : #include <stddef.h>
10 : #include <stdint.h>
11 : #include <stdlib.h>
12 : #include <string.h>
13 :
14 : #include "bpfilter/cgen/dump.h"
15 : #include "bpfilter/cgen/program.h"
16 : #include "bpfilter/ctx.h"
17 : #include "core/chain.h"
18 : #include "core/dump.h"
19 : #include "core/front.h"
20 : #include "core/helper.h"
21 : #include "core/hook.h"
22 : #include "core/list.h"
23 : #include "core/logger.h"
24 : #include "core/marsh.h"
25 : #include "core/ns.h"
26 : #include "core/opts.h"
27 : #include "core/rule.h"
28 :
29 9 : int bf_cgen_new(struct bf_cgen **cgen, enum bf_front front,
30 : struct bf_chain **chain)
31 : {
32 12 : bf_assert(cgen && chain && *chain);
33 :
34 6 : *cgen = malloc(sizeof(struct bf_cgen));
35 6 : if (!*cgen)
36 : return -ENOMEM;
37 :
38 5 : (*cgen)->front = front;
39 5 : (*cgen)->program = NULL;
40 5 : (*cgen)->chain = TAKE_PTR(*chain);
41 :
42 5 : return 0;
43 : }
44 :
45 3 : int bf_cgen_new_from_marsh(struct bf_cgen **cgen, const struct bf_marsh *marsh)
46 : {
47 1 : _cleanup_bf_cgen_ struct bf_cgen *_cgen = NULL;
48 1 : _cleanup_bf_program_ struct bf_program *program = NULL;
49 1 : _cleanup_bf_chain_ struct bf_chain *chain = NULL;
50 : struct bf_marsh *marsh_elem = NULL;
51 : enum bf_front front;
52 : int r;
53 :
54 3 : bf_assert(cgen);
55 2 : bf_assert(marsh);
56 :
57 1 : if (!(marsh_elem = bf_marsh_next_child(marsh, marsh_elem)))
58 : return -EINVAL;
59 1 : memcpy(&front, marsh_elem->data, sizeof(front));
60 :
61 1 : if (!(marsh_elem = bf_marsh_next_child(marsh, marsh_elem)))
62 : return -EINVAL;
63 :
64 1 : r = bf_chain_new_from_marsh(&chain, marsh_elem);
65 1 : if (r < 0)
66 : return r;
67 :
68 1 : r = bf_cgen_new(&_cgen, front, &chain);
69 1 : if (r)
70 0 : return bf_err_r(r, "failed to allocate codegen object");
71 :
72 1 : if (!(marsh_elem = bf_marsh_next_child(marsh, marsh_elem)))
73 : return -EINVAL;
74 1 : if (!bf_marsh_is_empty(marsh_elem)) {
75 0 : r = bf_program_unmarsh(marsh_elem, &_cgen->program, _cgen->chain);
76 0 : if (r < 0)
77 : return r;
78 : }
79 :
80 1 : if (bf_marsh_next_child(marsh, marsh_elem))
81 0 : bf_warn("codegen marsh has more children than expected");
82 :
83 1 : *cgen = TAKE_PTR(_cgen);
84 :
85 1 : return 0;
86 : }
87 :
88 9 : void bf_cgen_free(struct bf_cgen **cgen)
89 : {
90 9 : bf_assert(cgen);
91 :
92 8 : if (!*cgen)
93 : return;
94 :
95 5 : bf_program_free(&(*cgen)->program);
96 5 : bf_chain_free(&(*cgen)->chain);
97 :
98 5 : free(*cgen);
99 5 : *cgen = NULL;
100 : }
101 :
102 3 : int bf_cgen_marsh(const struct bf_cgen *cgen, struct bf_marsh **marsh)
103 : {
104 1 : _cleanup_bf_marsh_ struct bf_marsh *_marsh = NULL;
105 : int r;
106 :
107 3 : bf_assert(cgen);
108 2 : bf_assert(marsh);
109 :
110 1 : r = bf_marsh_new(&_marsh, NULL, 0);
111 1 : if (r)
112 : return r;
113 :
114 1 : r = bf_marsh_add_child_raw(&_marsh, &cgen->front, sizeof(cgen->front));
115 1 : if (r < 0)
116 0 : return bf_err_r(r, "failed to serialize codegen");
117 :
118 : {
119 : // Serialize cgen.chain
120 1 : _cleanup_bf_marsh_ struct bf_marsh *chain_elem = NULL;
121 :
122 1 : r = bf_chain_marsh(cgen->chain, &chain_elem);
123 1 : if (r < 0)
124 : return r;
125 :
126 1 : r = bf_marsh_add_child_obj(&_marsh, chain_elem);
127 1 : if (r < 0)
128 : return r;
129 : }
130 :
131 : {
132 1 : _cleanup_bf_marsh_ struct bf_marsh *prog_elem = NULL;
133 :
134 1 : if (cgen->program) {
135 0 : r = bf_program_marsh(cgen->program, &prog_elem);
136 0 : if (r < 0)
137 : return r;
138 : } else {
139 1 : r = bf_marsh_new(&prog_elem, NULL, 0);
140 1 : if (r < 0)
141 : return r;
142 : }
143 :
144 1 : r = bf_marsh_add_child_obj(&_marsh, prog_elem);
145 1 : if (r)
146 : return r;
147 : }
148 :
149 1 : *marsh = TAKE_PTR(_marsh);
150 :
151 1 : return 0;
152 : }
153 :
154 0 : int bf_cgen_unload(struct bf_cgen *cgen)
155 : {
156 0 : bf_assert(cgen);
157 :
158 0 : return bf_program_unload(cgen->program);
159 : }
160 :
161 0 : void bf_cgen_dump(const struct bf_cgen *cgen, prefix_t *prefix)
162 : {
163 0 : bf_assert(cgen);
164 0 : bf_assert(prefix);
165 :
166 0 : DUMP(prefix, "struct bf_cgen at %p", cgen);
167 :
168 0 : bf_dump_prefix_push(prefix);
169 0 : DUMP(prefix, "front: %s", bf_front_to_str(cgen->front));
170 :
171 : // Chain
172 0 : DUMP(prefix, "chain: struct bf_chain *");
173 0 : bf_dump_prefix_push(prefix);
174 0 : bf_chain_dump(cgen->chain, bf_dump_prefix_last(prefix));
175 0 : bf_dump_prefix_pop(prefix);
176 :
177 : // Programs
178 0 : if (cgen->program) {
179 0 : DUMP(bf_dump_prefix_last(prefix), "program: struct bf_program *");
180 0 : bf_dump_prefix_push(prefix);
181 0 : bf_program_dump(cgen->program, bf_dump_prefix_last(prefix));
182 0 : bf_dump_prefix_pop(prefix);
183 : } else {
184 0 : DUMP(bf_dump_prefix_last(prefix), "program: (struct bf_program *)NULL");
185 : }
186 :
187 0 : bf_dump_prefix_pop(prefix);
188 0 : }
189 :
190 0 : int bf_cgen_get_counter(const struct bf_cgen *cgen,
191 : enum bf_counter_type counter_idx,
192 : struct bf_counter *counter)
193 : {
194 0 : bf_assert(cgen && counter);
195 :
196 : /* There are two more counter than rules. The special counters must
197 : * be accessed via the specific values, to avoid confusion. */
198 0 : enum bf_counter_type rule_count = bf_list_size(&cgen->chain->rules);
199 0 : if (counter_idx == BF_COUNTER_POLICY) {
200 : counter_idx = rule_count;
201 0 : } else if (counter_idx == BF_COUNTER_ERRORS) {
202 0 : counter_idx = rule_count + 1;
203 0 : } else if (counter_idx < 0 || counter_idx >= rule_count) {
204 : return -EINVAL;
205 : }
206 :
207 0 : return bf_program_get_counter(cgen->program, counter_idx, counter);
208 : }
209 :
210 0 : int bf_cgen_up(struct bf_cgen *cgen, const struct bf_ns *ns)
211 : {
212 0 : _cleanup_bf_program_ struct bf_program *prog = NULL;
213 : int r;
214 :
215 0 : bf_assert(cgen);
216 :
217 0 : if (bf_opts_is_verbose(BF_VERBOSE_DEBUG))
218 0 : bf_cgen_dump(cgen, EMPTY_PREFIX);
219 :
220 0 : r = bf_program_new(&prog, cgen->chain->hook, cgen->front, cgen->chain);
221 0 : if (r < 0)
222 : return r;
223 :
224 0 : r = bf_program_generate(prog);
225 0 : if (r < 0) {
226 0 : return bf_err_r(r, "failed to generate bf_program for %s",
227 : bf_hook_to_str(cgen->chain->hook));
228 : }
229 :
230 0 : r = bf_ns_set(ns, bf_ctx_get_ns());
231 0 : if (r)
232 0 : return bf_err_r(r, "failed to switch to the client's namespaces");
233 :
234 0 : r = bf_program_load(prog, NULL);
235 0 : if (r < 0) {
236 0 : if (bf_ns_set(bf_ctx_get_ns(), ns))
237 0 : bf_abort("failed to restore previous namespaces, aborting");
238 0 : return bf_err_r(r, "failed to load and attach the chain");
239 : }
240 :
241 0 : if (bf_ns_set(bf_ctx_get_ns(), ns))
242 0 : bf_abort("failed to restore previous namespaces, aborting");
243 :
244 0 : cgen->program = TAKE_PTR(prog);
245 :
246 0 : return r;
247 : }
248 :
249 0 : int bf_cgen_update(struct bf_cgen *cgen, struct bf_chain **new_chain,
250 : const struct bf_ns *ns)
251 : {
252 0 : _cleanup_bf_program_ struct bf_program *new_prog = NULL;
253 : int r;
254 :
255 0 : bf_assert(cgen && new_chain);
256 :
257 0 : r = bf_program_new(&new_prog, (*new_chain)->hook, cgen->front, *new_chain);
258 0 : if (r < 0)
259 0 : return bf_err_r(r, "failed to create a new bf_program");
260 :
261 0 : r = bf_program_generate(new_prog);
262 0 : if (r < 0) {
263 0 : return bf_err_r(r,
264 : "failed to generate the bytecode for a new bf_program");
265 : }
266 :
267 0 : r = bf_ns_set(ns, bf_ctx_get_ns());
268 0 : if (r)
269 0 : return bf_err_r(r, "failed to switch to the client's namespaces");
270 :
271 0 : r = bf_program_load(new_prog, cgen->program);
272 0 : if (r < 0) {
273 0 : if (bf_ns_set(bf_ctx_get_ns(), ns))
274 0 : bf_abort("failed to restore previous namespaces, aborting");
275 0 : return bf_err_r(r, "failed to load and attach the new chain");
276 : }
277 :
278 0 : if (bf_ns_set(bf_ctx_get_ns(), ns))
279 0 : bf_abort("failed to restore previous namespaces, aborting");
280 :
281 0 : bf_swap(cgen->program, new_prog);
282 0 : bf_swap(cgen->chain, *new_chain);
283 :
284 0 : if (bf_opts_is_verbose(BF_VERBOSE_DEBUG))
285 0 : bf_cgen_dump(cgen, EMPTY_PREFIX);
286 :
287 : return 0;
288 : }
|