Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "bpfilter/chain.h"
7 : :
8 : : #include <errno.h>
9 : : #include <stdlib.h>
10 : : #include <string.h>
11 : :
12 : : #include "bpfilter/dump.h"
13 : : #include "bpfilter/helper.h"
14 : : #include "bpfilter/hook.h"
15 : : #include "bpfilter/list.h"
16 : : #include "bpfilter/logger.h"
17 : : #include "bpfilter/pack.h"
18 : : #include "bpfilter/rule.h"
19 : : #include "bpfilter/set.h"
20 : : #include "bpfilter/verdict.h"
21 : :
22 : : /**
23 : : * @brief Check if a rule references an empty set.
24 : : *
25 : : * @param chain Chain containing the sets list.
26 : : * @param rule Rule to check.
27 : : * @return 0 if no issues, 1 if rule references an empty set (should be
28 : : * disabled), or negative errno if rule references a non-existent set.
29 : : */
30 : 1151 : static int _bf_rule_references_empty_set(const struct bf_chain *chain,
31 : : const struct bf_rule *rule)
32 : : {
33 : : assert(chain);
34 : : assert(rule);
35 : :
36 [ + + + + : 9294 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
37 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
38 : : uint32_t set_index;
39 : : struct bf_set *set;
40 : :
41 [ + + ]: 3507 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
42 : 3250 : continue;
43 : :
44 : 257 : set_index = *(uint32_t *)bf_matcher_payload(matcher);
45 : 257 : set = bf_list_get_at(&chain->sets, set_index);
46 : :
47 [ - + ]: 257 : if (!set) {
48 [ # # ]: 0 : return bf_err_r(-EINVAL, "rule %u references non-existent set",
49 : : rule->index);
50 : : }
51 : :
52 [ + + ]: 257 : if (bf_set_is_empty(set)) {
53 [ + - ]: 11 : bf_warn("rule %u references empty set, rule will be disabled",
54 : : rule->index);
55 : 11 : return 1;
56 : : }
57 : : }
58 : : return 0;
59 : : }
60 : :
61 : 1151 : int _bf_chain_check_rule(struct bf_chain *chain, struct bf_rule *rule)
62 : : {
63 : : int r;
64 : :
65 : : assert(rule);
66 : :
67 : 1151 : r = _bf_rule_references_empty_set(chain, rule);
68 [ - + ]: 1151 : if (r < 0)
69 : : return r;
70 : :
71 : 1151 : rule->disabled = r;
72 : :
73 [ + + + + ]: 1151 : if (rule->log && !rule->disabled)
74 : 82 : chain->flags |= BF_FLAG(BF_CHAIN_LOG);
75 : :
76 [ + + ]: 1151 : if (bf_rule_mark_is_set(rule) &&
77 [ + + ]: 22 : (chain->hook == BF_HOOK_XDP || chain->hook == BF_HOOK_NF_PRE_ROUTING ||
78 : : chain->hook == BF_HOOK_NF_POST_ROUTING ||
79 : : chain->hook == BF_HOOK_NF_FORWARD ||
80 : : chain->hook == BF_HOOK_NF_LOCAL_IN ||
81 : : chain->hook == BF_HOOK_NF_LOCAL_OUT)) {
82 [ + - ]: 2 : return bf_err_r(-EINVAL,
83 : : "XDP and Netfilter chains can't set packet mark");
84 : : }
85 : :
86 [ + + + + : 9280 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
87 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
88 : : const struct bf_matcher_meta *meta;
89 : :
90 : : // Track if the chain uses IPv6 nexthdr matcher.
91 [ + + ]: 3505 : if (bf_matcher_get_type(matcher) == BF_MATCHER_IP6_NEXTHDR &&
92 [ + - ]: 170 : !rule->disabled)
93 : 170 : chain->flags |= BF_FLAG(BF_CHAIN_STORE_NEXTHDR);
94 : :
95 : : // Set matchers are compatible with all hooks.
96 [ + + ]: 3505 : if (bf_matcher_get_type(matcher) == BF_MATCHER_SET)
97 : 257 : continue;
98 : :
99 : : // Ensure the matcher is compatible with the chain's hook.
100 : 3248 : meta = bf_matcher_get_meta(bf_matcher_get_type(matcher));
101 [ - + ]: 3248 : if (!meta) {
102 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher type %d in rule",
103 : : bf_matcher_get_type(matcher));
104 : : }
105 : :
106 [ + + ]: 3248 : if (meta->unsupported_hooks & BF_FLAG(chain->hook)) {
107 [ + - ]: 14 : return bf_err_r(
108 : : -ENOTSUP, "matcher %s is not compatible with %s",
109 : : bf_matcher_type_to_str(bf_matcher_get_type(matcher)),
110 : : bf_hook_to_str(chain->hook));
111 : : }
112 : : }
113 : :
114 : : return 0;
115 : : }
116 : :
117 : 666 : int bf_chain_new(struct bf_chain **chain, const char *name, enum bf_hook hook,
118 : : enum bf_verdict policy, bf_list *sets, bf_list *rules)
119 : : {
120 : 666 : _free_bf_chain_ struct bf_chain *_chain = NULL;
121 : : size_t ridx = 0;
122 : : int r;
123 : :
124 : : assert(chain && name);
125 [ - + ]: 666 : if (hook >= _BF_HOOK_MAX)
126 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown hook type");
127 [ - + ]: 666 : if (policy >= _BF_TERMINAL_VERDICT_MAX)
128 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown policy type");
129 : :
130 : 666 : _chain = malloc(sizeof(*_chain));
131 [ - + ]: 666 : if (!_chain)
132 : : return -ENOMEM;
133 : :
134 : 666 : _chain->name = strdup(name);
135 [ - + ]: 666 : if (!_chain->name)
136 : : return -ENOMEM;
137 : :
138 : 666 : _chain->flags = 0;
139 : 666 : _chain->hook = hook;
140 : 666 : _chain->policy = policy;
141 : :
142 : 666 : _chain->sets = bf_list_default(bf_set_free, bf_set_pack);
143 [ + + ]: 666 : if (sets)
144 : 655 : _chain->sets = bf_list_move(*sets);
145 : :
146 : 666 : _chain->rules = bf_list_default(bf_rule_free, bf_rule_pack);
147 [ + + ]: 666 : if (rules)
148 : 612 : _chain->rules = bf_list_move(*rules);
149 [ + + + + : 3566 : bf_list_foreach (&_chain->rules, rule_node) {
+ + ]
150 : : struct bf_rule *rule = bf_list_node_get_data(rule_node);
151 : :
152 : 1133 : rule->index = ridx++;
153 : 1133 : r = _bf_chain_check_rule(_chain, rule);
154 [ + + ]: 1133 : if (r)
155 : : return r;
156 : : }
157 : :
158 : 650 : *chain = TAKE_PTR(_chain);
159 : :
160 : 650 : return 0;
161 : : }
162 : :
163 : 147 : int bf_chain_new_from_pack(struct bf_chain **chain, bf_rpack_node_t node)
164 : : {
165 : 147 : _free_bf_chain_ struct bf_chain *_chain = NULL;
166 : 147 : _cleanup_free_ char *name = NULL;
167 : : enum bf_hook hook;
168 : : enum bf_verdict policy;
169 : : bf_rpack_node_t array, array_node;
170 : 147 : bf_list rules = bf_list_default(bf_rule_free, bf_rule_pack);
171 : 147 : bf_list sets = bf_list_default(bf_set_free, bf_set_pack);
172 : : int r;
173 : :
174 : 147 : r = bf_rpack_kv_str(node, "name", &name);
175 [ - + ]: 147 : if (r)
176 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.name");
177 : :
178 [ - + - + : 147 : r = bf_rpack_kv_enum(node, "hook", &hook, 0, _BF_HOOK_MAX);
- - ]
179 : : if (r)
180 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.hook");
181 : :
182 [ - + - + : 147 : r = bf_rpack_kv_enum(node, "policy", &policy, 0, _BF_TERMINAL_VERDICT_MAX);
- - ]
183 : : if (r)
184 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.policy");
185 : :
186 : 147 : r = bf_rpack_kv_array(node, "sets", &array);
187 [ - + ]: 147 : if (r)
188 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.sets");
189 [ + + + + : 558 : bf_rpack_array_foreach (array, array_node) {
+ + ]
190 : 264 : _free_bf_set_ struct bf_set *set = NULL;
191 : :
192 [ + - + - : 132 : r = bf_list_emplace(&sets, bf_set_new_from_pack, set, array_node);
- - - - ]
193 : : if (r) {
194 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_set into bf_chain.sets");
195 : : }
196 : : }
197 : :
198 : 147 : r = bf_rpack_kv_array(node, "rules", &array);
199 [ - + ]: 147 : if (r)
200 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.rules");
201 [ + + + + : 1074 : bf_rpack_array_foreach (array, array_node) {
+ + ]
202 : 780 : _free_bf_rule_ struct bf_rule *rule = NULL;
203 : :
204 [ + - + - : 390 : r = bf_list_emplace(&rules, bf_rule_new_from_pack, rule, array_node);
- - - - ]
205 : : if (r) {
206 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_rule into bf_chain.rules");
207 : : }
208 : : }
209 : :
210 : 147 : r = bf_chain_new(&_chain, name, hook, policy, &sets, &rules);
211 [ - + ]: 147 : if (r)
212 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_chain from pack");
213 : :
214 : 147 : *chain = TAKE_PTR(_chain);
215 : :
216 : 147 : return 0;
217 : : }
218 : :
219 : 2134 : void bf_chain_free(struct bf_chain **chain)
220 : : {
221 : : assert(chain);
222 : :
223 [ + + ]: 2134 : if (!*chain)
224 : : return;
225 : :
226 : 666 : bf_list_clean(&(*chain)->sets);
227 : 666 : bf_list_clean(&(*chain)->rules);
228 : 666 : freep((void *)&(*chain)->name);
229 : : freep((void *)chain);
230 : : }
231 : :
232 : 352 : int bf_chain_pack(const struct bf_chain *chain, bf_wpack_t *pack)
233 : : {
234 : : assert(chain);
235 : : assert(pack);
236 : :
237 : 352 : bf_wpack_kv_str(pack, "name", chain->name);
238 : 352 : bf_wpack_kv_enum(pack, "hook", chain->hook);
239 : 352 : bf_wpack_kv_enum(pack, "policy", chain->policy);
240 : :
241 : 352 : bf_wpack_kv_list(pack, "sets", &chain->sets);
242 : 352 : bf_wpack_kv_list(pack, "rules", &chain->rules);
243 : :
244 [ - + ]: 352 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
245 : : }
246 : :
247 : 19 : void bf_chain_dump(const struct bf_chain *chain, prefix_t *prefix)
248 : : {
249 : : assert(chain);
250 : : assert(prefix);
251 : :
252 [ + + ]: 19 : DUMP(prefix, "struct bf_chain at %p", chain);
253 : 19 : bf_dump_prefix_push(prefix);
254 : :
255 [ + + ]: 19 : DUMP(prefix, "name: %s", chain->name);
256 [ + + ]: 19 : DUMP(prefix, "flags: %02x", chain->flags);
257 [ + + ]: 19 : DUMP(prefix, "hook: %s", bf_hook_to_str(chain->hook));
258 [ + + ]: 19 : DUMP(prefix, "policy: %s", bf_verdict_to_str(chain->policy));
259 : :
260 [ + + ]: 19 : DUMP(prefix, "sets: bf_list<bf_set>[%lu]", bf_list_size(&chain->sets));
261 : 19 : bf_dump_prefix_push(prefix);
262 [ + + + + : 52 : bf_list_foreach (&chain->sets, set_node) {
+ + ]
263 [ + + ]: 7 : if (bf_list_is_tail(&chain->sets, set_node))
264 : 3 : bf_dump_prefix_last(prefix);
265 : :
266 : 7 : bf_set_dump(bf_list_node_get_data(set_node), prefix);
267 : : }
268 : 19 : bf_dump_prefix_pop(prefix);
269 : :
270 [ + + ]: 19 : DUMP(bf_dump_prefix_last(prefix), "rules: bf_list<bf_rule>[%lu]",
271 : : bf_list_size(&chain->rules));
272 : 19 : bf_dump_prefix_push(prefix);
273 [ + + + + : 68 : bf_list_foreach (&chain->rules, rule_node) {
+ + ]
274 [ + + ]: 15 : if (bf_list_is_tail(&chain->rules, rule_node))
275 : 8 : bf_dump_prefix_last(prefix);
276 : :
277 : 15 : bf_rule_dump(bf_list_node_get_data(rule_node), prefix);
278 : : }
279 : 19 : bf_dump_prefix_pop(prefix);
280 : :
281 : 19 : bf_dump_prefix_pop(prefix);
282 : 19 : }
283 : :
284 : 18 : int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule)
285 : : {
286 : : int r;
287 : :
288 : : assert(chain);
289 : : assert(rule);
290 : :
291 : 18 : rule->index = bf_list_size(&chain->rules);
292 : 18 : r = _bf_chain_check_rule(chain, rule);
293 [ + - ]: 18 : if (r)
294 : : return r;
295 : :
296 : 18 : return bf_list_add_tail(&chain->rules, rule);
297 : : }
298 : :
299 : 0 : int bf_chain_add_set(struct bf_chain *chain, struct bf_set *set)
300 : : {
301 : : assert(chain && set);
302 : :
303 : 0 : return bf_list_add_tail(&chain->sets, set);
304 : : }
305 : :
306 : 138 : struct bf_set *bf_chain_get_set_for_matcher(const struct bf_chain *chain,
307 : : const struct bf_matcher *matcher)
308 : : {
309 : : assert(chain);
310 : : assert(matcher);
311 : :
312 : : uint32_t set_id;
313 : :
314 [ + + ]: 138 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
315 : : return NULL;
316 : :
317 : 137 : set_id = *(uint32_t *)bf_matcher_payload(matcher);
318 : :
319 : 137 : return bf_list_get_at(&chain->sets, set_id);
320 : : }
321 : :
322 : 20 : struct bf_set *bf_chain_get_set_by_name(struct bf_chain *chain,
323 : : const char *set_name)
324 : : {
325 : : assert(chain);
326 : : assert(set_name);
327 : :
328 [ + - + + : 46 : bf_list_foreach (&chain->sets, set_node) {
+ + ]
329 : : struct bf_set *set = bf_list_node_get_data(set_node);
330 [ + + ]: 22 : if (bf_streq(set->name, set_name))
331 : : return set;
332 : : }
333 : :
334 : : return NULL;
335 : : }
336 : :
337 : 8 : int bf_chain_new_from_copy(struct bf_chain **dest, const struct bf_chain *src)
338 : : {
339 : 8 : _free_bf_wpack_ bf_wpack_t *wpack = NULL;
340 : 8 : _free_bf_rpack_ bf_rpack_t *rpack = NULL;
341 : : const void *data;
342 : : size_t data_len;
343 : : int r;
344 : :
345 : : assert(dest);
346 : : assert(src);
347 : :
348 : : // For now, we do a copy by serializing and deserializing the struct.
349 : : // @todo Implement deep copy to avoid serialization overhead.
350 : 8 : r = bf_wpack_new(&wpack);
351 [ - + ]: 8 : if (r)
352 [ # # ]: 0 : return bf_err_r(r, "failed to create wpack for chain serialization");
353 : :
354 : 8 : r = bf_chain_pack(src, wpack);
355 [ - + ]: 8 : if (r)
356 [ # # ]: 0 : return bf_err_r(r, "failed to serialize chain");
357 : :
358 : 8 : r = bf_wpack_get_data(wpack, &data, &data_len);
359 [ - + ]: 8 : if (r)
360 [ # # ]: 0 : return bf_err_r(r, "failed to get serialized chain data");
361 : :
362 : 8 : r = bf_rpack_new(&rpack, data, data_len);
363 [ - + ]: 8 : if (r)
364 [ # # ]: 0 : return bf_err_r(r, "failed to create rpack for chain deserialization");
365 : :
366 : 8 : r = bf_chain_new_from_pack(dest, bf_rpack_root(rpack));
367 [ - + ]: 8 : if (r)
368 [ # # ]: 0 : return bf_err_r(r, "failed to deserialize chain");
369 : :
370 : : return 0;
371 : : }
|