Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "bpfilter/hook.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/netfilter.h>
10 : :
11 : : #include <errno.h>
12 : : #include <limits.h>
13 : : #include <stdbool.h>
14 : : #include <stdint.h>
15 : : #include <stdio.h>
16 : : #include <stdlib.h>
17 : : #include <string.h>
18 : : #include <sys/socket.h>
19 : :
20 : : #include "bpfilter/dump.h"
21 : : #include "bpfilter/flavor.h"
22 : : #include "bpfilter/helper.h"
23 : : #include "bpfilter/list.h"
24 : : #include "bpfilter/logger.h"
25 : : #include "bpfilter/pack.h"
26 : :
27 : : static const char *_bf_hook_strs[] = {
28 : : [BF_HOOK_XDP] = "BF_HOOK_XDP",
29 : : [BF_HOOK_TC_INGRESS] = "BF_HOOK_TC_INGRESS",
30 : : [BF_HOOK_NF_PRE_ROUTING] = "BF_HOOK_NF_PRE_ROUTING",
31 : : [BF_HOOK_NF_LOCAL_IN] = "BF_HOOK_NF_LOCAL_IN",
32 : : [BF_HOOK_CGROUP_INGRESS] = "BF_HOOK_CGROUP_INGRESS",
33 : : [BF_HOOK_CGROUP_EGRESS] = "BF_HOOK_CGROUP_EGRESS",
34 : : [BF_HOOK_NF_FORWARD] = "BF_HOOK_NF_FORWARD",
35 : : [BF_HOOK_NF_LOCAL_OUT] = "BF_HOOK_NF_LOCAL_OUT",
36 : : [BF_HOOK_NF_POST_ROUTING] = "BF_HOOK_NF_POST_ROUTING",
37 : : [BF_HOOK_TC_EGRESS] = "BF_HOOK_TC_EGRESS",
38 : : };
39 : : static_assert_enum_mapping(_bf_hook_strs, _BF_HOOK_MAX);
40 : :
41 : 115 : const char *bf_hook_to_str(enum bf_hook hook)
42 : : {
43 [ + + ]: 115 : if (hook < 0 || hook >= _BF_HOOK_MAX)
44 : : return "<bf_hook unknown>";
45 : :
46 : 113 : return _bf_hook_strs[hook];
47 : : }
48 : :
49 : 995 : int bf_hook_from_str(const char *str, enum bf_hook *hook)
50 : : {
51 : : assert(hook);
52 : :
53 [ + + ]: 1380 : for (enum bf_hook i = 0; i < _BF_HOOK_MAX; ++i) {
54 [ + + ]: 1377 : if (bf_streq(_bf_hook_strs[i], str)) {
55 : 992 : *hook = i;
56 : 992 : return 0;
57 : : }
58 : : }
59 : :
60 : : return -EINVAL;
61 : : }
62 : :
63 : 255 : enum bf_flavor bf_hook_to_flavor(enum bf_hook hook)
64 : : {
65 : : static const enum bf_flavor flavors[] = {
66 : : [BF_HOOK_XDP] = BF_FLAVOR_XDP,
67 : : [BF_HOOK_TC_INGRESS] = BF_FLAVOR_TC,
68 : : [BF_HOOK_NF_PRE_ROUTING] = BF_FLAVOR_NF,
69 : : [BF_HOOK_NF_LOCAL_IN] = BF_FLAVOR_NF,
70 : : [BF_HOOK_CGROUP_INGRESS] = BF_FLAVOR_CGROUP,
71 : : [BF_HOOK_CGROUP_EGRESS] = BF_FLAVOR_CGROUP,
72 : : [BF_HOOK_NF_FORWARD] = BF_FLAVOR_NF,
73 : : [BF_HOOK_NF_LOCAL_OUT] = BF_FLAVOR_NF,
74 : : [BF_HOOK_NF_POST_ROUTING] = BF_FLAVOR_NF,
75 : : [BF_HOOK_TC_EGRESS] = BF_FLAVOR_TC,
76 : : };
77 : :
78 : : static_assert_enum_mapping(flavors, _BF_HOOK_MAX);
79 : :
80 : 255 : return flavors[hook];
81 : : }
82 : :
83 : 170 : enum bf_bpf_attach_type bf_hook_to_bpf_attach_type(enum bf_hook hook)
84 : : {
85 : : static const enum bf_bpf_attach_type attach_types[] = {
86 : : [BF_HOOK_XDP] = BF_BPF_XDP,
87 : : [BF_HOOK_TC_INGRESS] = BF_BPF_TCX_INGRESS,
88 : : [BF_HOOK_NF_PRE_ROUTING] = BF_BPF_NETFILTER,
89 : : [BF_HOOK_NF_LOCAL_IN] = BF_BPF_NETFILTER,
90 : : [BF_HOOK_CGROUP_INGRESS] = BF_BPF_CGROUP_INET_INGRESS,
91 : : [BF_HOOK_CGROUP_EGRESS] = BF_BPF_CGROUP_INET_EGRESS,
92 : : [BF_HOOK_NF_FORWARD] = BF_BPF_NETFILTER,
93 : : [BF_HOOK_NF_LOCAL_OUT] = BF_BPF_NETFILTER,
94 : : [BF_HOOK_NF_POST_ROUTING] = BF_BPF_NETFILTER,
95 : : [BF_HOOK_TC_EGRESS] = BF_BPF_TCX_ENGRESS,
96 : : };
97 : :
98 : : static_assert_enum_mapping(attach_types, _BF_HOOK_MAX);
99 : :
100 : 170 : return attach_types[hook];
101 : : }
102 : :
103 : 110 : enum bf_bpf_prog_type bf_hook_to_bpf_prog_type(enum bf_hook hook)
104 : : {
105 : : static const enum bf_bpf_prog_type prog_types[] = {
106 : : [BF_HOOK_XDP] = BF_BPF_PROG_TYPE_XDP,
107 : : [BF_HOOK_TC_INGRESS] = BF_BPF_PROG_TYPE_SCHED_CLS,
108 : : [BF_HOOK_NF_PRE_ROUTING] = BF_BPF_PROG_TYPE_NETFILTER,
109 : : [BF_HOOK_NF_LOCAL_IN] = BF_BPF_PROG_TYPE_NETFILTER,
110 : : [BF_HOOK_CGROUP_INGRESS] = BF_BPF_PROG_TYPE_CGROUP_SKB,
111 : : [BF_HOOK_CGROUP_EGRESS] = BF_BPF_PROG_TYPE_CGROUP_SKB,
112 : : [BF_HOOK_NF_FORWARD] = BF_BPF_PROG_TYPE_NETFILTER,
113 : : [BF_HOOK_NF_LOCAL_OUT] = BF_BPF_PROG_TYPE_NETFILTER,
114 : : [BF_HOOK_NF_POST_ROUTING] = BF_BPF_PROG_TYPE_NETFILTER,
115 : : [BF_HOOK_TC_EGRESS] = BF_BPF_PROG_TYPE_SCHED_CLS,
116 : : };
117 : :
118 : : static_assert_enum_mapping(prog_types, _BF_HOOK_MAX);
119 : :
120 : 110 : return prog_types[hook];
121 : : }
122 : :
123 : 29 : enum bf_nf_inet_hooks bf_hook_to_nf_hook(enum bf_hook hook)
124 : : {
125 [ + + + + : 29 : switch (hook) {
+ + ]
126 : : case BF_HOOK_NF_PRE_ROUTING:
127 : : return BF_NF_INET_PRE_ROUTING;
128 : 17 : case BF_HOOK_NF_LOCAL_IN:
129 : 17 : return BF_NF_INET_LOCAL_IN;
130 : 2 : case BF_HOOK_NF_FORWARD:
131 : 2 : return BF_NF_INET_FORWARD;
132 : 2 : case BF_HOOK_NF_LOCAL_OUT:
133 : 2 : return BF_NF_INET_LOCAL_OUT;
134 : 2 : case BF_HOOK_NF_POST_ROUTING:
135 : 2 : return BF_NF_INET_POST_ROUTING;
136 : 3 : default:
137 [ + - ]: 3 : bf_warn("bf_hook %s (%d) is not an bf_nf_inet_hooks value",
138 : : bf_hook_to_str(hook), hook);
139 : : return -EINVAL;
140 : : }
141 : : }
142 : :
143 [ + + ]: 12 : enum bf_hook bf_hook_from_nf_hook(enum bf_nf_inet_hooks hook)
144 : : {
145 : : switch (hook) {
146 : : case BF_NF_INET_PRE_ROUTING:
147 : : return BF_HOOK_NF_PRE_ROUTING;
148 : : case BF_NF_INET_LOCAL_IN:
149 : : return BF_HOOK_NF_LOCAL_IN;
150 : : case BF_NF_INET_FORWARD:
151 : : return BF_HOOK_NF_FORWARD;
152 : : case BF_NF_INET_LOCAL_OUT:
153 : : return BF_HOOK_NF_LOCAL_OUT;
154 : : case BF_NF_INET_POST_ROUTING:
155 : : return BF_HOOK_NF_POST_ROUTING;
156 : 2 : default:
157 [ + - ]: 2 : bf_warn("nf_inet_hooks %s (%d) is not a bf_hook value",
158 : : bf_nf_hook_to_str(hook), hook);
159 : : return -EINVAL;
160 : : }
161 : : }
162 : :
163 : 14 : const char *bf_nf_hook_to_str(enum bf_nf_inet_hooks hook)
164 : : {
165 [ + + + + : 14 : switch (hook) {
+ + ]
166 : : case BF_NF_INET_PRE_ROUTING:
167 : : return "nf_prerouting";
168 : 2 : case BF_NF_INET_LOCAL_IN:
169 : 2 : return "nf_input";
170 : 2 : case BF_NF_INET_FORWARD:
171 : 2 : return "nf_forward";
172 : 2 : case BF_NF_INET_LOCAL_OUT:
173 : 2 : return "nf_output";
174 : 2 : case BF_NF_INET_POST_ROUTING:
175 : 2 : return "nf_postrouting";
176 : 4 : default:
177 [ + - ]: 4 : bf_warn("unknown nf_inet_hooks value %d", hook);
178 : : return NULL;
179 : : }
180 : : }
181 : :
182 : 57 : static int _bf_hookopts_ifindex_parse(struct bf_hookopts *hookopts,
183 : : const char *raw_opt)
184 : : {
185 : : unsigned long ifindex;
186 : :
187 : : assert(hookopts);
188 : : assert(raw_opt);
189 : :
190 [ + + ]: 57 : if (hookopts->used_opts & BF_FLAG(BF_HOOKOPTS_IFINDEX))
191 [ + - ]: 2 : return bf_err_r(-EEXIST, "ifindex= is defined multiple times");
192 : :
193 : 55 : errno = 0;
194 : 55 : ifindex = strtoul(raw_opt, NULL, 0);
195 [ - + ]: 55 : if (errno != 0) {
196 [ # # ]: 0 : return bf_err_r(-errno, "failed to parse bf_hookopts type ifindex=%s",
197 : : raw_opt);
198 : : }
199 : :
200 [ - + ]: 55 : if (ifindex > INT_MAX)
201 [ # # ]: 0 : return bf_err_r(-E2BIG, "ifindex is too big: %lu", ifindex);
202 : :
203 : 55 : hookopts->ifindex = (int)ifindex;
204 : 55 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_IFINDEX);
205 : :
206 : 55 : return 0;
207 : : }
208 : :
209 : 11 : static void _bf_hookopts_ifindex_dump(const struct bf_hookopts *hookopts,
210 : : prefix_t *prefix)
211 : : {
212 : : assert(hookopts);
213 : : assert(prefix);
214 : :
215 [ + + ]: 11 : DUMP(prefix, "ifindex: %d", hookopts->ifindex);
216 : 11 : }
217 : :
218 : 12 : static int _bf_hookopts_cgpath_parse(struct bf_hookopts *hookopts,
219 : : const char *raw_opt)
220 : : {
221 : : assert(hookopts);
222 : : assert(raw_opt);
223 : :
224 [ + + ]: 12 : if (hookopts->used_opts & BF_FLAG(BF_HOOKOPTS_CGPATH))
225 [ + - ]: 1 : return bf_err_r(-EEXIST, "cgpath= is defined multiple times");
226 : :
227 : 11 : hookopts->cgpath = strdup(raw_opt);
228 [ - + ]: 11 : if (!hookopts->cgpath) {
229 [ # # ]: 0 : return bf_err_r(-ENOMEM, "failed to copy hook option cgpath=%s",
230 : : raw_opt);
231 : : }
232 : :
233 : 11 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_CGPATH);
234 : :
235 : 11 : return 0;
236 : : }
237 : :
238 : 4 : static void _bf_hookopts_cgpath_dump(const struct bf_hookopts *hookopts,
239 : : prefix_t *prefix)
240 : : {
241 : : assert(hookopts);
242 : : assert(prefix);
243 : :
244 [ + + ]: 4 : DUMP(prefix, "cgpath: %s", hookopts->cgpath);
245 : 4 : }
246 : :
247 : 20 : static int _bf_hookopts_family_parse(struct bf_hookopts *hookopts,
248 : : const char *raw_opt)
249 : : {
250 : : assert(hookopts);
251 : : assert(raw_opt);
252 : :
253 [ - + ]: 20 : if (hookopts->used_opts & BF_FLAG(BF_HOOKOPTS_FAMILY))
254 [ # # ]: 0 : return bf_err_r(-EEXIST, "family= is defined multiple times");
255 : :
256 [ + + + + ]: 20 : if (bf_streq("inet4", raw_opt) || bf_streq("inet6", raw_opt)) {
257 [ - + ]: 18 : bf_warn(
258 : : "family= hook option is deprecated for Netfilter chains, bpfilter will automatically filter on both the IPv4 and IPv6 families");
259 : : } else {
260 [ + - ]: 2 : return bf_err_r(-ENOTSUP, "unknown netfilter family '%s'", raw_opt);
261 : : }
262 : :
263 : : return 0;
264 : : }
265 : :
266 : 0 : static void _bf_hookopts_family_dump(const struct bf_hookopts *hookopts,
267 : : prefix_t *prefix)
268 : : {
269 : : assert(hookopts);
270 : : assert(prefix);
271 : :
272 : : (void)hookopts;
273 : :
274 [ # # ]: 0 : DUMP(prefix, "family: <deprecated>");
275 : 0 : }
276 : :
277 : 26 : static int _bf_hookopts_priorities_parse(struct bf_hookopts *hookopts,
278 : : const char *raw_opt)
279 : : {
280 : : unsigned long priorities[2];
281 : : _cleanup_free_ char *copy = NULL;
282 : : char *right, *end;
283 : :
284 : : assert(hookopts);
285 : : assert(raw_opt);
286 : :
287 [ + + ]: 26 : if (hookopts->used_opts & BF_FLAG(BF_HOOKOPTS_PRIORITIES))
288 [ + - ]: 1 : return bf_err_r(-EEXIST, "priorities= is defined multiple times");
289 : :
290 : 25 : copy = strdup(raw_opt);
291 [ + - ]: 25 : if (!copy)
292 : : return -ENOMEM;
293 : :
294 : 25 : end = copy + strlen(copy);
295 : :
296 : 25 : right = strchr(copy, '-');
297 [ + + ]: 25 : if (!right)
298 : 1 : goto err_parsing;
299 : :
300 : 24 : *right = '\0';
301 : 24 : ++right;
302 [ + + ]: 24 : if (end <= right)
303 : 1 : goto err_parsing;
304 : :
305 : 23 : errno = 0;
306 : 23 : priorities[0] = strtoul(copy, NULL, 0);
307 [ - + ]: 23 : if (errno != 0)
308 : 0 : goto err_parsing;
309 : :
310 : 23 : priorities[1] = strtoul(right, NULL, 0);
311 [ - + ]: 23 : if (errno != 0)
312 : 0 : goto err_parsing;
313 : :
314 [ + - - + ]: 23 : if (priorities[0] > INT_MAX || priorities[1] > INT_MAX)
315 [ # # ]: 0 : return bf_err_r(-EINVAL, "priorities can't be bigger than %d", INT_MAX);
316 : :
317 [ + + ]: 23 : if (priorities[0] == priorities[1])
318 [ + - ]: 2 : return bf_err_r(-EINVAL, "priorities must be different");
319 : :
320 [ + + + + ]: 21 : if (!priorities[0] || !priorities[1])
321 [ + - ]: 4 : return bf_err_r(-EINVAL, "priorities can't be 0");
322 : :
323 : 17 : hookopts->priorities[0] = (int)priorities[0];
324 : 17 : hookopts->priorities[1] = (int)priorities[1];
325 : 17 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_PRIORITIES);
326 : :
327 : 17 : return 0;
328 : :
329 : 2 : err_parsing:
330 [ - + ]: 2 : return bf_err_r(-EINVAL, "failed to parse '%s', expecting '$INT-$INT'",
331 : : raw_opt);
332 : : }
333 : :
334 : 4 : static void _bf_hookopts_priorities_dump(const struct bf_hookopts *hookopts,
335 : : prefix_t *prefix)
336 : : {
337 : : assert(hookopts);
338 : : assert(prefix);
339 : :
340 [ + + ]: 4 : DUMP(prefix, "priorities: %d, %d", hookopts->priorities[0],
341 : : hookopts->priorities[1]);
342 : 4 : }
343 : :
344 : : static struct bf_hookopts_ops
345 : : {
346 : : const char *name;
347 : : enum bf_hookopts_type type;
348 : : uint32_t supported_by;
349 : : uint32_t required_by;
350 : : int (*parse)(struct bf_hookopts *, const char *);
351 : : void (*dump)(const struct bf_hookopts *, prefix_t *);
352 : : } _bf_hookopts_ops[] = {
353 : : [BF_HOOKOPTS_IFINDEX] = {.name = "ifindex",
354 : : .type = BF_HOOKOPTS_IFINDEX,
355 : : .required_by =
356 : : BF_FLAGS(BF_FLAVOR_XDP, BF_FLAVOR_TC),
357 : : .supported_by = 0,
358 : : .parse = _bf_hookopts_ifindex_parse,
359 : : .dump = _bf_hookopts_ifindex_dump},
360 : : [BF_HOOKOPTS_CGPATH] = {.name = "cgpath",
361 : : .type = BF_HOOKOPTS_CGPATH,
362 : : .required_by = BF_FLAGS(BF_FLAVOR_CGROUP),
363 : : .supported_by = 0,
364 : : .parse = _bf_hookopts_cgpath_parse,
365 : : .dump = _bf_hookopts_cgpath_dump},
366 : : /** @deprecated Hook option `family=` is deprecated for Netfilter chains.
367 : : * bpfilter will automatically filter on both the IPv4 and IPv6 families. */
368 : : [BF_HOOKOPTS_FAMILY] = {.name = "family",
369 : : .type = BF_HOOKOPTS_FAMILY,
370 : : .required_by = 0,
371 : : .supported_by = 0,
372 : : .parse = _bf_hookopts_family_parse,
373 : : .dump = _bf_hookopts_family_dump},
374 : : [BF_HOOKOPTS_PRIORITIES] = {.name = "priorities",
375 : : .type = BF_HOOKOPTS_PRIORITIES,
376 : : .required_by = BF_FLAGS(BF_FLAVOR_NF),
377 : : .supported_by = 0,
378 : : .parse = _bf_hookopts_priorities_parse,
379 : : .dump = _bf_hookopts_priorities_dump},
380 : : };
381 : :
382 : : static_assert_enum_mapping(_bf_hookopts_ops, _BF_HOOKOPTS_MAX);
383 : :
384 : : #define _bf_hookopts_is_required(type, flavor) \
385 : : (_bf_hookopts_ops[type].required_by & BF_FLAG(flavor))
386 : :
387 : : #define _bf_hookopts_is_supported(type, flavor) \
388 : : ((_bf_hookopts_ops[type].supported_by & BF_FLAG(flavor)) || \
389 : : _bf_hookopts_is_required((type), (flavor)))
390 : :
391 : 117 : static struct bf_hookopts_ops *_bf_hookopts_get_ops(const char *key)
392 : : {
393 : : assert(key);
394 : :
395 [ + + ]: 255 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
396 [ + + ]: 253 : if (bf_streq(_bf_hookopts_ops[type].name, key))
397 : 115 : return &_bf_hookopts_ops[type];
398 : : }
399 : :
400 : : return NULL;
401 : : }
402 : :
403 : 173 : int bf_hookopts_new(struct bf_hookopts **hookopts)
404 : : {
405 : : assert(hookopts);
406 : :
407 : 173 : *hookopts = calloc(1, sizeof(struct bf_hookopts));
408 [ - + ]: 173 : if (!*hookopts)
409 [ # # ]: 0 : return bf_err_r(-ENOMEM, "failed to allocate a new bf_hookopts object");
410 : :
411 : : return 0;
412 : : }
413 : :
414 : 91 : int bf_hookopts_new_from_pack(struct bf_hookopts **hookopts,
415 : : bf_rpack_node_t node)
416 : : {
417 : 91 : _free_bf_hookopts_ struct bf_hookopts *_hookopts = NULL;
418 : : bf_rpack_node_t child;
419 : : int r;
420 : :
421 : : assert(hookopts);
422 : :
423 : 91 : r = bf_hookopts_new(&_hookopts);
424 [ - + ]: 91 : if (r)
425 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_hookopts from pack");
426 : :
427 : 91 : r = bf_rpack_kv_node(node, "ifindex", &child);
428 [ - + ]: 91 : if (r)
429 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.ifindex");
430 [ + + ]: 91 : if (!bf_rpack_is_nil(child)) {
431 : 77 : r = bf_rpack_int(child, &_hookopts->ifindex);
432 [ - + ]: 77 : if (r)
433 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopt.ifindex");
434 : :
435 : 77 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_IFINDEX);
436 : : }
437 : :
438 : 91 : r = bf_rpack_kv_node(node, "cgpath", &child);
439 [ - + ]: 91 : if (r)
440 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.cgpath");
441 [ + + ]: 91 : if (!bf_rpack_is_nil(child)) {
442 : 4 : r = bf_rpack_str(child, (char **)&_hookopts->cgpath);
443 [ - + ]: 4 : if (r)
444 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.cgpath");
445 : :
446 : 4 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_CGPATH);
447 : : }
448 : :
449 : 91 : r = bf_rpack_kv_node(node, "family", &child);
450 [ - + ]: 91 : if (r)
451 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.family");
452 [ - + ]: 91 : if (!bf_rpack_is_nil(child)) {
453 : 0 : r = bf_rpack_uint(child, &_hookopts->family);
454 [ # # ]: 0 : if (r)
455 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.family");
456 : :
457 : 0 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_FAMILY);
458 : : }
459 : :
460 : 91 : r = bf_rpack_kv_node(node, "priorities", &child);
461 [ - + ]: 91 : if (r)
462 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.priorities");
463 [ + + ]: 91 : if (!bf_rpack_is_nil(child)) {
464 : : bf_rpack_node_t child, p_node;
465 : :
466 : 11 : r = bf_rpack_kv_array(node, "priorities", &child);
467 [ - + ]: 11 : if (r)
468 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.priorities");
469 [ - + ]: 11 : if (bf_rpack_array_count(child) != 2) {
470 [ # # ]: 0 : return bf_err_r(
471 : : -EINVAL, "bf_hookopts.priorities pack expects only 2 values");
472 : : }
473 : :
474 [ + - + + : 66 : bf_rpack_array_foreach (child, p_node) {
+ + ]
475 : 22 : r = bf_rpack_int(p_node, &_hookopts->priorities[i]);
476 [ - + ]: 22 : if (r) {
477 [ # # ]: 0 : return bf_rpack_key_err(
478 : : r, "failed to unpack bf_hookopts.priorities value");
479 : : }
480 : : }
481 : :
482 : 11 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_PRIORITIES);
483 : : }
484 : :
485 : 91 : *hookopts = TAKE_PTR(_hookopts);
486 : :
487 : 91 : return 0;
488 : : }
489 : :
490 : 1349 : void bf_hookopts_clean(struct bf_hookopts *hookopts)
491 : : {
492 : : freep((void *)&hookopts->cgpath);
493 : 1349 : }
494 : :
495 : 1877 : void bf_hookopts_free(struct bf_hookopts **hookopts)
496 : : {
497 : : assert(hookopts);
498 : :
499 [ + + ]: 1877 : if (!*hookopts)
500 : : return;
501 : :
502 : 173 : bf_hookopts_clean(*hookopts);
503 : : freep((void *)hookopts);
504 : : }
505 : :
506 : 177 : int bf_hookopts_pack(const struct bf_hookopts *hookopts, bf_wpack_t *pack)
507 : : {
508 : : assert(hookopts);
509 : : assert(pack);
510 : :
511 [ + + ]: 177 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_IFINDEX))
512 : 142 : bf_wpack_kv_int(pack, "ifindex", hookopts->ifindex);
513 : : else
514 : : bf_wpack_kv_nil(pack, "ifindex");
515 : :
516 [ + + ]: 177 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_CGPATH))
517 : 8 : bf_wpack_kv_str(pack, "cgpath", hookopts->cgpath);
518 : : else
519 : : bf_wpack_kv_nil(pack, "cgpath");
520 : :
521 [ - + ]: 177 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_FAMILY))
522 : 0 : bf_wpack_kv_uint(pack, "family", hookopts->family);
523 : : else
524 : : bf_wpack_kv_nil(pack, "family");
525 : :
526 [ + + ]: 177 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_PRIORITIES)) {
527 : 27 : bf_wpack_open_array(pack, "priorities");
528 : 27 : bf_wpack_int(pack, hookopts->priorities[0]);
529 : 27 : bf_wpack_int(pack, hookopts->priorities[1]);
530 : 27 : bf_wpack_close_array(pack);
531 : : } else {
532 : : bf_wpack_kv_nil(pack, "priorities");
533 : : }
534 : :
535 [ - + ]: 177 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
536 : : }
537 : :
538 : 18 : void bf_hookopts_dump(const struct bf_hookopts *hookopts, prefix_t *prefix)
539 : : {
540 : : assert(hookopts);
541 : : assert(prefix);
542 : :
543 [ + + ]: 18 : DUMP(prefix, "struct bf_hookopts at %p", hookopts);
544 : :
545 : 18 : bf_dump_prefix_push(prefix);
546 [ + + ]: 18 : DUMP(prefix, "used_opts: 0x%08x", hookopts->used_opts);
547 : :
548 [ + + ]: 90 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
549 [ + + ]: 72 : if (type == _BF_HOOKOPTS_MAX - 1)
550 : 18 : bf_dump_prefix_last(prefix);
551 : :
552 [ + + ]: 72 : if (bf_hookopts_is_used(hookopts, type))
553 : 19 : _bf_hookopts_ops[type].dump(hookopts, prefix);
554 : : else
555 [ + + ]: 53 : DUMP(prefix, "%s: <unset>", _bf_hookopts_ops[type].name);
556 : : }
557 : :
558 : 18 : bf_dump_prefix_pop(prefix);
559 : 18 : }
560 : :
561 : 119 : int bf_hookopts_parse_opt(struct bf_hookopts *hookopts, const char *raw_opt)
562 : : {
563 : : char *value;
564 : : struct bf_hookopts_ops *ops;
565 : : int r;
566 : :
567 : : assert(hookopts);
568 : : assert(raw_opt);
569 : :
570 : 119 : value = strchr(raw_opt, '=');
571 [ + + ]: 119 : if (!value)
572 : : return -ENOENT;
573 : :
574 : 117 : *value = '\0';
575 : 117 : ++value;
576 : :
577 : 117 : ops = _bf_hookopts_get_ops(raw_opt);
578 [ + + ]: 117 : if (!ops) {
579 [ + - ]: 2 : return bf_err_r(-ENOTSUP, "unknown hook option '%s', ignoring",
580 : : raw_opt);
581 : : }
582 : :
583 : 115 : r = ops->parse(hookopts, value);
584 : 115 : if (r < 0)
585 : : return r;
586 : :
587 : : return 0;
588 : : }
589 : :
590 : 2 : int bf_hookopts_parse_opts(struct bf_hookopts *hookopts, bf_list *raw_opts)
591 : : {
592 : : int r;
593 : :
594 : : assert(hookopts);
595 : : assert(raw_opts);
596 : :
597 [ - + ]: 2 : if (!raw_opts)
598 : : return 0;
599 : :
600 [ + + + + : 10 : bf_list_foreach (raw_opts, raw_opt_node) {
+ + ]
601 : 3 : r = bf_hookopts_parse_opt(hookopts,
602 : : bf_list_node_get_data(raw_opt_node));
603 [ + - ]: 3 : if (r)
604 : : return r;
605 : : }
606 : :
607 : : return 0;
608 : : }
609 : :
610 : 76 : int bf_hookopts_validate(const struct bf_hookopts *hookopts, enum bf_hook hook)
611 : : {
612 : 76 : enum bf_flavor flavor = bf_hook_to_flavor(hook);
613 : :
614 : : assert(hookopts);
615 : :
616 [ + + ]: 348 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
617 : : struct bf_hookopts_ops *ops = &_bf_hookopts_ops[type];
618 : 283 : bool is_used = bf_hookopts_is_used(hookopts, type);
619 : 283 : bool is_required = _bf_hookopts_is_required(type, flavor);
620 [ + - + + ]: 283 : bool is_supported = _bf_hookopts_is_supported(type, flavor);
621 : :
622 [ + + ]: 283 : if (is_required && !is_used) {
623 [ + - ]: 9 : return bf_err_r(-EINVAL,
624 : : "hook option '%s' is required for '%s' chains",
625 : : ops->name, bf_hook_to_str(hook));
626 : : }
627 : :
628 [ + + + + ]: 274 : if (is_used && !(is_supported | is_required)) {
629 [ + - ]: 2 : return bf_err_r(-ENOTSUP,
630 : : "hook option '%s' is not supported for '%s' chains",
631 : : ops->name, bf_hook_to_str(hook));
632 : : }
633 : : }
634 : :
635 : : return 0;
636 : : }
|