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 <limits.h>
10 : : #include <stdlib.h>
11 : : #include <string.h>
12 : :
13 : : #include "bpfilter/dump.h"
14 : : #include "bpfilter/helper.h"
15 : : #include "bpfilter/hook.h"
16 : : #include "bpfilter/list.h"
17 : : #include "bpfilter/logger.h"
18 : : #include "bpfilter/matcher.h"
19 : : #include "bpfilter/pack.h"
20 : : #include "bpfilter/rule.h"
21 : : #include "bpfilter/set.h"
22 : : #include "bpfilter/verdict.h"
23 : :
24 : : /**
25 : : * @brief Check if a rule references an empty set.
26 : : *
27 : : * @param chain Chain containing the sets list.
28 : : * @param rule Rule to check.
29 : : * @return 0 if no issues, 1 if rule references an empty set (should be
30 : : * disabled), or negative errno if rule references a non-existent set.
31 : : */
32 : 1292 : static int _bf_rule_references_empty_set(const struct bf_chain *chain,
33 : : const struct bf_rule *rule)
34 : : {
35 : : assert(chain);
36 : : assert(rule);
37 : :
38 [ + + + + : 10070 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
39 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
40 : : uint32_t set_index;
41 : : struct bf_set *set;
42 : :
43 [ + + ]: 3754 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
44 : 3497 : continue;
45 : :
46 : 257 : set_index = *(uint32_t *)bf_matcher_payload(matcher);
47 : 257 : set = bf_list_get_at(&chain->sets, set_index);
48 : :
49 [ - + ]: 257 : if (!set) {
50 [ # # ]: 0 : return bf_err_r(-EINVAL, "rule %u references non-existent set",
51 : : rule->index);
52 : : }
53 : :
54 [ + + ]: 257 : if (bf_set_is_empty(set)) {
55 [ + - ]: 11 : bf_warn("rule %u references empty set, rule will be disabled",
56 : : rule->index);
57 : 11 : return 1;
58 : : }
59 : : }
60 : : return 0;
61 : : }
62 : :
63 : : /**
64 : : * @brief Check if a rule has matchers on the same layer but different
65 : : * protocols.
66 : : *
67 : : * A rule matching on e.g. both IPv4 source address and IPv6 destination
68 : : * address can never match a packet (a packet is either IPv4 or IPv6, not
69 : : * both). Same for layer 4 (e.g. TCP vs UDP). Such rules should be disabled.
70 : : *
71 : : * @param rule Rule to check.
72 : : * @return 0 if no conflict, 1 if the rule contains incompatible matchers, or
73 : : * a negative errno value on failure.
74 : : */
75 : 1281 : static int _bf_rule_has_incompatible_matchers(const struct bf_rule *rule)
76 : : {
77 : : uint32_t layer_hdr_id[_BF_MATCHER_LAYER_MAX];
78 : :
79 : : assert(rule);
80 : :
81 [ + + ]: 7686 : for (int i = 0; i < _BF_MATCHER_LAYER_MAX; ++i)
82 : 6405 : layer_hdr_id[i] = UINT32_MAX;
83 : :
84 [ + + + + : 10044 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
85 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
86 : : const struct bf_matcher_meta *meta;
87 : :
88 [ + + ]: 3743 : if (bf_matcher_get_type(matcher) == BF_MATCHER_SET)
89 : 246 : continue;
90 : :
91 : 3497 : meta = bf_matcher_get_meta(bf_matcher_get_type(matcher));
92 [ - + ]: 3497 : if (!meta) {
93 [ # # ]: 0 : return bf_err_r(
94 : : -ENOENT, "missing bf_matcher_meta for %s",
95 : : bf_matcher_type_to_str(bf_matcher_get_type(matcher)));
96 : : }
97 [ + + ]: 3497 : if (meta->layer <= BF_MATCHER_NO_LAYER)
98 : 948 : continue;
99 : :
100 [ + + ]: 2549 : if (layer_hdr_id[meta->layer] == UINT32_MAX) {
101 : 840 : layer_hdr_id[meta->layer] = meta->hdr_id;
102 : 840 : continue;
103 : : }
104 : :
105 [ + + ]: 1709 : if (layer_hdr_id[meta->layer] != meta->hdr_id) {
106 [ + - ]: 2 : bf_warn(
107 : : "rule %u has incompatible matchers on the same layer, rule will be disabled",
108 : : rule->index);
109 : 2 : return 1;
110 : : }
111 : : }
112 : :
113 : : return 0;
114 : : }
115 : :
116 : 1292 : int _bf_chain_check_rule(struct bf_chain *chain, struct bf_rule *rule)
117 : : {
118 : : int r;
119 : :
120 : : assert(rule);
121 : :
122 : 1292 : r = _bf_rule_references_empty_set(chain, rule);
123 [ - + ]: 1292 : if (r < 0)
124 : : return r;
125 : 1292 : rule->disabled = r;
126 : :
127 [ + + ]: 1292 : if (!rule->disabled) {
128 : 1281 : r = _bf_rule_has_incompatible_matchers(rule);
129 [ + - ]: 1281 : if (r < 0)
130 : : return r;
131 : :
132 : 1281 : rule->disabled = r;
133 : : }
134 : :
135 [ + + - + ]: 1375 : if (rule->log &&
136 : 83 : bf_hook_to_flavor(chain->hook) == BF_FLAVOR_CGROUP_SOCK_ADDR) {
137 [ # # ]: 0 : return bf_err_r(-ENOTSUP, "logging is not supported by %s",
138 : : bf_hook_to_str(chain->hook));
139 : : }
140 : :
141 [ + + + + ]: 1292 : if (rule->log && !rule->disabled)
142 : 82 : chain->flags |= BF_FLAG(BF_CHAIN_LOG);
143 : :
144 [ + + + + ]: 1314 : if (bf_rule_mark_is_set(rule) &&
145 [ + + ]: 32 : bf_hook_to_flavor(chain->hook) != BF_FLAVOR_TC &&
146 : 10 : bf_hook_to_flavor(chain->hook) != BF_FLAVOR_CGROUP_SKB) {
147 [ + - ]: 2 : return bf_err_r(-EINVAL, "%s chains can't set packet mark",
148 : : bf_hook_to_str(chain->hook));
149 : : }
150 : :
151 [ + + + + : 10000 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
152 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
153 : : const struct bf_matcher_meta *meta;
154 : :
155 : : // Track if the chain uses IPv6 nexthdr matcher.
156 [ + + ]: 3752 : if (bf_matcher_get_type(matcher) == BF_MATCHER_IP6_NEXTHDR &&
157 [ + - ]: 171 : !rule->disabled)
158 : 171 : chain->flags |= BF_FLAG(BF_CHAIN_STORE_NEXTHDR);
159 : :
160 : : // Ensure the matcher is compatible with the chain's hook.
161 : 3752 : meta = bf_matcher_get_meta(bf_matcher_get_type(matcher));
162 [ - + ]: 3752 : if (!meta) {
163 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher type %d in rule",
164 : : bf_matcher_get_type(matcher));
165 : : }
166 : :
167 [ + + ]: 3752 : if (meta->unsupported_hooks & BF_FLAG(chain->hook)) {
168 [ + - ]: 42 : return bf_err_r(
169 : : -ENOTSUP, "matcher %s is not compatible with %s",
170 : : bf_matcher_type_to_str(bf_matcher_get_type(matcher)),
171 : : bf_hook_to_str(chain->hook));
172 : : }
173 : : }
174 : :
175 : : return 0;
176 : : }
177 : :
178 : 779 : int bf_chain_new(struct bf_chain **chain, const char *name, enum bf_hook hook,
179 : : enum bf_verdict policy, bf_list *sets, bf_list *rules)
180 : : {
181 : 779 : _free_bf_chain_ struct bf_chain *_chain = NULL;
182 : : size_t ridx = 0;
183 : : int r;
184 : :
185 : : assert(chain && name);
186 [ - + ]: 779 : if (hook >= _BF_HOOK_MAX)
187 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown hook type");
188 [ - + ]: 779 : if (policy >= _BF_TERMINAL_VERDICT_MAX)
189 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown policy type");
190 : :
191 : 779 : _chain = malloc(sizeof(*_chain));
192 [ - + ]: 779 : if (!_chain)
193 : : return -ENOMEM;
194 : :
195 : 779 : _chain->name = strdup(name);
196 [ - + ]: 779 : if (!_chain->name)
197 : : return -ENOMEM;
198 : :
199 : 779 : _chain->flags = 0;
200 : 779 : _chain->hook = hook;
201 : 779 : _chain->policy = policy;
202 : :
203 : 779 : _chain->sets = bf_list_default(bf_set_free, bf_set_pack);
204 [ + + ]: 779 : if (sets)
205 : 764 : _chain->sets = bf_list_move(*sets);
206 : :
207 : 779 : _chain->rules = bf_list_default(bf_rule_free, bf_rule_pack);
208 [ + + ]: 779 : if (rules)
209 : 724 : _chain->rules = bf_list_move(*rules);
210 [ + + + + : 4018 : bf_list_foreach (&_chain->rules, rule_node) {
+ + ]
211 : : struct bf_rule *rule = bf_list_node_get_data(rule_node);
212 : :
213 : 1274 : rule->index = ridx++;
214 : 1274 : r = _bf_chain_check_rule(_chain, rule);
215 [ + + ]: 1274 : if (r)
216 : : return r;
217 : : }
218 : :
219 : 735 : *chain = TAKE_PTR(_chain);
220 : :
221 : 735 : return 0;
222 : : }
223 : :
224 : 172 : int bf_chain_new_from_pack(struct bf_chain **chain, bf_rpack_node_t node)
225 : : {
226 : 172 : _free_bf_chain_ struct bf_chain *_chain = NULL;
227 : 172 : _cleanup_free_ char *name = NULL;
228 : : enum bf_hook hook;
229 : : enum bf_verdict policy;
230 : : bf_rpack_node_t array, array_node;
231 : 172 : bf_list rules = bf_list_default(bf_rule_free, bf_rule_pack);
232 : 172 : bf_list sets = bf_list_default(bf_set_free, bf_set_pack);
233 : : int r;
234 : :
235 : 172 : r = bf_rpack_kv_str(node, "name", &name);
236 [ - + ]: 172 : if (r)
237 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.name");
238 : :
239 [ - + - + : 172 : r = bf_rpack_kv_enum(node, "hook", &hook, 0, _BF_HOOK_MAX);
- - ]
240 : : if (r)
241 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.hook");
242 : :
243 [ - + - + : 172 : r = bf_rpack_kv_enum(node, "policy", &policy, 0, _BF_TERMINAL_VERDICT_MAX);
- - ]
244 : : if (r)
245 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.policy");
246 : :
247 : 172 : r = bf_rpack_kv_array(node, "sets", &array);
248 [ - + ]: 172 : if (r)
249 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.sets");
250 [ + + + + : 608 : bf_rpack_array_foreach (array, array_node) {
+ + ]
251 : 264 : _free_bf_set_ struct bf_set *set = NULL;
252 : :
253 [ + - + - : 132 : r = bf_list_emplace(&sets, bf_set_new_from_pack, set, array_node);
- - - - ]
254 : : if (r) {
255 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_set into bf_chain.sets");
256 : : }
257 : : }
258 : :
259 : 172 : r = bf_rpack_kv_array(node, "rules", &array);
260 [ - + ]: 172 : if (r)
261 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.rules");
262 [ + + + + : 1202 : bf_rpack_array_foreach (array, array_node) {
+ + ]
263 : 858 : _free_bf_rule_ struct bf_rule *rule = NULL;
264 : :
265 [ + - + - : 429 : r = bf_list_emplace(&rules, bf_rule_new_from_pack, rule, array_node);
- - - - ]
266 : : if (r) {
267 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_rule into bf_chain.rules");
268 : : }
269 : : }
270 : :
271 : 172 : r = bf_chain_new(&_chain, name, hook, policy, &sets, &rules);
272 [ - + ]: 172 : if (r)
273 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_chain from pack");
274 : :
275 : 172 : *chain = TAKE_PTR(_chain);
276 : :
277 : 172 : return 0;
278 : : }
279 : :
280 : 2468 : void bf_chain_free(struct bf_chain **chain)
281 : : {
282 : : assert(chain);
283 : :
284 [ + + ]: 2468 : if (!*chain)
285 : : return;
286 : :
287 : 779 : bf_list_clean(&(*chain)->sets);
288 : 779 : bf_list_clean(&(*chain)->rules);
289 : 779 : freep((void *)&(*chain)->name);
290 : : freep((void *)chain);
291 : : }
292 : :
293 : 315 : int bf_chain_pack(const struct bf_chain *chain, bf_wpack_t *pack)
294 : : {
295 : : assert(chain);
296 : : assert(pack);
297 : :
298 : 315 : bf_wpack_kv_str(pack, "name", chain->name);
299 : 315 : bf_wpack_kv_enum(pack, "hook", chain->hook);
300 : 315 : bf_wpack_kv_enum(pack, "policy", chain->policy);
301 : :
302 : 315 : bf_wpack_kv_list(pack, "sets", &chain->sets);
303 : 315 : bf_wpack_kv_list(pack, "rules", &chain->rules);
304 : :
305 [ - + ]: 315 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
306 : : }
307 : :
308 : 21 : void bf_chain_dump(const struct bf_chain *chain, prefix_t *prefix)
309 : : {
310 : : assert(chain);
311 : : assert(prefix);
312 : :
313 [ + + ]: 21 : DUMP(prefix, "struct bf_chain at %p", chain);
314 : 21 : bf_dump_prefix_push(prefix);
315 : :
316 [ + + ]: 21 : DUMP(prefix, "name: %s", chain->name);
317 [ + + ]: 21 : DUMP(prefix, "flags: %02x", chain->flags);
318 [ + + ]: 21 : DUMP(prefix, "hook: %s", bf_hook_to_str(chain->hook));
319 [ + + ]: 21 : DUMP(prefix, "policy: %s", bf_verdict_to_str(chain->policy));
320 : :
321 [ + + ]: 21 : DUMP(prefix, "sets: bf_list<bf_set>[%lu]", bf_list_size(&chain->sets));
322 : 21 : bf_dump_prefix_push(prefix);
323 [ + + + + : 56 : bf_list_foreach (&chain->sets, set_node) {
+ + ]
324 [ + + ]: 7 : if (bf_list_is_tail(&chain->sets, set_node))
325 : 3 : bf_dump_prefix_last(prefix);
326 : :
327 : 7 : bf_set_dump(bf_list_node_get_data(set_node), prefix);
328 : : }
329 : 21 : bf_dump_prefix_pop(prefix);
330 : :
331 [ + + ]: 21 : DUMP(bf_dump_prefix_last(prefix), "rules: bf_list<bf_rule>[%lu]",
332 : : bf_list_size(&chain->rules));
333 : 21 : bf_dump_prefix_push(prefix);
334 [ + + + + : 74 : bf_list_foreach (&chain->rules, rule_node) {
+ + ]
335 [ + + ]: 16 : if (bf_list_is_tail(&chain->rules, rule_node))
336 : 9 : bf_dump_prefix_last(prefix);
337 : :
338 : 16 : bf_rule_dump(bf_list_node_get_data(rule_node), prefix);
339 : : }
340 : 21 : bf_dump_prefix_pop(prefix);
341 : :
342 : 21 : bf_dump_prefix_pop(prefix);
343 : 21 : }
344 : :
345 : 18 : int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule)
346 : : {
347 : : int r;
348 : :
349 : : assert(chain);
350 : : assert(rule);
351 : :
352 : 18 : rule->index = bf_list_size(&chain->rules);
353 : 18 : r = _bf_chain_check_rule(chain, rule);
354 [ + - ]: 18 : if (r)
355 : : return r;
356 : :
357 : 18 : return bf_list_add_tail(&chain->rules, rule);
358 : : }
359 : :
360 : 0 : int bf_chain_add_set(struct bf_chain *chain, struct bf_set *set)
361 : : {
362 : : assert(chain && set);
363 : :
364 : 0 : return bf_list_add_tail(&chain->sets, set);
365 : : }
366 : :
367 : 138 : struct bf_set *bf_chain_get_set_for_matcher(const struct bf_chain *chain,
368 : : const struct bf_matcher *matcher)
369 : : {
370 : : assert(chain);
371 : : assert(matcher);
372 : :
373 : : uint32_t set_id;
374 : :
375 [ + + ]: 138 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
376 : : return NULL;
377 : :
378 : 137 : set_id = *(uint32_t *)bf_matcher_payload(matcher);
379 : :
380 : 137 : return bf_list_get_at(&chain->sets, set_id);
381 : : }
382 : :
383 : 20 : struct bf_set *bf_chain_get_set_by_name(struct bf_chain *chain,
384 : : const char *set_name)
385 : : {
386 : : assert(chain);
387 : : assert(set_name);
388 : :
389 [ + - + + : 46 : bf_list_foreach (&chain->sets, set_node) {
+ + ]
390 : : struct bf_set *set = bf_list_node_get_data(set_node);
391 [ + + ]: 22 : if (bf_streq(set->name, set_name))
392 : : return set;
393 : : }
394 : :
395 : : return NULL;
396 : : }
397 : :
398 : 8 : int bf_chain_new_from_copy(struct bf_chain **dest, const struct bf_chain *src)
399 : : {
400 : 8 : _free_bf_wpack_ bf_wpack_t *wpack = NULL;
401 : 8 : _free_bf_rpack_ bf_rpack_t *rpack = NULL;
402 : : const void *data;
403 : : size_t data_len;
404 : : int r;
405 : :
406 : : assert(dest);
407 : : assert(src);
408 : :
409 : : // For now, we do a copy by serializing and deserializing the struct.
410 : : // @todo Implement deep copy to avoid serialization overhead.
411 : 8 : r = bf_wpack_new(&wpack);
412 [ - + ]: 8 : if (r)
413 [ # # ]: 0 : return bf_err_r(r, "failed to create wpack for chain serialization");
414 : :
415 : 8 : r = bf_chain_pack(src, wpack);
416 [ - + ]: 8 : if (r)
417 [ # # ]: 0 : return bf_err_r(r, "failed to serialize chain");
418 : :
419 : 8 : r = bf_wpack_get_data(wpack, &data, &data_len);
420 [ - + ]: 8 : if (r)
421 [ # # ]: 0 : return bf_err_r(r, "failed to get serialized chain data");
422 : :
423 : 8 : r = bf_rpack_new(&rpack, data, data_len);
424 [ - + ]: 8 : if (r)
425 [ # # ]: 0 : return bf_err_r(r, "failed to create rpack for chain deserialization");
426 : :
427 : 8 : r = bf_chain_new_from_pack(dest, bf_rpack_root(rpack));
428 [ - + ]: 8 : if (r)
429 [ # # ]: 0 : return bf_err_r(r, "failed to deserialize chain");
430 : :
431 : : return 0;
432 : : }
|