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