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/rule.h"
7 : :
8 : : #include <errno.h>
9 : : #include <inttypes.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/logger.h"
17 : : #include "bpfilter/matcher.h"
18 : : #include "bpfilter/pack.h"
19 : : #include "bpfilter/runtime.h"
20 : : #include "bpfilter/verdict.h"
21 : :
22 : : static const char *_bf_log_opt_strs[] = {
23 : : [BF_LOG_OPT_LINK] = "link",
24 : : [BF_LOG_OPT_INTERNET] = "internet",
25 : : [BF_LOG_OPT_TRANSPORT] = "transport",
26 : : [BF_LOG_OPT_5_TUPLE] = "5-tuple",
27 : : };
28 : : static_assert_enum_mapping(_bf_log_opt_strs, _BF_LOG_OPT_MAX);
29 : :
30 : 6 : const char *bf_log_opt_to_str(enum bf_log_opt hdr)
31 : : {
32 [ + + ]: 6 : if (hdr < 0 || hdr >= _BF_LOG_OPT_MAX)
33 : : return "<bf_log_opt unknown>";
34 : :
35 : 4 : return _bf_log_opt_strs[hdr];
36 : : }
37 : :
38 : 78 : int bf_log_opt_from_str(const char *str, enum bf_log_opt *hdr)
39 : : {
40 : : assert(hdr);
41 : :
42 [ + + ]: 172 : for (int i = 0; i < _BF_LOG_OPT_MAX; ++i) {
43 [ + + ]: 166 : if (bf_streq_i(str, _bf_log_opt_strs[i])) {
44 : 72 : *hdr = (enum bf_log_opt)i;
45 : 72 : return 0;
46 : : }
47 : : }
48 : :
49 : : return -EINVAL;
50 : : }
51 : :
52 : 9956 : int bf_rule_new(struct bf_rule **rule)
53 : : {
54 : 9956 : _free_bf_rule_ struct bf_rule *_rule = NULL;
55 : :
56 : : assert(rule);
57 : :
58 : 9956 : _rule = calloc(1, sizeof(*_rule));
59 [ + - ]: 9956 : if (!_rule)
60 : : return -ENOMEM;
61 : :
62 : 9956 : _rule->matchers = bf_list_default(bf_matcher_free, bf_matcher_pack);
63 : :
64 : 9956 : *rule = TAKE_PTR(_rule);
65 : :
66 : 9956 : return 0;
67 : : }
68 : :
69 : 5781 : int bf_rule_new_from_pack(struct bf_rule **rule, bf_rpack_node_t node,
70 : : const bf_hashset *sets)
71 : : {
72 : 5781 : _free_bf_rule_ struct bf_rule *_rule = NULL;
73 : : bf_rpack_node_t m_nodes, m_node;
74 : : bool has_counters;
75 : : int r;
76 : :
77 : : assert(rule);
78 : :
79 : 5781 : r = bf_rule_new(&_rule);
80 [ - + ]: 5781 : if (r)
81 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_rule from pack");
82 : :
83 : 5781 : r = bf_rpack_kv_u32(node, "index", &_rule->index);
84 [ - + ]: 5781 : if (r)
85 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.index");
86 : :
87 : 5781 : r = bf_rpack_kv_u8(node, "log", &_rule->log);
88 [ + + ]: 5781 : if (r)
89 [ + - ]: 1 : return bf_rpack_key_err(r, "bf_rule.log");
90 : :
91 [ + - ]: 5780 : if (bf_rpack_kv_contains(node, "log_rate_ns")) {
92 : 5780 : r = bf_rpack_kv_u64(node, "log_rate_ns", &_rule->log_rate_ns);
93 [ - + ]: 5780 : if (r)
94 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.log_rate_ns");
95 : : }
96 : :
97 [ + - ]: 5780 : if (bf_rpack_kv_contains(node, "has_counters")) {
98 : 5780 : r = bf_rpack_kv_bool(node, "has_counters", &has_counters);
99 [ - + ]: 5780 : if (r)
100 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.has_counters");
101 [ # # ]: 0 : } else if (bf_rpack_kv_contains(node, "counters")) {
102 : 0 : r = bf_rpack_kv_bool(node, "counters", &has_counters);
103 [ # # ]: 0 : if (r)
104 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.counters");
105 : : } else {
106 [ # # ]: 0 : return bf_err_r(-ENOENT, "missing bf_rule counters flag in pack");
107 : : }
108 : 5780 : _rule->has_counters = has_counters;
109 : :
110 : 5780 : r = bf_rpack_kv_u64(node, "mark", &_rule->mark);
111 [ - + ]: 5780 : if (r)
112 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.mark");
113 : :
114 [ - + - + : 5780 : r = bf_rpack_kv_enum(node, "verdict", &_rule->verdict, 0, _BF_VERDICT_MAX);
- - ]
115 : : if (r)
116 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.verdict");
117 : :
118 [ + - ]: 5780 : if (bf_rpack_kv_contains(node, "redirect_ifindex")) {
119 : 5780 : r = bf_rpack_kv_u32(node, "redirect_ifindex", &_rule->redirect_ifindex);
120 [ - + ]: 5780 : if (r)
121 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.redirect_ifindex");
122 : : }
123 : :
124 [ + - ]: 5780 : if (bf_rpack_kv_contains(node, "redirect_dir")) {
125 [ - + - + : 5780 : r = bf_rpack_kv_enum(node, "redirect_dir", &_rule->redirect_dir, 0,
- - ]
126 : : _BF_REDIRECT_DIR_MAX);
127 : : if (r)
128 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.redirect_dir");
129 : : }
130 : :
131 : 5780 : r = bf_rpack_kv_array(node, "matchers", &m_nodes);
132 [ - + ]: 5780 : if (r)
133 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_rule.matchers");
134 [ + - + + : 17446 : bf_rpack_array_foreach (m_nodes, m_node) {
+ + ]
135 : 11666 : _free_bf_matcher_ struct bf_matcher *matcher = NULL;
136 : :
137 [ + - + - : 5833 : r = bf_list_emplace(&_rule->matchers, bf_matcher_new_from_pack, matcher,
- - - - ]
138 : : m_node, sets);
139 : : if (r) {
140 [ # # ]: 0 : return bf_err_r(
141 : : r, "failed to unpack bf_matcher into bf_rule.matchers");
142 : : }
143 : : }
144 : :
145 : 5780 : *rule = TAKE_PTR(_rule);
146 : :
147 : 5780 : return 0;
148 : : }
149 : :
150 : 1746 : int bf_rule_new_from_copy(struct bf_rule **dest, const struct bf_rule *src)
151 : : {
152 : 1746 : _free_bf_rule_ struct bf_rule *copy = NULL;
153 : : int r;
154 : :
155 : : assert(dest);
156 : : assert(src);
157 : :
158 [ + + ]: 1746 : if (src->verdict < 0 || src->verdict >= _BF_VERDICT_MAX ||
159 [ - + ]: 1745 : src->redirect_dir < 0 || src->redirect_dir >= _BF_REDIRECT_DIR_MAX) {
160 [ + - ]: 1 : return bf_err_r(-EINVAL,
161 : : "invalid source rule verdict or redirect direction");
162 : : }
163 : :
164 : 1745 : r = bf_rule_new(©);
165 [ + - ]: 1745 : if (r)
166 : : return r;
167 : :
168 : 1745 : copy->index = src->index;
169 : 1745 : copy->log = src->log;
170 : 1745 : copy->log_rate_ns = src->log_rate_ns;
171 : 1745 : copy->has_counters = src->has_counters;
172 : 1745 : copy->mark = src->mark;
173 : 1745 : copy->verdict = src->verdict;
174 : 1745 : copy->redirect_ifindex = src->redirect_ifindex;
175 : 1745 : copy->redirect_dir = src->redirect_dir;
176 : :
177 [ + - + + : 9460 : bf_list_foreach (&src->matchers, matcher_node) {
+ + ]
178 : 2985 : _free_bf_matcher_ struct bf_matcher *matcher = NULL;
179 : :
180 [ + - + - : 2985 : r = bf_list_emplace(©->matchers, bf_matcher_new_from_copy, matcher,
- - - - ]
181 : : bf_list_node_get_data(matcher_node));
182 : : if (r)
183 : : return r;
184 : : }
185 : :
186 : 1745 : *dest = TAKE_PTR(copy);
187 : :
188 : 1745 : return 0;
189 : : }
190 : :
191 : 36160 : void bf_rule_free(struct bf_rule **rule)
192 : : {
193 : : assert(rule);
194 : :
195 [ + + ]: 36160 : if (!*rule)
196 : : return;
197 : :
198 : 9956 : bf_list_clean(&(*rule)->matchers);
199 : :
200 : 9956 : free(*rule);
201 : 9956 : *rule = NULL;
202 : : }
203 : :
204 : 1763 : int bf_rule_pack(const struct bf_rule *rule, bf_wpack_t *pack)
205 : : {
206 : : assert(rule);
207 : : assert(pack);
208 : :
209 : 1763 : bf_wpack_kv_u32(pack, "index", rule->index);
210 : 1763 : bf_wpack_kv_u8(pack, "log", rule->log);
211 : 1763 : bf_wpack_kv_u64(pack, "log_rate_ns", rule->log_rate_ns);
212 : 1763 : bf_wpack_kv_bool(pack, "has_counters", rule->has_counters);
213 : 1763 : bf_wpack_kv_u64(pack, "mark", rule->mark);
214 : 1763 : bf_wpack_kv_int(pack, "verdict", rule->verdict);
215 : 1763 : bf_wpack_kv_u32(pack, "redirect_ifindex", rule->redirect_ifindex);
216 : 1763 : bf_wpack_kv_int(pack, "redirect_dir", rule->redirect_dir);
217 : :
218 : 1763 : bf_wpack_kv_list(pack, "matchers", &rule->matchers);
219 : :
220 [ - + ]: 1763 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
221 : : }
222 : :
223 : 8 : void bf_rule_dump(const struct bf_rule *rule, prefix_t *prefix)
224 : : {
225 : : assert(rule);
226 : : assert(prefix);
227 : :
228 [ - + ]: 8 : DUMP(prefix, "struct bf_rule at %p", rule);
229 : :
230 : 8 : bf_dump_prefix_push(prefix);
231 : :
232 [ - + ]: 8 : DUMP(prefix, "index: %u", rule->index);
233 : :
234 : : // Matchers
235 [ - + ]: 8 : DUMP(prefix, "matchers: %lu", bf_list_size(&rule->matchers));
236 : 8 : bf_dump_prefix_push(prefix);
237 [ + + + + : 72 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
238 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
239 : :
240 [ + + ]: 28 : if (bf_list_is_tail(&rule->matchers, matcher_node))
241 : 7 : bf_dump_prefix_last(prefix);
242 : :
243 : 28 : bf_matcher_dump(matcher, prefix);
244 : : }
245 : 8 : bf_dump_prefix_pop(prefix);
246 : :
247 [ - + ]: 8 : DUMP(prefix, "log: %02x", rule->log);
248 : :
249 [ - + - - ]: 8 : DUMP(prefix, "has_counters: %s", rule->has_counters ? "yes" : "no");
250 [ + + ]: 8 : if (rule->has_counters) {
251 [ - + ]: 6 : DUMP(prefix, "counters: struct bf_counter");
252 : 6 : bf_dump_prefix_push(prefix);
253 [ - + ]: 6 : DUMP(prefix, "count: %lu", rule->counters.count);
254 [ - + ]: 6 : DUMP(prefix, "size: %lu", rule->counters.size);
255 : 6 : bf_dump_prefix_pop(prefix);
256 : : }
257 : :
258 [ - + - - ]: 8 : DUMP(prefix, "disabled: %s", rule->disabled ? "yes" : "no");
259 [ - + ]: 8 : DUMP(prefix, "mark: 0x%" PRIx64, rule->mark);
260 [ - + ]: 8 : DUMP(prefix, "verdict: %s", bf_verdict_to_str(rule->verdict));
261 [ - + ]: 8 : if (bf_rule_has_redirect(rule)) {
262 [ # # ]: 0 : DUMP(prefix, "redirect_ifindex: %u", rule->redirect_ifindex);
263 [ # # ]: 0 : DUMP(bf_dump_prefix_last(prefix), "redirect_dir: %s",
264 : : bf_redirect_dir_to_str(rule->redirect_dir));
265 : : } else {
266 [ - + ]: 8 : DUMP(prefix, "redirect_ifindex: <not set>");
267 [ - + ]: 8 : DUMP(bf_dump_prefix_last(prefix), "redirect_dir: <not set>");
268 : : }
269 : :
270 : 8 : bf_dump_prefix_pop(prefix);
271 : 8 : }
272 : :
273 : 18 : int bf_rule_add_matcher(struct bf_rule *rule, enum bf_matcher_type type,
274 : : enum bf_matcher_op op, const void *payload,
275 : : size_t payload_len, bool negate)
276 : : {
277 : 18 : _free_bf_matcher_ struct bf_matcher *matcher = NULL;
278 : : int r;
279 : :
280 : : assert(rule);
281 : :
282 : 18 : r = bf_matcher_new(&matcher, type, op, payload, payload_len, negate);
283 [ + - ]: 18 : if (r)
284 : : return r;
285 : :
286 : 18 : r = bf_list_add_tail(&rule->matchers, matcher);
287 [ + - ]: 18 : if (r)
288 : : return r;
289 : :
290 : 18 : TAKE_PTR(matcher);
291 : :
292 : 18 : return 0;
293 : : }
|