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 10 : static void _bf_chain_update_features(struct bf_chain *chain,
23 : const struct bf_rule *rule)
24 : {
25 10 : bf_assert(rule);
26 :
27 10 : if (rule->log)
28 0 : chain->flags |= BF_FLAG(BF_CHAIN_LOG);
29 :
30 10 : if (bf_rule_mark_is_set(rule))
31 0 : chain->flags |= BF_FLAG(BF_CHAIN_SET_MARK);
32 :
33 80 : bf_list_foreach (&rule->matchers, matcher_node) {
34 30 : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
35 30 : if (bf_matcher_get_type(matcher) == BF_MATCHER_IP6_NEXTHDR)
36 0 : chain->flags |= BF_FLAG(BF_CHAIN_STORE_NEXTHDR);
37 :
38 30 : if (bf_matcher_get_type(matcher) == BF_MATCHER_META_MARK)
39 0 : chain->flags |= BF_FLAG(BF_CHAIN_GET_MARK);
40 : }
41 10 : }
42 :
43 19 : int bf_chain_new(struct bf_chain **chain, const char *name, enum bf_hook hook,
44 : enum bf_verdict policy, bf_list *sets, bf_list *rules)
45 : {
46 15 : _free_bf_chain_ struct bf_chain *_chain = NULL;
47 : size_t ridx = 0;
48 :
49 19 : bf_assert(chain && name);
50 17 : bf_assert(policy < _BF_TERMINAL_VERDICT_MAX);
51 :
52 15 : _chain = malloc(sizeof(*_chain));
53 15 : if (!_chain)
54 : return -ENOMEM;
55 :
56 15 : _chain->name = strdup(name);
57 15 : if (!_chain->name)
58 : return -ENOMEM;
59 :
60 15 : _chain->flags = 0;
61 15 : _chain->hook = hook;
62 15 : _chain->policy = policy;
63 :
64 15 : _chain->sets = bf_list_default(bf_set_free, bf_set_pack);
65 15 : if (sets)
66 3 : _chain->sets = bf_list_move(*sets);
67 :
68 15 : _chain->rules = bf_list_default(bf_rule_free, bf_rule_pack);
69 15 : if (rules)
70 4 : _chain->rules = bf_list_move(*rules);
71 50 : bf_list_foreach (&_chain->rules, rule_node) {
72 10 : struct bf_rule *rule = bf_list_node_get_data(rule_node);
73 :
74 10 : rule->index = ridx++;
75 10 : _bf_chain_update_features(_chain, rule);
76 : }
77 :
78 15 : *chain = TAKE_PTR(_chain);
79 :
80 15 : return 0;
81 : }
82 :
83 2 : int bf_chain_new_from_pack(struct bf_chain **chain, bf_rpack_node_t node)
84 : {
85 2 : _free_bf_chain_ struct bf_chain *_chain = NULL;
86 2 : _cleanup_free_ char *name = NULL;
87 : enum bf_hook hook;
88 : enum bf_verdict policy;
89 : bf_rpack_node_t array, array_node;
90 2 : bf_list rules = bf_list_default(bf_rule_free, bf_rule_pack);
91 2 : bf_list sets = bf_list_default(bf_set_free, bf_set_pack);
92 : int r;
93 :
94 2 : r = bf_rpack_kv_str(node, "name", &name);
95 2 : if (r)
96 0 : return bf_rpack_key_err(r, "bf_chain.name");
97 :
98 2 : r = bf_rpack_kv_enum(node, "hook", &hook);
99 : if (r)
100 0 : return bf_rpack_key_err(r, "bf_chain.hook");
101 :
102 2 : r = bf_rpack_kv_enum(node, "policy", &policy);
103 : if (r)
104 0 : return bf_rpack_key_err(r, "bf_chain.policy");
105 :
106 2 : r = bf_rpack_kv_array(node, "sets", &array);
107 2 : if (r)
108 0 : return bf_rpack_key_err(r, "bf_chain.sets");
109 4 : bf_rpack_array_foreach (array, array_node) {
110 0 : _free_bf_set_ struct bf_set *set = NULL;
111 :
112 0 : r = bf_list_emplace(&sets, bf_set_new_from_pack, set, array_node);
113 : if (r) {
114 0 : return bf_err_r(r, "failed to unpack bf_set into bf_chain.sets");
115 : }
116 : }
117 :
118 2 : r = bf_rpack_kv_array(node, "rules", &array);
119 2 : if (r)
120 0 : return bf_rpack_key_err(r, "bf_chain.rules");
121 12 : bf_rpack_array_foreach (array, array_node) {
122 8 : _free_bf_rule_ struct bf_rule *rule = NULL;
123 :
124 4 : r = bf_list_emplace(&rules, bf_rule_new_from_pack, rule, array_node);
125 : if (r) {
126 0 : return bf_err_r(r, "failed to unpack bf_rule into bf_chain.rules");
127 : }
128 : }
129 :
130 2 : r = bf_chain_new(&_chain, name, hook, policy, &sets, &rules);
131 2 : if (r)
132 0 : return bf_err_r(r, "failed to create bf_chain from pack");
133 :
134 2 : *chain = TAKE_PTR(_chain);
135 :
136 2 : return 0;
137 : }
138 :
139 37 : void bf_chain_free(struct bf_chain **chain)
140 : {
141 37 : bf_assert(chain);
142 :
143 36 : if (!*chain)
144 : return;
145 :
146 15 : bf_list_clean(&(*chain)->sets);
147 15 : bf_list_clean(&(*chain)->rules);
148 15 : freep((void *)&(*chain)->name);
149 : freep((void *)chain);
150 : }
151 :
152 4 : int bf_chain_pack(const struct bf_chain *chain, bf_wpack_t *pack)
153 : {
154 4 : bf_assert(chain);
155 3 : bf_assert(pack);
156 :
157 2 : bf_wpack_kv_str(pack, "name", chain->name);
158 2 : bf_wpack_kv_enum(pack, "hook", chain->hook);
159 2 : bf_wpack_kv_enum(pack, "policy", chain->policy);
160 :
161 2 : bf_wpack_kv_list(pack, "sets", &chain->sets);
162 2 : bf_wpack_kv_list(pack, "rules", &chain->rules);
163 :
164 2 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
165 : }
166 :
167 0 : void bf_chain_dump(const struct bf_chain *chain, prefix_t *prefix)
168 : {
169 0 : bf_assert(chain && prefix);
170 :
171 0 : DUMP(prefix, "struct bf_chain at %p", chain);
172 0 : bf_dump_prefix_push(prefix);
173 :
174 0 : DUMP(prefix, "name: %s", chain->name);
175 0 : DUMP(prefix, "flags: %02x", chain->flags);
176 0 : DUMP(prefix, "hook: %s", bf_hook_to_str(chain->hook));
177 0 : DUMP(prefix, "policy: %s", bf_verdict_to_str(chain->policy));
178 :
179 0 : DUMP(prefix, "sets: bf_list<bf_set>[%lu]", bf_list_size(&chain->sets));
180 0 : bf_dump_prefix_push(prefix);
181 0 : bf_list_foreach (&chain->sets, set_node) {
182 0 : if (bf_list_is_tail(&chain->sets, set_node))
183 0 : bf_dump_prefix_last(prefix);
184 :
185 0 : bf_set_dump(bf_list_node_get_data(set_node), prefix);
186 : }
187 0 : bf_dump_prefix_pop(prefix);
188 :
189 0 : DUMP(bf_dump_prefix_last(prefix), "rules: bf_list<bf_rule>[%lu]",
190 : bf_list_size(&chain->rules));
191 0 : bf_dump_prefix_push(prefix);
192 0 : bf_list_foreach (&chain->rules, rule_node) {
193 0 : if (bf_list_is_tail(&chain->rules, rule_node))
194 0 : bf_dump_prefix_last(prefix);
195 :
196 0 : bf_rule_dump(bf_list_node_get_data(rule_node), prefix);
197 : }
198 0 : bf_dump_prefix_pop(prefix);
199 :
200 0 : bf_dump_prefix_pop(prefix);
201 0 : }
202 :
203 0 : int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule)
204 : {
205 0 : bf_assert(chain && rule);
206 :
207 0 : rule->index = bf_list_size(&chain->rules);
208 0 : _bf_chain_update_features(chain, rule);
209 :
210 0 : return bf_list_add_tail(&chain->rules, rule);
211 : }
212 :
213 0 : struct bf_set *bf_chain_get_set_for_matcher(const struct bf_chain *chain,
214 : const struct bf_matcher *matcher)
215 : {
216 0 : bf_assert(chain && matcher);
217 :
218 : uint32_t set_id;
219 :
220 0 : if (bf_matcher_get_type(matcher) != BF_MATCHER_SET)
221 : return NULL;
222 :
223 0 : set_id = *(uint32_t *)bf_matcher_payload(matcher);
224 :
225 0 : return bf_list_get_at(&chain->sets, set_id);
226 : }
227 :
228 8 : bool bf_chain_validate(struct bf_chain *chain)
229 : {
230 8 : bf_assert(chain);
231 :
232 8 : if (chain->flags & BF_FLAG(BF_CHAIN_SET_MARK) &&
233 0 : (chain->hook == BF_HOOK_XDP || chain->hook == BF_HOOK_NF_PRE_ROUTING ||
234 : chain->hook == BF_HOOK_NF_POST_ROUTING ||
235 : chain->hook == BF_HOOK_NF_FORWARD ||
236 : chain->hook == BF_HOOK_NF_LOCAL_IN ||
237 : chain->hook == BF_HOOK_NF_LOCAL_OUT)) {
238 0 : bf_err("XDP and Netfilter chains can't set packet mark");
239 0 : return false;
240 : }
241 :
242 8 : if (chain->flags & BF_FLAG(BF_CHAIN_GET_MARK) &&
243 0 : chain->hook == BF_HOOK_XDP) {
244 0 : bf_err("XDP chains can't read packet mark");
245 0 : return false;
246 : }
247 :
248 : return true;
249 : }
|