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