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/swich.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : :
11 : : #include <errno.h>
12 : : #include <stddef.h>
13 : : #include <stdint.h>
14 : : #include <stdlib.h>
15 : : #include <string.h>
16 : :
17 : : #include <bpfilter/core/list.h>
18 : : #include <bpfilter/helper.h>
19 : : #include <bpfilter/logger.h>
20 : :
21 : : #include "cgen/jmp.h"
22 : : #include "cgen/program.h"
23 : : #include "filter.h"
24 : :
25 : : /// Cleanup attribute for a @ref bf_swich_option variable.
26 : : #define _free_bf_swich_option_ __attribute__((cleanup(_bf_swich_option_free)))
27 : :
28 : : /**
29 : : * @struct bf_swich_option
30 : : *
31 : : * Represent a @c case of the switch: contains the instructions to execute if
32 : : * the switch's register is equal to a specific value.
33 : : */
34 : : struct bf_swich_option
35 : : {
36 : : /// Immediate value to match against the switch's register.
37 : : uint32_t imm;
38 : : /// Jump context used to jump from the comparison to the bytecode to
39 : : /// execute, then to the end of the switch.
40 : : struct bf_jmpctx jmp;
41 : : /// Number of instructions for this option.
42 : : size_t insns_len;
43 : : /// Instructions to execute, as a flexible array member.
44 : : struct bpf_insn insns[];
45 : : };
46 : :
47 : : static void _bf_swich_option_free(struct bf_swich_option **option);
48 : :
49 : : /**
50 : : * Allocate and initialize a new switch option (case).
51 : : *
52 : : * @param option Switch option to allocate and initialize. Can't be NULL.
53 : : * @param imm Immediate value to match against the switch's register.
54 : : * @param insns Instructions to execute if the option matches.
55 : : * @param insns_len Number of instructions in @p insns .
56 : : * @return 0 on success, or negative errno value on failure.
57 : : */
58 : 13320 : static int _bf_swich_option_new(struct bf_swich_option **option, uint32_t imm,
59 : : const struct bpf_insn *insns, size_t insns_len)
60 : : {
61 : : _free_bf_swich_option_ struct bf_swich_option *_option = NULL;
62 : :
63 : : assert(option);
64 : : assert(insns);
65 : :
66 : 13320 : _option = malloc(sizeof(*_option) + (sizeof(struct bpf_insn) * insns_len));
67 [ + - ]: 13320 : if (!_option)
68 : : return -ENOMEM;
69 : :
70 : 13320 : _option->imm = imm;
71 : 13320 : _option->insns_len = insns_len;
72 : 13320 : memcpy(_option->insns, insns, sizeof(struct bpf_insn) * insns_len);
73 : :
74 : 13320 : *option = TAKE_PTR(_option);
75 : :
76 : : return 0;
77 : : }
78 : :
79 : : /**
80 : : * Free a switch option.
81 : : *
82 : : * Free @p option is it points to valid memory. If @p option points to a NULL
83 : : * pointer, nothing is done.
84 : : *
85 : : * @param option Option to free. Can't be NULL.
86 : : */
87 : 9712 : static void _bf_swich_option_free(struct bf_swich_option **option)
88 : : {
89 : : assert(option);
90 : :
91 [ - - - + : 26640 : if (!*option)
- + + - +
- ]
92 : : return;
93 : :
94 : 0 : free(*option);
95 : 13320 : *option = NULL;
96 : : }
97 : :
98 : 3608 : int bf_swich_init(struct bf_swich *swich, struct bf_program *program, int reg)
99 : : {
100 : : assert(swich);
101 : : assert(program);
102 : :
103 : 3608 : swich->program = program;
104 : 3608 : swich->reg = reg;
105 : :
106 : 3608 : bf_list_init(
107 : : &swich->options,
108 : 3608 : (bf_list_ops[]) {{.free = (bf_list_ops_free)_bf_swich_option_free}});
109 : :
110 : 3608 : swich->default_opt = NULL;
111 : :
112 : 3608 : return 0;
113 : : }
114 : :
115 : 3608 : void bf_swich_cleanup(struct bf_swich *swich)
116 : : {
117 : : assert(swich);
118 : :
119 : 3608 : bf_list_clean(&swich->options);
120 : : _bf_swich_option_free(&swich->default_opt);
121 : 3608 : }
122 : :
123 : 9712 : int bf_swich_add_option(struct bf_swich *swich, uint32_t imm,
124 : : const struct bpf_insn *insns, size_t insns_len)
125 : : {
126 : 9712 : _free_bf_swich_option_ struct bf_swich_option *option = NULL;
127 : : int r;
128 : :
129 : : assert(swich);
130 : : assert(insns);
131 : :
132 : 9712 : r = _bf_swich_option_new(&option, imm, insns, insns_len);
133 [ + - ]: 9712 : if (r)
134 : : return r;
135 : :
136 : 9712 : r = bf_list_add_tail(&swich->options, option);
137 [ + - ]: 9712 : if (r)
138 : : return r;
139 : :
140 : 9712 : TAKE_PTR(option);
141 : :
142 : 9712 : return 0;
143 : : }
144 : :
145 : 3608 : int bf_swich_set_default(struct bf_swich *swich, const struct bpf_insn *insns,
146 : : size_t insns_len)
147 : : {
148 : 3608 : _free_bf_swich_option_ struct bf_swich_option *option = NULL;
149 : : int r;
150 : :
151 : : assert(swich);
152 : : assert(insns);
153 : :
154 [ - + ]: 3608 : if (swich->default_opt) {
155 [ # # ]: 0 : bf_warn("default bf_swich option already exists, replacing it");
156 : : _bf_swich_option_free(&swich->default_opt);
157 : : }
158 : :
159 : 3608 : r = _bf_swich_option_new(&option, 0, insns, insns_len);
160 [ + - ]: 3608 : if (r)
161 : : return r;
162 : :
163 : 3608 : swich->default_opt = TAKE_PTR(option);
164 : :
165 : 3608 : return 0;
166 : : }
167 : :
168 : 3608 : int bf_swich_generate(struct bf_swich *swich)
169 : : {
170 : 3608 : struct bf_program *program = swich->program;
171 : :
172 : : // Match an option against the value and jump to the related bytecode
173 [ + - + + ]: 23032 : bf_list_foreach (&swich->options, option_node) {
174 : : struct bf_swich_option *option = bf_list_node_get_data(option_node);
175 : :
176 [ + - + + ]: 9712 : option->jmp = bf_jmpctx_get(
177 : : program, BPF_JMP_IMM(BPF_JEQ, swich->reg, option->imm, 0));
178 : : }
179 : :
180 : : // Insert the default option if any
181 [ + - ]: 3608 : if (swich->default_opt)
182 [ + - ]: 3608 : swich->default_opt->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
183 : :
184 : : // Insert each option's bytecode
185 [ + - + + ]: 23032 : bf_list_foreach (&swich->options, option_node) {
186 : : struct bf_swich_option *option = bf_list_node_get_data(option_node);
187 : :
188 : 9712 : bf_jmpctx_cleanup(&option->jmp);
189 [ + + ]: 19424 : for (size_t i = 0; i < option->insns_len; ++i)
190 [ + - ]: 9712 : EMIT(program, option->insns[i]);
191 [ + - + + ]: 9712 : option->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
192 : : }
193 : :
194 : : // Insert the default instructions
195 [ + - ]: 3608 : if (swich->default_opt) {
196 : 3608 : bf_jmpctx_cleanup(&swich->default_opt->jmp);
197 [ + + ]: 7216 : for (size_t i = 0; i < swich->default_opt->insns_len; ++i)
198 [ + - ]: 3608 : EMIT(program, swich->default_opt->insns[i]);
199 : : }
200 : :
201 [ + - + + : 26640 : bf_list_foreach (&swich->options, option_node) {
+ + ]
202 : : struct bf_swich_option *option = bf_list_node_get_data(option_node);
203 : :
204 : 9712 : bf_jmpctx_cleanup(&option->jmp);
205 : : }
206 : :
207 : : return 0;
208 : : }
|