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/list.h"
14 : : #include "bpfilter/dump.h"
15 : : #include "bpfilter/helper.h"
16 : : #include "bpfilter/hook.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 : 6186 : 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 [ + + + + : 41618 : 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 [ + + ]: 14637 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
44 : 13413 : continue;
45 : :
46 : 1224 : set_index = *(uint32_t *)bf_matcher_payload(matcher);
47 : 1224 : set = bf_list_get_at(&chain->sets, set_index);
48 : :
49 [ - + ]: 1224 : if (!set) {
50 [ # # ]: 0 : return bf_err_r(-EINVAL, "rule %u references non-existent set",
51 : : rule->index);
52 : : }
53 : :
54 [ + + ]: 1224 : if (bf_set_is_empty(set)) {
55 [ + - ]: 14 : bf_warn("rule %u references empty set, rule will be disabled",
56 : : rule->index);
57 : 14 : 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 : 6172 : 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 [ + + ]: 37032 : for (int i = 0; i < _BF_MATCHER_LAYER_MAX; ++i)
82 : 30860 : layer_hdr_id[i] = UINT32_MAX;
83 : :
84 [ + + + + : 41586 : 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 [ + + ]: 14623 : if (bf_matcher_get_type(matcher) == BF_MATCHER_SET)
89 : 1210 : continue;
90 : :
91 : 13413 : meta = bf_matcher_get_meta(bf_matcher_get_type(matcher));
92 [ - + ]: 13413 : 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 [ + + ]: 13413 : if (meta->layer <= BF_MATCHER_NO_LAYER)
98 : 3558 : continue;
99 : :
100 [ + + ]: 9855 : if (layer_hdr_id[meta->layer] == UINT32_MAX) {
101 : 3952 : layer_hdr_id[meta->layer] = meta->hdr_id;
102 : 3952 : continue;
103 : : }
104 : :
105 [ + + ]: 5903 : 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 : 6186 : int _bf_chain_check_rule(struct bf_chain *chain, struct bf_rule *rule)
117 : : {
118 : : int r;
119 : :
120 : : assert(rule);
121 : :
122 : 6186 : r = _bf_rule_references_empty_set(chain, rule);
123 [ - + ]: 6186 : if (r < 0)
124 : : return r;
125 : 6186 : rule->disabled = r;
126 : :
127 [ + + ]: 6186 : if (!rule->disabled) {
128 : 6172 : r = _bf_rule_has_incompatible_matchers(rule);
129 [ + - ]: 6172 : if (r < 0)
130 : : return r;
131 : :
132 : 6172 : rule->disabled = r;
133 : : }
134 : :
135 [ + + - + ]: 6388 : if (rule->log &&
136 : 202 : 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 [ + + + + ]: 6186 : if (rule->log && !rule->disabled)
142 : 201 : chain->flags |= BF_FLAG(BF_CHAIN_LOG);
143 : :
144 [ + + + + ]: 6257 : if (bf_rule_mark_is_set(rule) &&
145 [ + + ]: 123 : bf_hook_to_flavor(chain->hook) != BF_FLAVOR_TC &&
146 : 52 : 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 [ + + + + : 41494 : 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 [ + + ]: 14635 : if (bf_matcher_get_type(matcher) == BF_MATCHER_IP6_NEXTHDR &&
157 [ + - ]: 624 : !rule->disabled)
158 : 624 : chain->flags |= BF_FLAG(BF_CHAIN_STORE_NEXTHDR);
159 : :
160 : : // Ensure the matcher is compatible with the chain's hook.
161 : 14635 : meta = bf_matcher_get_meta(bf_matcher_get_type(matcher));
162 [ - + ]: 14635 : if (!meta) {
163 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher type %d in rule",
164 : : bf_matcher_get_type(matcher));
165 : : }
166 : :
167 [ + + ]: 14635 : if (meta->unsupported_hooks & BF_FLAG(chain->hook)) {
168 [ + - ]: 72 : 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 : 4298 : 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 : 4298 : _free_bf_chain_ struct bf_chain *_chain = NULL;
182 : : size_t ridx = 0;
183 : : int r;
184 : :
185 : : assert(chain && name);
186 [ - + ]: 4298 : if (hook >= _BF_HOOK_MAX)
187 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown hook type");
188 [ + + ]: 4298 : if (!bf_verdict_is_valid_policy(policy))
189 [ + - ]: 2 : return bf_err_r(-EINVAL, "invalid policy '%s'",
190 : : bf_verdict_to_str(policy));
191 : :
192 : 4296 : _chain = malloc(sizeof(*_chain));
193 [ - + ]: 4296 : if (!_chain)
194 : : return -ENOMEM;
195 : :
196 : 4296 : _chain->name = strdup(name);
197 [ - + ]: 4296 : if (!_chain->name)
198 : : return -ENOMEM;
199 : :
200 : 4296 : _chain->flags = 0;
201 : 4296 : _chain->hook = hook;
202 : 4296 : _chain->policy = policy;
203 : :
204 : 4296 : _chain->sets = bf_list_default(bf_set_free, bf_set_pack);
205 [ + + ]: 4296 : if (sets)
206 : 3253 : _chain->sets = bf_list_move(*sets);
207 : :
208 : 4296 : _chain->rules = bf_list_default(bf_rule_free, bf_rule_pack);
209 [ + + ]: 4296 : if (rules)
210 : 3210 : _chain->rules = bf_list_move(*rules);
211 [ + + + + : 18718 : bf_list_foreach (&_chain->rules, rule_node) {
+ + ]
212 : : struct bf_rule *rule = bf_list_node_get_data(rule_node);
213 : :
214 : 5137 : rule->index = ridx++;
215 : 5137 : r = _bf_chain_check_rule(_chain, rule);
216 [ + + ]: 5137 : if (r)
217 : : return r;
218 : : }
219 : :
220 : 4222 : *chain = TAKE_PTR(_chain);
221 : :
222 : 4222 : return 0;
223 : : }
224 : :
225 : 2561 : int bf_chain_new_from_pack(struct bf_chain **chain, bf_rpack_node_t node)
226 : : {
227 : 2561 : _free_bf_chain_ struct bf_chain *_chain = NULL;
228 : 2561 : _cleanup_free_ char *name = NULL;
229 : : enum bf_hook hook;
230 : : enum bf_verdict policy;
231 : : bf_rpack_node_t array, array_node;
232 : 2561 : bf_list rules = bf_list_default(bf_rule_free, bf_rule_pack);
233 : 2561 : bf_list sets = bf_list_default(bf_set_free, bf_set_pack);
234 : : int r;
235 : :
236 : 2561 : r = bf_rpack_kv_str(node, "name", &name);
237 [ - + ]: 2561 : if (r)
238 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.name");
239 : :
240 [ - + - + : 2561 : r = bf_rpack_kv_enum(node, "hook", &hook, 0, _BF_HOOK_MAX);
- - ]
241 : : if (r)
242 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.hook");
243 : :
244 [ - + - + : 2561 : r = bf_rpack_kv_enum(node, "policy", &policy, 0, _BF_VERDICT_MAX);
- - ]
245 : : if (r)
246 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.policy");
247 : :
248 : 2561 : r = bf_rpack_kv_array(node, "sets", &array);
249 [ - + ]: 2561 : if (r)
250 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.sets");
251 [ + + + + : 6960 : bf_rpack_array_foreach (array, array_node) {
+ + ]
252 : 1838 : _free_bf_set_ struct bf_set *set = NULL;
253 : :
254 [ + - + - : 919 : r = bf_list_emplace(&sets, bf_set_new_from_pack, set, array_node);
- - - - ]
255 : : if (r) {
256 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_set into bf_chain.sets");
257 : : }
258 : : }
259 : :
260 : 2561 : r = bf_rpack_kv_array(node, "rules", &array);
261 [ - + ]: 2561 : if (r)
262 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_chain.rules");
263 [ + + + + : 13510 : bf_rpack_array_foreach (array, array_node) {
+ + ]
264 : 8388 : _free_bf_rule_ struct bf_rule *rule = NULL;
265 : :
266 [ + - + - : 4194 : r = bf_list_emplace(&rules, bf_rule_new_from_pack, rule, array_node);
- - - - ]
267 : : if (r) {
268 [ # # ]: 0 : return bf_err_r(r, "failed to unpack bf_rule into bf_chain.rules");
269 : : }
270 : : }
271 : :
272 : 2561 : r = bf_chain_new(&_chain, name, hook, policy, &sets, &rules);
273 [ - + ]: 2561 : if (r)
274 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_chain from pack");
275 : :
276 : 2561 : *chain = TAKE_PTR(_chain);
277 : :
278 : 2561 : return 0;
279 : : }
280 : :
281 : 14023 : void bf_chain_free(struct bf_chain **chain)
282 : : {
283 : : assert(chain);
284 : :
285 [ + + ]: 14023 : if (!*chain)
286 : : return;
287 : :
288 : 4296 : bf_list_clean(&(*chain)->sets);
289 : 4296 : bf_list_clean(&(*chain)->rules);
290 : 4296 : freep((void *)&(*chain)->name);
291 : : freep((void *)chain);
292 : : }
293 : :
294 : 3453 : int bf_chain_pack(const struct bf_chain *chain, bf_wpack_t *pack)
295 : : {
296 : : assert(chain);
297 : : assert(pack);
298 : :
299 : 3453 : bf_wpack_kv_str(pack, "name", chain->name);
300 : 3453 : bf_wpack_kv_enum(pack, "hook", chain->hook);
301 : 3453 : bf_wpack_kv_enum(pack, "policy", chain->policy);
302 : :
303 : 3453 : bf_wpack_kv_list(pack, "sets", &chain->sets);
304 : 3453 : bf_wpack_kv_list(pack, "rules", &chain->rules);
305 : :
306 [ - + ]: 3453 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
307 : : }
308 : :
309 : 18 : void bf_chain_dump(const struct bf_chain *chain, prefix_t *prefix)
310 : : {
311 : : assert(chain);
312 : : assert(prefix);
313 : :
314 [ - + ]: 18 : DUMP(prefix, "struct bf_chain at %p", chain);
315 : 18 : bf_dump_prefix_push(prefix);
316 : :
317 [ - + ]: 18 : DUMP(prefix, "name: %s", chain->name);
318 [ - + ]: 18 : DUMP(prefix, "flags: %02x", chain->flags);
319 [ - + ]: 18 : DUMP(prefix, "hook: %s", bf_hook_to_str(chain->hook));
320 [ - + ]: 18 : DUMP(prefix, "policy: %s", bf_verdict_to_str(chain->policy));
321 : :
322 [ - + ]: 18 : DUMP(prefix, "sets: bf_list<bf_set>[%lu]", bf_list_size(&chain->sets));
323 : 18 : bf_dump_prefix_push(prefix);
324 [ + + + + : 42 : bf_list_foreach (&chain->sets, set_node) {
+ + ]
325 [ + + ]: 3 : if (bf_list_is_tail(&chain->sets, set_node))
326 : 1 : bf_dump_prefix_last(prefix);
327 : :
328 : 3 : bf_set_dump(bf_list_node_get_data(set_node), prefix);
329 : : }
330 : 18 : bf_dump_prefix_pop(prefix);
331 : :
332 [ - + ]: 18 : DUMP(bf_dump_prefix_last(prefix), "rules: bf_list<bf_rule>[%lu]",
333 : : bf_list_size(&chain->rules));
334 : 18 : bf_dump_prefix_push(prefix);
335 [ + + + + : 60 : bf_list_foreach (&chain->rules, rule_node) {
+ + ]
336 [ + + ]: 12 : if (bf_list_is_tail(&chain->rules, rule_node))
337 : 7 : bf_dump_prefix_last(prefix);
338 : :
339 : 12 : bf_rule_dump(bf_list_node_get_data(rule_node), prefix);
340 : : }
341 : 18 : bf_dump_prefix_pop(prefix);
342 : :
343 : 18 : bf_dump_prefix_pop(prefix);
344 : 18 : }
345 : :
346 : 1049 : int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule)
347 : : {
348 : : int r;
349 : :
350 : : assert(chain);
351 : : assert(rule);
352 : :
353 : 1049 : rule->index = bf_list_size(&chain->rules);
354 : 1049 : r = _bf_chain_check_rule(chain, rule);
355 [ + - ]: 1049 : if (r)
356 : : return r;
357 : :
358 : 1049 : return bf_list_add_tail(&chain->rules, rule);
359 : : }
360 : :
361 : 180 : int bf_chain_add_set(struct bf_chain *chain, struct bf_set *set)
362 : : {
363 : : assert(chain && set);
364 : :
365 : 180 : return bf_list_add_tail(&chain->sets, set);
366 : : }
367 : :
368 : 358 : struct bf_set *bf_chain_get_set_for_matcher(const struct bf_chain *chain,
369 : : const struct bf_matcher *matcher)
370 : : {
371 : : assert(chain);
372 : : assert(matcher);
373 : :
374 : : uint32_t set_id;
375 : :
376 [ + + ]: 358 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
377 : : return NULL;
378 : :
379 : 357 : set_id = *(uint32_t *)bf_matcher_payload(matcher);
380 : :
381 : 357 : return bf_list_get_at(&chain->sets, set_id);
382 : : }
383 : :
384 : 20 : struct bf_set *bf_chain_get_set_by_name(struct bf_chain *chain,
385 : : const char *set_name)
386 : : {
387 : : assert(chain);
388 : : assert(set_name);
389 : :
390 [ + - + + : 46 : bf_list_foreach (&chain->sets, set_node) {
+ + ]
391 : : struct bf_set *set = bf_list_node_get_data(set_node);
392 [ + + ]: 22 : if (bf_streq(set->name, set_name))
393 : : return set;
394 : : }
395 : :
396 : : return NULL;
397 : : }
398 : :
399 : 2241 : int bf_chain_new_from_copy(struct bf_chain **dest, const struct bf_chain *src)
400 : : {
401 : 2241 : _free_bf_wpack_ bf_wpack_t *wpack = NULL;
402 : 2241 : _free_bf_rpack_ bf_rpack_t *rpack = NULL;
403 : : const void *data;
404 : : size_t data_len;
405 : : int r;
406 : :
407 : : assert(dest);
408 : : assert(src);
409 : :
410 : : // For now, we do a copy by serializing and deserializing the struct.
411 : : // @todo Implement deep copy to avoid serialization overhead.
412 : 2241 : r = bf_wpack_new(&wpack);
413 [ - + ]: 2241 : if (r)
414 [ # # ]: 0 : return bf_err_r(r, "failed to create wpack for chain serialization");
415 : :
416 : 2241 : r = bf_chain_pack(src, wpack);
417 [ - + ]: 2241 : if (r)
418 [ # # ]: 0 : return bf_err_r(r, "failed to serialize chain");
419 : :
420 : 2241 : r = bf_wpack_get_data(wpack, &data, &data_len);
421 [ - + ]: 2241 : if (r)
422 [ # # ]: 0 : return bf_err_r(r, "failed to get serialized chain data");
423 : :
424 : 2241 : r = bf_rpack_new(&rpack, data, data_len);
425 [ - + ]: 2241 : if (r)
426 [ # # ]: 0 : return bf_err_r(r, "failed to create rpack for chain deserialization");
427 : :
428 : 2241 : r = bf_chain_new_from_pack(dest, bf_rpack_root(rpack));
429 [ - + ]: 2241 : if (r)
430 [ # # ]: 0 : return bf_err_r(r, "failed to deserialize chain");
431 : :
432 : : return 0;
433 : : }
|