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/core/hashset.h"
14 : : #include "bpfilter/core/list.h"
15 : : #include "bpfilter/dump.h"
16 : : #include "bpfilter/helper.h"
17 : : #include "bpfilter/hook.h"
18 : : #include "bpfilter/logger.h"
19 : : #include "bpfilter/matcher.h"
20 : : #include "bpfilter/pack.h"
21 : : #include "bpfilter/rule.h"
22 : : #include "bpfilter/set.h"
23 : : #include "bpfilter/verdict.h"
24 : :
25 : : /**
26 : : * @brief Check if a rule can't match because it references an unmatchable set.
27 : : *
28 : : * Check if a rule contains a matcher on a set that can't match any packet:
29 : : * - `... in {}`: the reference value can't be found in the set, because the
30 : : * set is empty. The rule will never match.
31 : : * - `... not in {}`: the set is empty, but we test for the negative: the
32 : : * reference value won't be in the set. The rule can match.
33 : : *
34 : : * @param chain Chain containing the sets. Can't be NULL.
35 : : * @param rule Rule to check. Can't be NULL.
36 : : * @return 0 if the rule has no set matcher, or a set matcher that might match
37 : : * a packet. 1 if the rule contains a set matcher that can't match any
38 : : * packet. A negative errno value on failure.
39 : : */
40 : 9967 : static int _bf_rule_has_unmatchable_set_matcher(const struct bf_chain *chain,
41 : : const struct bf_rule *rule)
42 : : {
43 : : assert(chain);
44 : : assert(rule);
45 : :
46 [ + + + + : 44960 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
47 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
48 : : const struct bf_set *set;
49 : :
50 [ + + ]: 12552 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
51 : 10376 : continue;
52 : :
53 : 2176 : set = bf_chain_get_set_for_matcher(chain, matcher);
54 [ + + ]: 2176 : if (!set) {
55 [ + - ]: 2 : return bf_err_r(-EINVAL, "rule %u references non-existent set",
56 : : rule->index);
57 : : }
58 : :
59 [ + + + + ]: 2174 : if (bf_set_is_empty(set) && !bf_matcher_get_negate(matcher)) {
60 [ + - ]: 37 : bf_warn("rule %u references empty set, rule will be disabled",
61 : : rule->index);
62 : : return 1;
63 : : }
64 : : }
65 : : return 0;
66 : : }
67 : :
68 : : /**
69 : : * @brief Check a single matcher type against per-layer header tracking.
70 : : *
71 : : * @param type Matcher type to check.
72 : : * @param layer_hdr_id Per-layer header ID tracking array.
73 : : * @return 0 if compatible, 1 if incompatible, negative errno on error.
74 : : */
75 : 12810 : static int _bf_rule_check_layer(enum bf_matcher_type type,
76 : : uint32_t layer_hdr_id[_BF_MATCHER_LAYER_MAX])
77 : : {
78 : : const struct bf_matcher_meta *meta;
79 : :
80 : : assert(layer_hdr_id);
81 : :
82 : 12810 : meta = bf_matcher_get_meta(type);
83 [ - + ]: 12810 : if (!meta) {
84 [ # # ]: 0 : return bf_err_r(-EINVAL, "missing meta for '%s'",
85 : : bf_matcher_type_to_str(type));
86 : : }
87 : :
88 [ + + ]: 12810 : if (meta->layer <= BF_MATCHER_NO_LAYER)
89 : : return 0;
90 : :
91 [ + + ]: 9781 : if (layer_hdr_id[meta->layer] == UINT32_MAX) {
92 : 7781 : layer_hdr_id[meta->layer] = meta->hdr_id;
93 : 7781 : return 0;
94 : : }
95 : :
96 [ + + ]: 2000 : if (layer_hdr_id[meta->layer] != meta->hdr_id)
97 : 24 : return 1;
98 : :
99 : : return 0;
100 : : }
101 : :
102 : : /**
103 : : * @brief Check if a rule has matchers on the same layer but different
104 : : * protocols.
105 : : *
106 : : * A rule matching on e.g. both IPv4 source address and IPv6 destination
107 : : * address can never match a packet (a packet is either IPv4 or IPv6, not
108 : : * both). Same for layer 4 (e.g. TCP vs UDP). Such rules should be disabled.
109 : : *
110 : : * @param chain Chain containing the sets.
111 : : * @param rule Rule to check.
112 : : * @return 0 if no conflict, 1 if the rule contains incompatible matchers, or
113 : : * a negative errno value on failure.
114 : : */
115 : 9928 : static int _bf_rule_has_incompatible_matchers(const struct bf_chain *chain,
116 : : const struct bf_rule *rule)
117 : : {
118 : : uint32_t layer_hdr_id[_BF_MATCHER_LAYER_MAX];
119 : :
120 : : assert(chain);
121 : : assert(rule);
122 : :
123 [ + + ]: 59568 : for (int i = 0; i < _BF_MATCHER_LAYER_MAX; ++i)
124 : 49640 : layer_hdr_id[i] = UINT32_MAX;
125 : :
126 [ + + + + : 44752 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
127 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
128 : : int r = 0;
129 : :
130 [ + + ]: 12472 : if (bf_matcher_get_type(matcher) == BF_MATCHER_SET) {
131 : : const struct bf_set *set =
132 : 2109 : bf_chain_get_set_for_matcher(chain, matcher);
133 : :
134 [ - + ]: 2109 : if (!set) {
135 [ # # ]: 0 : return bf_err_r(-ENOENT, "rule %u references non-existent set",
136 : : rule->index);
137 : : }
138 : :
139 [ + + + - ]: 4556 : for (size_t i = 0; i < set->n_comps && !r; ++i)
140 : 2447 : r = _bf_rule_check_layer(set->key[i], layer_hdr_id);
141 : : } else {
142 : 10363 : r = _bf_rule_check_layer(bf_matcher_get_type(matcher),
143 : : layer_hdr_id);
144 : : }
145 : :
146 [ + - ]: 12472 : if (r < 0)
147 : : return r;
148 [ + + ]: 12472 : if (r) {
149 [ + - ]: 24 : bf_warn(
150 : : "rule %u has incompatible matchers on the same layer, rule will be disabled",
151 : : rule->index);
152 : : return 1;
153 : : }
154 : : }
155 : :
156 : : return 0;
157 : : }
158 : :
159 : 9967 : static int _bf_chain_check_rule(struct bf_chain *chain, struct bf_rule *rule)
160 : : {
161 : : const uint8_t tuple = BF_FLAG(BF_LOG_OPT_5_TUPLE);
162 : : int r;
163 : :
164 : : assert(rule);
165 : :
166 : 9967 : r = _bf_rule_has_unmatchable_set_matcher(chain, rule);
167 [ + + ]: 9967 : if (r < 0)
168 : : return r;
169 : 9965 : rule->disabled = r;
170 : :
171 [ + + ]: 9965 : if (!rule->disabled) {
172 : 9928 : r = _bf_rule_has_incompatible_matchers(chain, rule);
173 [ + - ]: 9928 : if (r < 0)
174 : : return r;
175 : :
176 : 9928 : rule->disabled = r;
177 : : }
178 : :
179 [ + + + + : 9965 : if (rule->log != BF_LOG_OPT_DEFAULT && (rule->log & tuple) &&
+ + ]
180 : : rule->log != tuple) {
181 [ + - ]: 3 : return bf_err_r(
182 : : -EINVAL,
183 : : "5-tuple logging can't be combined with packet layer options");
184 : : }
185 : :
186 [ + + + + ]: 10114 : if (rule->log && rule->log != BF_LOG_OPT_DEFAULT &&
187 : 152 : bf_hook_to_flavor(chain->hook) == BF_FLAVOR_CGROUP_SOCK_ADDR) {
188 [ + - ]: 6 : return bf_err_r(
189 : : -ENOTSUP,
190 : : "per-field log options are not supported by %s, use 'log'",
191 : : bf_hook_to_str(chain->hook));
192 : : }
193 : :
194 [ + + + + ]: 9956 : if (rule->log && !rule->disabled)
195 : 200 : chain->flags |= BF_FLAG(BF_CHAIN_LOG);
196 : :
197 [ + + + + : 9956 : if (rule->log && rule->log_rate_ns && !rule->disabled)
+ + ]
198 : 54 : chain->flags |= BF_FLAG(BF_CHAIN_LOG_RATELIMIT);
199 : :
200 [ + + + + ]: 9981 : if (bf_rule_mark_is_set(rule) &&
201 [ + + ]: 36 : bf_hook_to_flavor(chain->hook) != BF_FLAVOR_TC &&
202 : 11 : bf_hook_to_flavor(chain->hook) != BF_FLAVOR_CGROUP_SKB) {
203 [ + - ]: 3 : return bf_err_r(-EINVAL, "%s chains can't set packet mark",
204 : : bf_hook_to_str(chain->hook));
205 : : }
206 : :
207 [ + + + + : 44822 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
208 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
209 : : const struct bf_matcher_meta *meta;
210 : :
211 : : // Track if the chain uses IPv6 nexthdr matcher.
212 [ + + ]: 12548 : if (bf_matcher_get_type(matcher) == BF_MATCHER_IP6_NEXTHDR &&
213 [ + + ]: 305 : !rule->disabled)
214 : 303 : chain->flags |= BF_FLAG(BF_CHAIN_STORE_NEXTHDR);
215 : :
216 : : // Ensure the matcher is compatible with the chain's hook.
217 : 12548 : meta = bf_matcher_get_meta(bf_matcher_get_type(matcher));
218 [ - + ]: 12548 : if (!meta) {
219 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher type %d in rule",
220 : : bf_matcher_get_type(matcher));
221 : : }
222 : :
223 [ + + ]: 12548 : if (meta->unsupported_hooks & BF_FLAG(chain->hook)) {
224 [ + - ]: 76 : return bf_err_r(
225 : : -ENOTSUP, "matcher %s is not compatible with %s",
226 : : bf_matcher_type_to_str(bf_matcher_get_type(matcher)),
227 : : bf_hook_to_str(chain->hook));
228 : : }
229 : :
230 [ + + ]: 12472 : if (bf_matcher_get_type(matcher) == BF_MATCHER_SET) {
231 : : const struct bf_set *set =
232 : 2178 : bf_chain_get_set_for_matcher(chain, matcher);
233 : :
234 [ - + ]: 2178 : if (!set) {
235 [ # # ]: 0 : return bf_err_r(-ENOENT, "rule %u references non-existent set",
236 : : rule->index);
237 : : }
238 : :
239 [ + + ]: 4681 : for (size_t i = 0; i < set->n_comps; ++i) {
240 : : const struct bf_matcher_meta *comp_meta =
241 : 2517 : bf_matcher_get_meta(set->key[i]);
242 : :
243 [ - + ]: 2517 : if (!comp_meta) {
244 [ # # ]: 0 : return bf_err_r(-EINVAL,
245 : : "missing meta for set component '%s'",
246 : : bf_matcher_type_to_str(set->key[i]));
247 : : }
248 : :
249 [ + + ]: 2517 : if (comp_meta->unsupported_hooks & BF_FLAG(chain->hook)) {
250 [ + - ]: 14 : return bf_err_r(-ENOTSUP,
251 : : "set component '%s' not compatible with %s",
252 : : bf_matcher_type_to_str(set->key[i]),
253 : : bf_hook_to_str(chain->hook));
254 : : }
255 : : }
256 : : }
257 : : }
258 : :
259 : : return 0;
260 : : }
261 : :
262 : 9443 : static int _bf_chain_validate_rules(struct bf_chain *chain)
263 : : {
264 : : int r;
265 : :
266 : : assert(chain);
267 : :
268 : 9443 : chain->flags = 0;
269 : :
270 [ + + + + : 32562 : bf_list_foreach (&chain->rules, rule_node) {
+ + ]
271 : : struct bf_rule *rule = bf_list_node_get_data(rule_node);
272 : :
273 : 6940 : r = _bf_chain_check_rule(chain, rule);
274 [ + + ]: 6940 : if (r)
275 : : return r;
276 : : }
277 : :
278 : : return 0;
279 : : }
280 : :
281 : 9425 : int bf_chain_new(struct bf_chain **chain, const char *name, enum bf_hook hook,
282 : : enum bf_verdict policy, bf_hashset *sets, bf_list *rules)
283 : : {
284 : 9425 : _free_bf_chain_ struct bf_chain *_chain = NULL;
285 : : size_t ridx = 0;
286 : : int r;
287 : :
288 : : assert(chain && name);
289 [ - + ]: 9425 : if (hook >= _BF_HOOK_MAX)
290 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown hook type");
291 [ + + ]: 9425 : if (!bf_verdict_is_valid_policy(policy))
292 [ + - ]: 2 : return bf_err_r(-EINVAL, "invalid policy '%s'",
293 : : bf_verdict_to_str(policy));
294 : :
295 [ + + ]: 9423 : if (sets) {
296 [ + + + + : 13730 : bf_hashset_foreach (sets, set_node) {
+ + ]
297 : 183 : const struct bf_set *set = set_node->data;
298 : :
299 [ - + ]: 183 : if (!bf_set_is_valid_name(set->name))
300 [ # # ]: 0 : return bf_err_r(-EINVAL, "chain sets must have valid names");
301 : : }
302 : : }
303 : :
304 : 9423 : _chain = calloc(1, sizeof(*_chain));
305 [ + - ]: 9423 : if (!_chain)
306 : : return -ENOMEM;
307 : :
308 : 9423 : _chain->name = strdup(name);
309 [ + - ]: 9423 : if (!_chain->name)
310 : : return -ENOMEM;
311 : :
312 : 9423 : _chain->hook = hook;
313 : 9423 : _chain->policy = policy;
314 : :
315 : 9423 : _chain->sets = bf_set_hashset_default();
316 [ + + ]: 9423 : if (sets)
317 : 6682 : _chain->sets = bf_hashset_move(sets);
318 : :
319 : 9423 : _chain->rules = bf_list_default(bf_rule_free, bf_rule_pack);
320 [ + + ]: 9423 : if (rules)
321 : 6639 : _chain->rules = bf_list_move(rules);
322 [ + + + + ]: 26098 : bf_list_foreach (&_chain->rules, rule_node) {
323 : : struct bf_rule *rule = bf_list_node_get_data(rule_node);
324 : :
325 [ + + ]: 6918 : rule->index = ridx++;
326 : : }
327 : :
328 : 9423 : r = _bf_chain_validate_rules(_chain);
329 [ + + ]: 9423 : if (r) {
330 [ + - ]: 102 : if (rules)
331 : 102 : *rules = bf_list_move(&_chain->rules);
332 [ + + ]: 102 : if (sets)
333 : 99 : *sets = bf_hashset_move(&_chain->sets);
334 : : return r;
335 : : }
336 : :
337 : 9321 : *chain = TAKE_PTR(_chain);
338 : :
339 : 9321 : return 0;
340 : : }
341 : :
342 : 5805 : int bf_chain_new_from_pack(struct bf_chain **chain, bf_rpack_node_t node)
343 : : {
344 : 5805 : _free_bf_chain_ struct bf_chain *_chain = NULL;
345 : 5805 : _cleanup_free_ char *name = NULL;
346 : : enum bf_hook hook;
347 : : enum bf_verdict policy;
348 : : bf_rpack_node_t array, array_node;
349 : 11610 : _clean_bf_hashset_ bf_hashset sets = bf_set_hashset_default();
350 : 5805 : _clean_bf_list_ bf_list rules = bf_list_default(bf_rule_free, bf_rule_pack);
351 : : int r;
352 : :
353 : 5805 : r = bf_rpack_kv_str(node, "name", &name);
354 [ - + ]: 5805 : if (r)
355 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.name");
356 : :
357 [ - + - + : 5805 : r = bf_rpack_kv_enum(node, "hook", &hook, 0, _BF_HOOK_MAX);
- - ]
358 : : if (r)
359 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.hook");
360 : :
361 [ - + - + : 5805 : r = bf_rpack_kv_enum(node, "policy", &policy, 0, _BF_VERDICT_MAX);
- - ]
362 : : if (r)
363 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.policy");
364 : :
365 : 5805 : r = bf_rpack_kv_array(node, "sets", &array);
366 [ - + ]: 5805 : if (r)
367 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.sets");
368 [ + + + + : 6021 : bf_rpack_array_foreach (array, array_node) {
+ + ]
369 : 109 : _free_bf_set_ struct bf_set *set = NULL;
370 : :
371 : 109 : r = bf_set_new_from_pack(&set, array_node);
372 [ - + ]: 109 : if (r)
373 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_set into bf_chain.sets");
374 : :
375 [ - + ]: 109 : if (bf_set_is_anonymous(set))
376 [ # # ]: 0 : return bf_err_r(-EINVAL, "anonymous set outside a matcher");
377 : :
378 : 109 : r = bf_hashset_add(&sets, (void **)&set);
379 [ + + ]: 109 : if (r)
380 [ + - ]: 1 : return bf_err_r(r, "failed to add unpacked set to chain");
381 : : }
382 : :
383 : 5804 : r = bf_rpack_kv_array(node, "rules", &array);
384 [ - + ]: 5804 : if (r)
385 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.rules");
386 [ + + + + : 17362 : bf_rpack_array_foreach (array, array_node) {
+ + ]
387 : 11558 : _free_bf_rule_ struct bf_rule *rule = NULL;
388 : :
389 [ + - + - : 5779 : r = bf_list_emplace(&rules, bf_rule_new_from_pack, rule, array_node,
- - - - ]
390 : : &sets);
391 : : if (r) {
392 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_rule into bf_chain.rules");
393 : : }
394 : : }
395 : :
396 : 5804 : r = bf_chain_new(&_chain, name, hook, policy, &sets, &rules);
397 [ - + ]: 5804 : if (r)
398 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_chain from pack");
399 : :
400 : 5804 : *chain = TAKE_PTR(_chain);
401 : :
402 : 5804 : return 0;
403 : : }
404 : :
405 : 30910 : void bf_chain_free(struct bf_chain **chain)
406 : : {
407 : : assert(chain);
408 : :
409 [ + + ]: 30910 : if (!*chain)
410 : : return;
411 : :
412 : 9423 : bf_list_clean(&(*chain)->rules);
413 : 9423 : bf_hashset_clean(&(*chain)->sets);
414 : 9423 : BF_FREEP(&(*chain)->name);
415 : 9423 : BF_FREEP(chain);
416 : : }
417 : :
418 : 1494 : int bf_chain_pack(const struct bf_chain *chain, bf_wpack_t *pack)
419 : : {
420 : : int r;
421 : :
422 : : assert(chain);
423 : : assert(pack);
424 : :
425 : 1494 : bf_wpack_kv_str(pack, "name", chain->name);
426 : 1494 : bf_wpack_kv_enum(pack, "hook", chain->hook);
427 : 1494 : bf_wpack_kv_enum(pack, "policy", chain->policy);
428 : :
429 : 1494 : bf_wpack_open_array(pack, "sets");
430 [ + + + + : 3194 : bf_hashset_foreach (&chain->sets, set_node) {
+ + ]
431 : 103 : bf_wpack_open_object(pack, NULL);
432 : 103 : r = bf_set_pack(set_node->data, pack);
433 [ + - ]: 103 : if (r)
434 : : return r;
435 : 103 : bf_wpack_close_object(pack);
436 : : }
437 : 1494 : bf_wpack_close_array(pack);
438 : 1494 : bf_wpack_kv_list(pack, "rules", &chain->rules);
439 : :
440 [ - + ]: 1494 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
441 : : }
442 : :
443 : 2 : void bf_chain_dump(const struct bf_chain *chain, prefix_t *prefix)
444 : : {
445 : : assert(chain);
446 : : assert(prefix);
447 : :
448 [ - + ]: 2 : DUMP(prefix, "struct bf_chain at %p", chain);
449 : 2 : bf_dump_prefix_push(prefix);
450 : :
451 [ - + ]: 2 : DUMP(prefix, "name: %s", chain->name);
452 [ - + ]: 2 : DUMP(prefix, "flags: %02x", chain->flags);
453 [ - + ]: 2 : DUMP(prefix, "hook: %s", bf_hook_to_str(chain->hook));
454 [ - + ]: 2 : DUMP(prefix, "policy: %s", bf_verdict_to_str(chain->policy));
455 : :
456 [ - + ]: 2 : DUMP(prefix, "sets: bf_hashset<bf_set>[%lu]",
457 : : bf_hashset_size(&chain->sets));
458 : 2 : bf_dump_prefix_push(prefix);
459 [ + + + + : 10 : bf_hashset_foreach (&chain->sets, set_node) {
+ + ]
460 [ + + ]: 3 : if (!set_node->next)
461 : 1 : bf_dump_prefix_last(prefix);
462 : :
463 : 3 : bf_set_dump(set_node->data, prefix);
464 : : }
465 : 2 : bf_dump_prefix_pop(prefix);
466 : :
467 [ - + ]: 2 : DUMP(bf_dump_prefix_last(prefix), "rules: bf_list<bf_rule>[%lu]",
468 : : bf_list_size(&chain->rules));
469 : 2 : bf_dump_prefix_push(prefix);
470 [ + + + + : 16 : bf_list_foreach (&chain->rules, rule_node) {
+ + ]
471 [ + + ]: 6 : if (bf_list_is_tail(&chain->rules, rule_node))
472 : 1 : bf_dump_prefix_last(prefix);
473 : :
474 : 6 : bf_rule_dump(bf_list_node_get_data(rule_node), prefix);
475 : : }
476 : 2 : bf_dump_prefix_pop(prefix);
477 : :
478 [ - + ]: 2 : DUMP(prefix, "policy_counters: struct bf_counter");
479 : 2 : bf_dump_prefix_push(prefix);
480 [ - + ]: 2 : DUMP(prefix, "count: %lu", chain->policy_counters.count);
481 [ - + ]: 2 : DUMP(prefix, "size: %lu", chain->policy_counters.size);
482 : 2 : bf_dump_prefix_pop(prefix);
483 : :
484 [ - + ]: 2 : DUMP(prefix, "error_counters: struct bf_counter");
485 : 2 : bf_dump_prefix_push(prefix);
486 [ - + ]: 2 : DUMP(prefix, "count: %lu", chain->error_counters.count);
487 [ - + ]: 2 : DUMP(prefix, "size: %lu", chain->error_counters.size);
488 : 2 : bf_dump_prefix_pop(prefix);
489 : :
490 : 2 : bf_dump_prefix_pop(prefix);
491 : 2 : }
492 : :
493 : 3027 : int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule)
494 : : {
495 : : int r;
496 : :
497 : : assert(chain);
498 : : assert(rule);
499 : :
500 : 3027 : rule->index = bf_list_size(&chain->rules);
501 : 3027 : r = _bf_chain_check_rule(chain, rule);
502 [ + + ]: 3027 : if (r)
503 : : return r;
504 : :
505 : 3025 : return bf_list_add_tail(&chain->rules, rule);
506 : : }
507 : :
508 : 77 : int bf_chain_add_set(struct bf_chain *chain, struct bf_set *set)
509 : : {
510 : : assert(chain && set);
511 : :
512 [ + + ]: 77 : if (!bf_set_is_valid_name(set->name))
513 [ + - ]: 1 : return bf_err_r(-EINVAL, "only named sets can be added to a chain");
514 : :
515 : 76 : return bf_hashset_add(&chain->sets, (void **)&set);
516 : : }
517 : :
518 : : const struct bf_set *
519 : 8273 : bf_chain_get_set_for_matcher(const struct bf_chain *chain,
520 : : const struct bf_matcher *matcher)
521 : : {
522 : : const struct bf_set *set;
523 : :
524 : : assert(chain);
525 : : assert(matcher);
526 : :
527 [ + + ]: 8273 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
528 : : return NULL;
529 : :
530 : 8272 : memcpy(&set, bf_matcher_payload(matcher), sizeof(struct bf_set *));
531 [ + + ]: 8272 : if (bf_set_is_anonymous(set))
532 : : return set;
533 : :
534 [ + + ]: 1115 : return bf_hashset_get(&chain->sets, set) == set ? set : NULL;
535 : : }
536 : :
537 : 128 : static struct bf_set *_bf_chain_get_set_by_name(struct bf_chain *chain,
538 : : const char *set_name)
539 : : {
540 : 128 : struct bf_set key = {.name = set_name};
541 : :
542 : : assert(chain);
543 : : assert(set_name);
544 : :
545 : 128 : return bf_hashset_get(&chain->sets, &key);
546 : : }
547 : :
548 : 106 : const struct bf_set *bf_chain_get_set_by_name(const struct bf_chain *chain,
549 : : const char *set_name)
550 : : {
551 : 106 : return _bf_chain_get_set_by_name((struct bf_chain *)chain, set_name);
552 : : }
553 : :
554 : 23 : int bf_chain_apply_set_delta(struct bf_chain *chain, const char *set_name,
555 : : const struct bf_set *to_add,
556 : : const struct bf_set *to_remove)
557 : : {
558 : 23 : _free_bf_set_ struct bf_set *add_copy = NULL;
559 : 23 : _free_bf_set_ struct bf_set *remove_copy = NULL;
560 : : struct bf_set *set;
561 : : int r;
562 : :
563 : : assert(chain);
564 : : assert(set_name);
565 : :
566 [ + + ]: 23 : if (!to_add && !to_remove)
567 [ + - ]: 1 : return bf_err_r(-EINVAL, "to_add and to_remove can't both be NULL");
568 : :
569 : 22 : set = _bf_chain_get_set_by_name(chain, set_name);
570 [ + + ]: 22 : if (!set)
571 [ + - ]: 1 : return bf_err_r(-ENOENT, "set '%s' does not exist", set_name);
572 : :
573 [ + + + + : 21 : if ((to_add && !bf_set_same_key(set, to_add)) ||
+ + ]
574 [ - + ]: 18 : (to_remove && !bf_set_same_key(set, to_remove)))
575 [ + - ]: 1 : return bf_err_r(-EINVAL, "set key format mismatch");
576 : :
577 [ + + ]: 20 : if (to_add) {
578 : 19 : r = bf_set_new_from_copy(&add_copy, to_add);
579 [ + - ]: 19 : if (r)
580 : : return r;
581 : :
582 : 19 : r = bf_set_add_many(set, &add_copy);
583 [ - + ]: 19 : if (r)
584 [ # # ]: 0 : return bf_err_r(r, "failed to calculate set union");
585 : : }
586 : :
587 [ + + ]: 20 : if (to_remove) {
588 : 18 : r = bf_set_new_from_copy(&remove_copy, to_remove);
589 [ + - ]: 18 : if (r)
590 : : return r;
591 : :
592 : 18 : r = bf_set_remove_many(set, &remove_copy);
593 [ - + ]: 18 : if (r)
594 [ # # ]: 0 : return bf_err_r(r, "failed to calculate set difference");
595 : : }
596 : :
597 : 20 : r = _bf_chain_validate_rules(chain);
598 [ - + ]: 20 : if (r)
599 [ # # ]: 0 : return bf_err_r(r, "failed to validate chain after set update");
600 : :
601 : : return 0;
602 : : }
603 : :
604 : 1473 : int bf_chain_new_from_copy(struct bf_chain **dest, const struct bf_chain *src)
605 : : {
606 : 1473 : _free_bf_chain_ struct bf_chain *copy = NULL;
607 : : int r;
608 : :
609 : : assert(dest);
610 : : assert(src);
611 : :
612 [ - + ]: 1473 : if (src->hook < 0 || src->hook >= _BF_HOOK_MAX)
613 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown source chain hook type");
614 : :
615 : 1473 : r = bf_chain_new(©, src->name, src->hook, src->policy, NULL, NULL);
616 [ + - ]: 1473 : if (r)
617 : : return r;
618 : :
619 : 1473 : r = bf_hashset_reserve(©->sets, bf_hashset_size(&src->sets));
620 [ + - ]: 1473 : if (r)
621 : : return r;
622 : :
623 [ + + + + : 3118 : bf_hashset_foreach (&src->sets, set_node) {
+ + ]
624 : 86 : _free_bf_set_ struct bf_set *set = NULL;
625 : :
626 : 86 : r = bf_set_new_from_copy(&set, set_node->data);
627 [ + - ]: 86 : if (r)
628 : : return r;
629 : :
630 : 86 : r = bf_hashset_add(©->sets, (void **)&set);
631 [ + - ]: 86 : if (r)
632 : : return r;
633 : : }
634 : :
635 [ + + + + : 6432 : bf_list_foreach (&src->rules, rule_node) {
+ + ]
636 : 1745 : _free_bf_rule_ struct bf_rule *rule = NULL;
637 : :
638 : 1744 : r = bf_rule_new_from_copy(&rule, bf_list_node_get_data(rule_node));
639 [ + - ]: 1744 : if (r)
640 : : return r;
641 : :
642 [ + - + + : 9450 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
643 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
644 : : const struct bf_set *set;
645 : :
646 [ + + ]: 2982 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
647 : 2574 : continue;
648 : :
649 : 408 : set = bf_chain_get_set_for_matcher(src, matcher);
650 [ + + ]: 408 : if (!set)
651 [ + - ]: 1 : return bf_err_r(-ENOENT,
652 : : "matcher set not found in source chain");
653 [ + + ]: 407 : if (!set->name)
654 : 331 : continue;
655 : :
656 : 76 : set = bf_chain_get_set_by_name(copy, set->name);
657 [ - + ]: 76 : if (!set)
658 [ # # ]: 0 : return bf_err_r(-ENOENT,
659 : : "matcher set not found in copied chain");
660 : :
661 : 76 : memcpy((void *)bf_matcher_payload(matcher), &set,
662 : : sizeof(struct bf_set *));
663 : : }
664 : :
665 : 1743 : r = bf_chain_add_rule(copy, rule);
666 [ + - ]: 1743 : if (r)
667 : : return r;
668 : :
669 : 1743 : TAKE_PTR(rule);
670 : : }
671 : :
672 : 1472 : *dest = TAKE_PTR(copy);
673 : :
674 : 1472 : return 0;
675 : : }
|