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