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