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 : : #include "cgen/program.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : : #include <linux/limits.h>
11 : :
12 : : #include <errno.h>
13 : : #include <fcntl.h>
14 : : #include <limits.h>
15 : : #include <stddef.h>
16 : : #include <stdint.h>
17 : : #include <stdio.h>
18 : : #include <stdlib.h>
19 : : #include <string.h>
20 : : #include <unistd.h>
21 : :
22 : : #include <bpfilter/bpf.h>
23 : : #include <bpfilter/btf.h>
24 : : #include <bpfilter/chain.h>
25 : : #include <bpfilter/core/list.h>
26 : : #include <bpfilter/counter.h>
27 : : #include <bpfilter/ctx.h>
28 : : #include <bpfilter/dump.h>
29 : : #include <bpfilter/flavor.h>
30 : : #include <bpfilter/helper.h>
31 : : #include <bpfilter/hook.h>
32 : : #include <bpfilter/io.h>
33 : : #include <bpfilter/logger.h>
34 : : #include <bpfilter/matcher.h>
35 : : #include <bpfilter/pack.h>
36 : : #include <bpfilter/rule.h>
37 : : #include <bpfilter/set.h>
38 : : #include <bpfilter/verdict.h>
39 : :
40 : : #include "cgen/cgroup_skb.h"
41 : : #include "cgen/cgroup_sock_addr.h"
42 : : #include "cgen/dump.h"
43 : : #include "cgen/fixup.h"
44 : : #include "cgen/handle.h"
45 : : #include "cgen/jmp.h"
46 : : #include "cgen/nf.h"
47 : : #include "cgen/printer.h"
48 : : #include "cgen/prog/link.h"
49 : : #include "cgen/prog/map.h"
50 : : #include "cgen/stub.h"
51 : : #include "cgen/tc.h"
52 : : #include "cgen/xdp.h"
53 : : #include "filter.h"
54 : :
55 : : #define _BF_LOG_BUF_SIZE \
56 : : (UINT32_MAX >> 8) /* verifier maximum in kernels <= 5.1 */
57 : : #define _BF_PROGRAM_DEFAULT_IMG_SIZE (1 << 6)
58 : : #define _BF_LOG_MAP_N_ENTRIES 1000
59 : : #define _BF_LOG_MAP_SIZE \
60 : : _bf_round_next_power_of_2(sizeof(struct bf_log) * _BF_LOG_MAP_N_ENTRIES)
61 : : #define _BF_SET_MAP_PREFIX "bf_set_"
62 : : #define _BF_COUNTER_MAP_NAME "bf_cmap"
63 : : #define _BF_PRINTER_MAP_NAME "bf_pmap"
64 : : #define _BF_LOG_MAP_NAME "bf_lmap"
65 : :
66 : 2291 : static inline size_t _bf_round_next_power_of_2(size_t value)
67 : : {
68 : 2291 : value--;
69 : 2291 : value |= value >> 1;
70 : 2291 : value |= value >> 2;
71 : 2291 : value |= value >> 4;
72 : 2291 : value |= value >> 8;
73 : 2291 : value |= value >> 16;
74 : :
75 : 2291 : return ++value;
76 : : }
77 : :
78 : : static const struct bf_flavor_ops *bf_flavor_ops_get(enum bf_flavor flavor)
79 : : {
80 : : static const struct bf_flavor_ops *flavor_ops[] = {
81 : : [BF_FLAVOR_TC] = &bf_flavor_ops_tc,
82 : : [BF_FLAVOR_NF] = &bf_flavor_ops_nf,
83 : : [BF_FLAVOR_XDP] = &bf_flavor_ops_xdp,
84 : : [BF_FLAVOR_CGROUP_SKB] = &bf_flavor_ops_cgroup_skb,
85 : : [BF_FLAVOR_CGROUP_SOCK_ADDR] = &bf_flavor_ops_cgroup_sock_addr,
86 : : };
87 : :
88 : : static_assert_enum_mapping(flavor_ops, _BF_FLAVOR_MAX);
89 : :
90 : 1159 : return flavor_ops[flavor];
91 : : }
92 : :
93 : 1159 : int bf_program_new(struct bf_program **program, const struct bf_chain *chain,
94 : : struct bf_handle *handle)
95 : : {
96 : 1159 : _free_bf_program_ struct bf_program *_program = NULL;
97 : : int r;
98 : :
99 : : assert(program);
100 : : assert(chain);
101 : : assert(handle);
102 : :
103 : 1159 : _program = calloc(1, sizeof(*_program));
104 [ + - ]: 1159 : if (!_program)
105 : : return -ENOMEM;
106 : :
107 : 1159 : _program->flavor = bf_hook_to_flavor(chain->hook);
108 : 1159 : _program->runtime.ops = bf_flavor_ops_get(_program->flavor);
109 : 1159 : _program->runtime.chain = chain;
110 : 1159 : _program->fixups = bf_list_default(bf_fixup_free, NULL);
111 : 1159 : _program->handle = handle;
112 : :
113 : 1159 : r = bf_printer_new(&_program->printer);
114 [ + - ]: 1159 : if (r)
115 : : return r;
116 : :
117 : 1159 : *program = TAKE_PTR(_program);
118 : :
119 : 1159 : return 0;
120 : : }
121 : :
122 : 2318 : void bf_program_free(struct bf_program **program)
123 : : {
124 [ + + ]: 2318 : if (!*program)
125 : : return;
126 : :
127 : 1159 : bf_list_clean(&(*program)->fixups);
128 : 1159 : free((*program)->img);
129 : :
130 : 1159 : bf_printer_free(&(*program)->printer);
131 : :
132 : 1159 : free(*program);
133 : 1159 : *program = NULL;
134 : : }
135 : :
136 : 0 : void bf_program_dump(const struct bf_program *program, prefix_t *prefix)
137 : : {
138 : : assert(program);
139 : : assert(prefix);
140 : :
141 [ # # ]: 0 : DUMP(prefix, "struct bf_program at %p", program);
142 : :
143 : 0 : bf_dump_prefix_push(prefix);
144 : :
145 [ # # ]: 0 : DUMP(prefix, "handle: struct bf_handle *");
146 : 0 : bf_dump_prefix_push(prefix);
147 : 0 : bf_handle_dump(program->handle, bf_dump_prefix_last(prefix));
148 : 0 : bf_dump_prefix_pop(prefix);
149 : :
150 [ # # ]: 0 : DUMP(prefix, "printer: struct bf_printer *");
151 : 0 : bf_dump_prefix_push(prefix);
152 : 0 : bf_printer_dump(program->printer, prefix);
153 : 0 : bf_dump_prefix_pop(prefix);
154 : :
155 [ # # ]: 0 : DUMP(prefix, "img: %p", program->img);
156 [ # # ]: 0 : DUMP(prefix, "img_size: %lu", program->img_size);
157 [ # # ]: 0 : DUMP(prefix, "img_cap: %lu", program->img_cap);
158 : :
159 [ # # ]: 0 : DUMP(prefix, "fixups: bf_list<struct bf_fixup>[%lu]",
160 : : bf_list_size(&program->fixups));
161 : 0 : bf_dump_prefix_push(prefix);
162 [ # # # # : 0 : bf_list_foreach (&program->fixups, fixup_node) {
# # ]
163 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
164 : :
165 [ # # ]: 0 : if (bf_list_is_tail(&program->fixups, fixup_node))
166 : 0 : bf_dump_prefix_last(prefix);
167 : :
168 : 0 : bf_fixup_dump(fixup, prefix);
169 : : }
170 : 0 : bf_dump_prefix_pop(prefix);
171 : :
172 [ # # ]: 0 : DUMP(bf_dump_prefix_last(prefix), "runtime: <anonymous>");
173 : 0 : bf_dump_prefix_push(prefix);
174 [ # # ]: 0 : DUMP(bf_dump_prefix_last(prefix), "ops: %p", program->runtime.ops);
175 : 0 : bf_dump_prefix_pop(prefix);
176 : :
177 : 0 : bf_dump_prefix_pop(prefix);
178 : 0 : }
179 : :
180 : 3450 : int bf_program_grow_img(struct bf_program *program)
181 : : {
182 : : size_t new_cap = _BF_PROGRAM_DEFAULT_IMG_SIZE;
183 : : int r;
184 : :
185 : : assert(program);
186 : :
187 [ + + ]: 3450 : if (program->img)
188 : 2291 : new_cap = _bf_round_next_power_of_2(program->img_cap << 1);
189 : :
190 : 3450 : r = bf_realloc((void **)&program->img, new_cap * sizeof(struct bpf_insn));
191 [ - + ]: 3450 : if (r < 0) {
192 [ # # ]: 0 : return bf_err_r(r, "failed to grow program img from %lu to %lu insn",
193 : : program->img_cap, new_cap);
194 : : }
195 : :
196 : 3450 : program->img_cap = new_cap;
197 : :
198 : 3450 : return 0;
199 : : }
200 : :
201 : 19537 : static void _bf_program_fixup_insn(struct bpf_insn *insn,
202 : : enum bf_fixup_insn type, int32_t value)
203 : : {
204 [ + + - ]: 19537 : switch (type) {
205 : 4489 : case BF_FIXUP_INSN_OFF:
206 : : assert(!insn->off);
207 : : assert(value < SHRT_MAX);
208 : 4489 : insn->off = (int16_t)value;
209 : 4489 : break;
210 : 15048 : case BF_FIXUP_INSN_IMM:
211 : : assert(!insn->imm);
212 : 15048 : insn->imm = value;
213 : 15048 : break;
214 : 0 : default:
215 [ # # ]: 0 : bf_abort(
216 : : "unsupported fixup instruction type, this should not happen: %d",
217 : : type);
218 : : break;
219 : : }
220 : 19537 : }
221 : :
222 : 6063 : static int _bf_program_fixup(struct bf_program *program,
223 : : enum bf_fixup_type type)
224 : : {
225 : : assert(program);
226 : : assert(type >= 0 && type < _BF_FIXUP_TYPE_MAX);
227 : :
228 [ + - + + : 127282 : bf_list_foreach (&program->fixups, fixup_node) {
+ + ]
229 : : enum bf_fixup_insn insn_type = _BF_FIXUP_INSN_MAX;
230 : : int32_t value;
231 : : size_t offset;
232 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
233 : 57578 : struct bpf_insn *insn = &program->img[fixup->insn];
234 : : struct bf_map *map;
235 : :
236 [ + + ]: 57578 : if (type != fixup->type)
237 : 38041 : continue;
238 : :
239 [ + + + + : 19537 : switch (type) {
+ + - ]
240 : 4489 : case BF_FIXUP_TYPE_JMP_NEXT_RULE:
241 : : insn_type = BF_FIXUP_INSN_OFF;
242 : 4489 : value = (int)(program->img_size - fixup->insn - 1U);
243 : 4489 : break;
244 : 6204 : case BF_FIXUP_TYPE_COUNTERS_MAP_FD:
245 : : insn_type = BF_FIXUP_INSN_IMM;
246 : 6204 : value = program->handle->cmap->fd;
247 : 6204 : break;
248 : 1180 : case BF_FIXUP_TYPE_PRINTER_MAP_FD:
249 : : insn_type = BF_FIXUP_INSN_IMM;
250 : 1180 : value = program->handle->pmap->fd;
251 : 1180 : break;
252 : 24 : case BF_FIXUP_TYPE_LOG_MAP_FD:
253 : : insn_type = BF_FIXUP_INSN_IMM;
254 : 24 : value = program->handle->lmap->fd;
255 : 24 : break;
256 : 282 : case BF_FIXUP_TYPE_SET_MAP_FD:
257 : 282 : map = bf_list_get_at(&program->handle->sets, fixup->attr.set_index);
258 [ - + ]: 282 : if (!map) {
259 [ # # ]: 0 : return bf_err_r(-ENOENT, "can't find set map at index %lu",
260 : : fixup->attr.set_index);
261 : : }
262 : : insn_type = BF_FIXUP_INSN_IMM;
263 : 282 : value = map->fd;
264 : 282 : break;
265 : 7358 : case BF_FIXUP_ELFSTUB_CALL:
266 : : insn_type = BF_FIXUP_INSN_IMM;
267 : 7358 : offset = program->elfstubs_location[fixup->attr.elfstub_id] -
268 : : fixup->insn - 1;
269 [ - + ]: 7358 : if (offset >= INT_MAX)
270 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid ELF stub call offset");
271 : 7358 : value = (int32_t)offset;
272 : 7358 : break;
273 : 0 : default:
274 [ # # ]: 0 : bf_abort("unsupported fixup type, this should not happen: %d",
275 : : type);
276 : : break;
277 : : }
278 : :
279 : 19537 : _bf_program_fixup_insn(insn, insn_type, value);
280 : 19537 : bf_list_delete(&program->fixups, fixup_node);
281 : : }
282 : :
283 : : return 0;
284 : : }
285 : :
286 : 1421 : static int _bf_program_generate_rule(struct bf_program *program,
287 : : struct bf_rule *rule)
288 : : {
289 : : uint32_t checked_layers = 0;
290 : : int r;
291 : :
292 : : assert(program);
293 : : assert(rule);
294 : : assert(program->runtime.ops->gen_inline_matcher);
295 : :
296 [ + + ]: 1421 : if (rule->disabled)
297 : : return 0;
298 : :
299 [ + - + + : 8088 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
300 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
301 : : const struct bf_matcher_meta *meta =
302 : 2626 : bf_matcher_get_meta(bf_matcher_get_type(matcher));
303 : :
304 [ + + ]: 2626 : if (bf_matcher_get_type(matcher) == BF_MATCHER_SET)
305 : 282 : continue;
306 : :
307 [ - + ]: 2344 : if (!meta) {
308 [ # # ]: 0 : return bf_err_r(-EINVAL, "missing meta for matcher type %d",
309 : : bf_matcher_get_type(matcher));
310 : : }
311 : :
312 [ + + ]: 2344 : if (checked_layers & BF_FLAG(meta->layer))
313 : 834 : continue;
314 : :
315 : 1510 : if (meta->layer == BF_MATCHER_LAYER_2 ||
316 [ + + ]: 1510 : meta->layer == BF_MATCHER_LAYER_3 ||
317 : : meta->layer == BF_MATCHER_LAYER_4) {
318 : 893 : r = bf_stub_rule_check_protocol(program, meta);
319 [ + - ]: 893 : if (r)
320 : : return r;
321 : 893 : checked_layers |= BF_FLAG(meta->layer);
322 : : }
323 : : }
324 : :
325 [ + - + + : 8088 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
326 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
327 : :
328 : 2626 : r = program->runtime.ops->gen_inline_matcher(program, matcher);
329 [ + - ]: 2626 : if (r)
330 : : return r;
331 : : }
332 : :
333 [ + + ]: 1418 : if (bf_rule_mark_is_set(rule)) {
334 [ - + ]: 10 : if (!program->runtime.ops->gen_inline_set_mark) {
335 [ # # ]: 0 : return bf_err_r(-ENOTSUP, "set mark is not supported by %s",
336 : : program->runtime.chain->name);
337 : : }
338 : :
339 : 10 : r = program->runtime.ops->gen_inline_set_mark(program,
340 : : bf_rule_mark_get(rule));
341 [ - + ]: 10 : if (r) {
342 [ # # ]: 0 : return bf_err_r(r,
343 : : "failed to generate bytecode to set mark for '%s'",
344 : : program->runtime.chain->name);
345 : : }
346 : : }
347 : :
348 [ + + ]: 1418 : if (rule->log) {
349 [ - + ]: 33 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
350 [ - + ]: 33 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
351 [ - + ]: 33 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, rule->index));
352 [ - + ]: 33 : EMIT(program, BPF_MOV64_IMM(BPF_REG_3, rule->log));
353 [ - + ]: 33 : EMIT(program, BPF_MOV64_IMM(BPF_REG_4, rule->verdict));
354 : :
355 : : // Pack l3_proto and l4_proto
356 [ - + ]: 33 : EMIT(program, BPF_MOV64_REG(BPF_REG_5, BPF_REG_7));
357 [ - + ]: 33 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 16));
358 [ - + ]: 33 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_5, BPF_REG_8));
359 : :
360 [ + - ]: 33 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_LOG);
361 : : }
362 : :
363 [ + + ]: 1418 : if (rule->counters) {
364 [ - + ]: 1342 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
365 [ - + ]: 1342 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
366 [ + - + - ]: 1342 : EMIT_LOAD_COUNTERS_FD_FIXUP(program, BPF_REG_2);
367 [ - + ]: 1342 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, rule->index));
368 [ + - ]: 1342 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_UPDATE_COUNTERS);
369 : : }
370 : :
371 [ + + - + ]: 1418 : switch (rule->verdict) {
372 : 1366 : case BF_VERDICT_ACCEPT:
373 : : case BF_VERDICT_DROP:
374 : 1366 : r = program->runtime.ops->get_verdict(rule->verdict);
375 [ + - ]: 1366 : if (r < 0)
376 : : return r;
377 [ - + ]: 1366 : EMIT(program, BPF_MOV64_IMM(BPF_REG_0, r));
378 [ - + ]: 1366 : EMIT(program, BPF_EXIT_INSN());
379 : 1366 : break;
380 : 12 : case BF_VERDICT_REDIRECT:
381 [ + + ]: 12 : if (!program->runtime.ops->gen_inline_redirect) {
382 [ + - ]: 2 : return bf_err_r(-ENOTSUP, "redirect is not supported by %s hook",
383 : : bf_hook_to_str(program->runtime.chain->hook));
384 : : }
385 : 10 : r = program->runtime.ops->gen_inline_redirect(
386 : : program, rule->redirect_ifindex, rule->redirect_dir);
387 [ + + ]: 10 : if (r)
388 : : return r;
389 : : break;
390 : : case BF_VERDICT_CONTINUE:
391 : : // Fall through to next rule or default chain policy.
392 : : break;
393 : 0 : default:
394 [ # # ]: 0 : bf_abort("unsupported verdict, this should not happen: %d",
395 : : rule->verdict);
396 : : break;
397 : : }
398 : :
399 : 1415 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_JMP_NEXT_RULE);
400 [ + - ]: 1415 : if (r)
401 [ # # ]: 0 : return bf_err_r(r, "failed to generate next rule fixups");
402 : :
403 : : return 0;
404 : : }
405 : :
406 : 1156 : static int _bf_program_generate_elfstubs(struct bf_program *program)
407 : : {
408 : : const struct bf_elfstub *elfstub;
409 : : size_t start_at;
410 : : int r;
411 : :
412 : : assert(program);
413 : :
414 [ + - + + : 32312 : bf_list_foreach (&program->fixups, fixup_node) {
+ + ]
415 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
416 : 15000 : size_t off = program->img_size;
417 : :
418 [ + + ]: 15000 : if (fixup->type != BF_FIXUP_ELFSTUB_CALL)
419 : 7642 : continue;
420 : :
421 : : // Only generate each ELF stub once
422 [ + + ]: 7358 : if (program->elfstubs_location[fixup->attr.elfstub_id])
423 : 5057 : continue;
424 : :
425 [ - + ]: 2301 : bf_dbg("generate ELF stub for ID %d", fixup->attr.elfstub_id);
426 : :
427 : 2301 : elfstub = bf_ctx_get_elfstub(fixup->attr.elfstub_id);
428 [ - + ]: 2301 : if (!elfstub) {
429 [ # # ]: 0 : return bf_err_r(-ENOENT, "no ELF stub found for ID %d",
430 : : fixup->attr.elfstub_id);
431 : : }
432 : :
433 : 2301 : start_at = program->img_size;
434 : :
435 [ + + ]: 87022 : for (size_t i = 0; i < elfstub->ninsns; ++i) {
436 : 84721 : r = bf_program_emit(program, elfstub->insns[i]);
437 [ - + ]: 84721 : if (r)
438 [ # # ]: 0 : return bf_err_r(r, "failed to insert ELF stub instruction");
439 : : }
440 : :
441 [ + + - + : 6962 : bf_list_foreach (&elfstub->strs, pstr_node) {
+ + ]
442 : 1180 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
443 : : struct bf_printk_str *pstr = bf_list_node_get_data(pstr_node);
444 : 1180 : size_t insn_idx = start_at + pstr->insn_idx;
445 : : const struct bf_printer_msg *msg =
446 : 1180 : bf_printer_add_msg(program->printer, pstr->str);
447 : : struct bpf_insn ld_insn[2] = {
448 : : BPF_LD_MAP_FD(BPF_REG_1, 0),
449 : : };
450 : :
451 : : ld_insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
452 : 1180 : ld_insn[1].imm = (int)bf_printer_msg_offset(msg);
453 : :
454 : 1180 : program->img[insn_idx] = ld_insn[0];
455 : 1180 : program->img[insn_idx + 1] = ld_insn[1];
456 : :
457 : 1180 : r = bf_fixup_new(&fixup, BF_FIXUP_TYPE_PRINTER_MAP_FD, insn_idx,
458 : : NULL);
459 [ + - ]: 1180 : if (r)
460 : : return r;
461 : :
462 : 1180 : r = bf_list_add_tail(&program->fixups, fixup);
463 [ + - ]: 1180 : if (r)
464 : : return r;
465 : :
466 : 1180 : TAKE_PTR(fixup);
467 : : }
468 : :
469 : 2301 : program->elfstubs_location[fixup->attr.elfstub_id] = off;
470 : : }
471 : :
472 : : return 0;
473 : : }
474 : :
475 : 263207 : int bf_program_emit(struct bf_program *program, struct bpf_insn insn)
476 : : {
477 : : int r;
478 : :
479 : : assert(program);
480 : :
481 [ + + ]: 263207 : if (program->img_size == program->img_cap) {
482 : 3208 : r = bf_program_grow_img(program);
483 [ + - ]: 3208 : if (r)
484 : : return r;
485 : : }
486 : :
487 : 263207 : program->img[program->img_size++] = insn;
488 : :
489 : 263207 : return 0;
490 : : }
491 : :
492 : 3716 : int bf_program_emit_kfunc_call(struct bf_program *program, const char *name)
493 : : {
494 : : int r;
495 : :
496 : : assert(program);
497 : : assert(name);
498 : :
499 : 3716 : r = bf_btf_get_id(name);
500 [ + - ]: 3716 : if (r < 0)
501 : : return r;
502 : :
503 [ - + ]: 3716 : EMIT(program, ((struct bpf_insn) {.code = BPF_JMP | BPF_CALL,
504 : : .dst_reg = 0,
505 : : .src_reg = BPF_PSEUDO_KFUNC_CALL,
506 : : .off = 0,
507 : : .imm = r}));
508 : :
509 : 3716 : return 0;
510 : : }
511 : :
512 : 11015 : int bf_program_emit_fixup(struct bf_program *program, enum bf_fixup_type type,
513 : : struct bpf_insn insn, const union bf_fixup_attr *attr)
514 : : {
515 : 11015 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
516 : : int r;
517 : :
518 : : assert(program);
519 : :
520 [ + + ]: 11015 : if (program->img_size == program->img_cap) {
521 : 79 : r = bf_program_grow_img(program);
522 [ + - ]: 79 : if (r)
523 : : return r;
524 : : }
525 : :
526 : 11015 : r = bf_fixup_new(&fixup, type, program->img_size, attr);
527 [ + - ]: 11015 : if (r)
528 : : return r;
529 : :
530 : 11015 : r = bf_list_add_tail(&program->fixups, fixup);
531 [ + - ]: 11015 : if (r)
532 : : return r;
533 : :
534 : 11015 : TAKE_PTR(fixup);
535 : :
536 : : /* This call could fail and return an error, in which case it is not
537 : : * properly handled. However, this shouldn't be an issue as we previously
538 : : * test whether enough room is available in cgen.img, which is currently
539 : : * the only reason for EMIT() to fail. */
540 : 11015 : EMIT(program, insn);
541 : :
542 : : return 0;
543 : : }
544 : :
545 : 7371 : int bf_program_emit_fixup_elfstub(struct bf_program *program,
546 : : enum bf_elfstub_id id)
547 : : {
548 : 7371 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
549 : : int r;
550 : :
551 : : assert(program);
552 : :
553 [ + + ]: 7371 : if (program->img_size == program->img_cap) {
554 : 163 : r = bf_program_grow_img(program);
555 [ + - ]: 163 : if (r)
556 : : return r;
557 : : }
558 : :
559 : 7371 : r = bf_fixup_new(&fixup, BF_FIXUP_ELFSTUB_CALL, program->img_size, NULL);
560 [ + - ]: 7371 : if (r)
561 : : return r;
562 : :
563 : 7371 : fixup->attr.elfstub_id = id;
564 : :
565 : 7371 : r = bf_list_add_tail(&program->fixups, fixup);
566 [ + - ]: 7371 : if (r)
567 : : return r;
568 : :
569 : 7371 : TAKE_PTR(fixup);
570 : :
571 [ - + ]: 7371 : EMIT(program, BPF_CALL_REL(0));
572 : :
573 : 7371 : return 0;
574 : : }
575 : :
576 : 1159 : int bf_program_generate(struct bf_program *program)
577 : : {
578 : 1159 : const struct bf_chain *chain = program->runtime.chain;
579 : : int r;
580 : :
581 : : // Save the program's argument into the context.
582 [ - + ]: 1159 : EMIT(program,
583 : : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
584 : :
585 : : // Reset the protocol ID registers
586 [ - + ]: 1159 : EMIT(program, BPF_MOV64_IMM(BPF_REG_7, 0));
587 [ - + ]: 1159 : EMIT(program, BPF_MOV64_IMM(BPF_REG_8, 0));
588 : :
589 : : // If at least one rule logs the matched packets, populate ctx->log_map
590 [ + + ]: 1159 : if (program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG)) {
591 [ + - + - ]: 24 : EMIT_LOAD_LOG_FD_FIXUP(program, BPF_REG_2);
592 [ - + ]: 24 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2,
593 : : BF_PROG_CTX_OFF(log_map)));
594 : : }
595 : :
596 : : // Zeroing IPv6 extension headers
597 [ + + ]: 1159 : if (program->runtime.chain->flags & BF_FLAG(BF_CHAIN_STORE_NEXTHDR)) {
598 [ - + ]: 30 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_7,
599 : : BF_PROG_CTX_OFF(ipv6_eh)));
600 : : }
601 : :
602 : 1159 : r = program->runtime.ops->gen_inline_prologue(program);
603 [ + - ]: 1159 : if (r)
604 : : return r;
605 : :
606 [ + + + + : 5154 : bf_list_foreach (&chain->rules, rule_node) {
+ + ]
607 : 1421 : r = _bf_program_generate_rule(program,
608 : : bf_list_node_get_data(rule_node));
609 [ + + ]: 1421 : if (r)
610 : : return r;
611 : : }
612 : :
613 : 1156 : r = program->runtime.ops->gen_inline_epilogue(program);
614 [ + - ]: 1156 : if (r)
615 : : return r;
616 : :
617 : : // Call the update counters function
618 : : /// @todo Allow chains to have no counters at all.
619 [ - + ]: 1156 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
620 [ - + ]: 1156 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
621 [ + - + - ]: 1156 : EMIT_LOAD_COUNTERS_FD_FIXUP(program, BPF_REG_2);
622 [ - + ]: 1156 : EMIT(program,
623 : : BPF_MOV32_IMM(BPF_REG_3, bf_program_chain_counter_idx(program)));
624 [ + - ]: 1156 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_UPDATE_COUNTERS);
625 : :
626 : 1156 : r = program->runtime.ops->get_verdict(chain->policy);
627 [ + - ]: 1156 : if (r < 0)
628 : : return r;
629 [ - + ]: 1156 : EMIT(program, BPF_MOV64_IMM(BPF_REG_0, r));
630 [ - + ]: 1156 : EMIT(program, BPF_EXIT_INSN());
631 : :
632 : 1156 : r = _bf_program_generate_elfstubs(program);
633 [ + - ]: 1156 : if (r)
634 : : return r;
635 : :
636 : 1156 : r = _bf_program_fixup(program, BF_FIXUP_ELFSTUB_CALL);
637 [ - + ]: 1156 : if (r)
638 [ # # ]: 0 : return bf_err_r(r, "failed to generate ELF stub call fixups");
639 : :
640 : : return 0;
641 : : }
642 : :
643 : 1156 : static int _bf_program_load_printer_map(struct bf_program *program)
644 : : {
645 : 1156 : _cleanup_free_ void *pstr = NULL;
646 : : size_t pstr_len;
647 : 1156 : uint32_t key = 0;
648 : : int r;
649 : :
650 : : assert(program);
651 : :
652 : 1156 : r = bf_printer_assemble(program->printer, &pstr, &pstr_len);
653 [ - + ]: 1156 : if (r)
654 [ # # ]: 0 : return bf_err_r(r, "failed to assemble printer map string");
655 : :
656 : 1156 : r = bf_map_new(&program->handle->pmap, _BF_PRINTER_MAP_NAME,
657 : : BF_MAP_TYPE_PRINTER, sizeof(uint32_t), pstr_len, 1);
658 [ - + ]: 1156 : if (r)
659 [ # # ]: 0 : return bf_err_r(r, "failed to create the printer bf_map object");
660 : :
661 : 1156 : r = bf_map_set_elem(program->handle->pmap, &key, pstr);
662 [ - + ]: 1156 : if (r)
663 [ # # ]: 0 : return bf_err_r(r, "failed to set print map elem");
664 : :
665 : 1156 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_PRINTER_MAP_FD);
666 [ - + ]: 1156 : if (r)
667 [ # # ]: 0 : return bf_err_r(r, "failed to fixup printer map FD");
668 : :
669 : : return 0;
670 : : }
671 : :
672 : 1156 : static int _bf_program_load_counters_map(struct bf_program *program)
673 : : {
674 : : int r;
675 : :
676 : : assert(program);
677 : :
678 : 1156 : r = bf_map_new(&program->handle->cmap, _BF_COUNTER_MAP_NAME,
679 : : BF_MAP_TYPE_COUNTERS, sizeof(uint32_t),
680 : : sizeof(struct bf_counter),
681 : 1156 : bf_list_size(&program->runtime.chain->rules) + 2);
682 [ - + ]: 1156 : if (r)
683 [ # # ]: 0 : return bf_err_r(r, "failed to create the counters bf_map object");
684 : :
685 : 1156 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_COUNTERS_MAP_FD);
686 [ - + ]: 1156 : if (r)
687 [ # # ]: 0 : return bf_err_r(r, "failed to fixup counters map FD");
688 : :
689 : : return 0;
690 : : }
691 : :
692 : 1156 : static int _bf_program_load_log_map(struct bf_program *program)
693 : : {
694 : : int r;
695 : :
696 : : assert(program);
697 : :
698 : : // Do not create a log map if it's unused in the chain
699 [ + + ]: 1156 : if (!(program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG)))
700 : : return 0;
701 : :
702 : 24 : r = bf_map_new(&program->handle->lmap, _BF_LOG_MAP_NAME, BF_MAP_TYPE_LOG, 0,
703 : : 0, _BF_LOG_MAP_SIZE);
704 [ - + ]: 24 : if (r)
705 [ # # ]: 0 : return bf_err_r(r, "failed to create the log bf_map object");
706 : :
707 : 24 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_LOG_MAP_FD);
708 [ + - ]: 24 : if (r)
709 [ # # ]: 0 : return bf_err_r(r, "failed to fixup log map FD");
710 : :
711 : : return 0;
712 : : }
713 : :
714 : 1156 : static int _bf_program_load_sets_maps(struct bf_program *new_prog)
715 : : {
716 : : char name[BPF_OBJ_NAME_LEN];
717 : : size_t set_idx = 0;
718 : : int r;
719 : :
720 : : assert(new_prog);
721 : :
722 [ + + + + : 2884 : bf_list_foreach (&new_prog->runtime.chain->sets, set_node) {
+ + ]
723 : : struct bf_set *set = bf_list_node_get_data(set_node);
724 [ + + ]: 568 : _free_bf_map_ struct bf_map *map = NULL;
725 : : _cleanup_free_ uint8_t *values = NULL;
726 : : _cleanup_free_ uint8_t *keys = NULL;
727 : : size_t nelems = bf_list_size(&set->elems);
728 : : size_t idx = 0;
729 : :
730 [ + + ]: 290 : if (!nelems) {
731 : 4 : r = bf_list_add_tail(&new_prog->handle->sets, NULL);
732 [ + - ]: 4 : if (r)
733 : : return r;
734 : : continue;
735 : : }
736 : :
737 : 282 : (void)snprintf(name, BPF_OBJ_NAME_LEN, _BF_SET_MAP_PREFIX "%04x",
738 : 282 : (uint8_t)set_idx++);
739 : 282 : r = bf_map_new_from_set(&map, name, set);
740 [ + - ]: 282 : if (r)
741 : : return r;
742 : :
743 : 282 : values = malloc(nelems);
744 [ - + ]: 282 : if (!values)
745 [ # # ]: 0 : return bf_err_r(-errno, "failed to allocate map values");
746 : :
747 : 282 : keys = malloc(set->elem_size * nelems);
748 [ - + ]: 282 : if (!keys)
749 [ # # ]: 0 : return bf_err_r(errno, "failed to allocate map keys");
750 : :
751 [ + - + + ]: 1512 : bf_list_foreach (&set->elems, elem_node) {
752 : : void *elem = bf_list_node_get_data(elem_node);
753 : :
754 : 615 : memcpy(keys + (idx * set->elem_size), elem, set->elem_size);
755 : 615 : values[idx] = 1;
756 [ + + ]: 615 : ++idx;
757 : : }
758 : :
759 : 282 : r = bf_bpf_map_update_batch(map->fd, keys, values, nelems, BPF_ANY);
760 [ - + ]: 282 : if (r)
761 [ # # ]: 0 : return bf_err_r(r, "failed to add set elements to the map");
762 : :
763 : 282 : r = bf_list_push(&new_prog->handle->sets, (void **)&map);
764 [ + - ]: 282 : if (r)
765 : : return r;
766 : : };
767 : :
768 : 1156 : r = _bf_program_fixup(new_prog, BF_FIXUP_TYPE_SET_MAP_FD);
769 : : if (r)
770 : : return r;
771 : :
772 : : return 0;
773 : : }
774 : :
775 : 1156 : int bf_program_load(struct bf_program *prog)
776 : : {
777 : : _cleanup_free_ char *log_buf = NULL;
778 : : int r;
779 : :
780 : : assert(prog);
781 : :
782 : 1156 : r = _bf_program_load_sets_maps(prog);
783 [ - + ]: 1156 : if (r)
784 [ # # ]: 0 : return bf_err_r(r, "failed to load the sets map");
785 : :
786 : 1156 : r = _bf_program_load_counters_map(prog);
787 [ - + ]: 1156 : if (r)
788 [ # # ]: 0 : return bf_err_r(r, "failed to load the counter map");
789 : :
790 : 1156 : r = _bf_program_load_printer_map(prog);
791 [ - + ]: 1156 : if (r)
792 [ # # ]: 0 : return bf_err_r(r, "failed to load the printer map");
793 : :
794 : 1156 : r = _bf_program_load_log_map(prog);
795 [ - + ]: 1156 : if (r)
796 [ # # ]: 0 : return bf_err_r(r, "failed to load the log map");
797 : :
798 [ - + ]: 1156 : if (bf_ctx_is_verbose(BF_VERBOSE_DEBUG)) {
799 : 0 : log_buf = malloc(_BF_LOG_BUF_SIZE);
800 [ # # ]: 0 : if (!log_buf) {
801 [ # # ]: 0 : return bf_err_r(-ENOMEM,
802 : : "failed to allocate BPF_PROG_LOAD logs buffer");
803 : : }
804 : : }
805 : :
806 [ - + ]: 1156 : if (bf_ctx_is_verbose(BF_VERBOSE_BYTECODE))
807 : 0 : bf_program_dump_bytecode(prog);
808 : :
809 [ + - ]: 2312 : r = bf_bpf_prog_load(prog->handle->prog_name,
810 : 1156 : bf_hook_to_bpf_prog_type(prog->runtime.chain->hook),
811 : 1156 : prog->img, prog->img_size,
812 : 1156 : bf_hook_to_bpf_attach_type(prog->runtime.chain->hook),
813 : : log_buf, log_buf ? _BF_LOG_BUF_SIZE : 0,
814 : 1156 : bf_ctx_token(), &prog->handle->prog_fd);
815 [ - + ]: 1156 : if (r) {
816 [ # # # # ]: 0 : return bf_err_r(r, "failed to load bf_program (%lu bytes):\n%s\nerrno:",
817 : : prog->img_size, log_buf ? log_buf : "<NO LOG BUFFER>");
818 : : }
819 : :
820 : : return r;
821 : : }
822 : :
823 : 0 : int bf_program_get_counter(const struct bf_program *program,
824 : : uint32_t counter_idx, struct bf_counter *counter)
825 : : {
826 : : assert(program);
827 : : assert(counter);
828 : :
829 : 0 : return bf_handle_get_counter(program->handle, counter_idx, counter);
830 : : }
831 : :
832 : 0 : int bf_cgen_set_counters(struct bf_program *program,
833 : : const struct bf_counter *counters)
834 : : {
835 : : (void)program;
836 : : (void)counters;
837 : :
838 : 0 : return -ENOTSUP;
839 : : }
840 : :
841 : 1156 : size_t bf_program_chain_counter_idx(const struct bf_program *program)
842 : : {
843 : 1156 : return bf_list_size(&program->runtime.chain->rules);
844 : : }
845 : :
846 : 3716 : size_t bf_program_error_counter_idx(const struct bf_program *program)
847 : : {
848 : 3716 : return bf_list_size(&program->runtime.chain->rules) + 1;
849 : : }
|