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 : 81 : const char *bf_hook_to_str(enum bf_hook hook)
43 : : {
44 : 81 : return _bf_hook_strs[hook];
45 : : }
46 : :
47 : 900 : enum bf_hook bf_hook_from_str(const char *str)
48 : : {
49 : : bf_assert(str);
50 : :
51 [ + + ]: 1190 : for (enum bf_hook hook = 0; hook < _BF_HOOK_MAX; ++hook) {
52 [ + + ]: 1187 : if (bf_streq(_bf_hook_strs[hook], str))
53 : : return hook;
54 : : }
55 : :
56 : : return -EINVAL;
57 : : }
58 : :
59 : 154 : 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 : 154 : return flavors[hook];
78 : : }
79 : :
80 : 101 : 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 : 101 : return attach_types[hook];
99 : : }
100 : :
101 : 73 : 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 : 73 : return prog_types[hook];
120 : : }
121 : :
122 : 20 : enum bf_nf_inet_hooks bf_hook_to_nf_hook(enum bf_hook hook)
123 : : {
124 [ + + + + : 20 : switch (hook) {
+ + ]
125 : : case BF_HOOK_NF_PRE_ROUTING:
126 : : return BF_NF_INET_PRE_ROUTING;
127 : 8 : case BF_HOOK_NF_LOCAL_IN:
128 : 8 : 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 : 29 : 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 : 29 : errno = 0;
189 : 29 : ifindex = strtoul(raw_opt, NULL, 0);
190 [ - + ]: 29 : if (errno != 0) {
191 [ # # ]: 0 : return bf_err_r(-errno, "failed to parse bf_hookopts type ifindex=%s",
192 : : raw_opt);
193 : : }
194 : :
195 [ - + ]: 29 : if (ifindex > INT_MAX)
196 [ # # ]: 0 : return bf_err_r(-E2BIG, "ifindex is too big: %lu", ifindex);
197 : :
198 : 29 : hookopts->ifindex = (int)ifindex;
199 : 29 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_IFINDEX);
200 : :
201 : 29 : return 0;
202 : : }
203 : :
204 : 8 : static void _bf_hookopts_ifindex_dump(const struct bf_hookopts *hookopts,
205 : : prefix_t *prefix)
206 : : {
207 : : bf_assert(hookopts && prefix);
208 : :
209 [ + + ]: 8 : DUMP(prefix, "ifindex: %d", hookopts->ifindex);
210 : 8 : }
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 [ + + ]: 17 : if (bf_streq("inet4", raw_opt))
242 : 14 : hookopts->family = PF_INET;
243 [ + + ]: 3 : else if (bf_streq("inet6", raw_opt))
244 : 1 : hookopts->family = PF_INET6;
245 : : else
246 [ + - ]: 2 : return bf_err_r(-ENOTSUP, "unknown netfilter family '%s'", raw_opt);
247 : :
248 : 15 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_FAMILY);
249 : :
250 : 15 : return 0;
251 : : }
252 : :
253 : 4 : static void _bf_hookopts_family_dump(const struct bf_hookopts *hookopts,
254 : : prefix_t *prefix)
255 : : {
256 : : bf_assert(hookopts && prefix);
257 : :
258 [ + + - + ]: 4 : DUMP(prefix, "family: %s", hookopts->family == PF_INET ? "inet4" : "inet6");
259 : 4 : }
260 : :
261 : 22 : static int _bf_hookopts_priorities_parse(struct bf_hookopts *hookopts,
262 : : const char *raw_opt)
263 : : {
264 : : unsigned long priorities[2];
265 : : _cleanup_free_ char *copy = NULL;
266 : : char *right, *end;
267 : :
268 : : bf_assert(hookopts && raw_opt);
269 : :
270 : 22 : copy = strdup(raw_opt);
271 [ + - ]: 22 : if (!copy)
272 : : return -ENOMEM;
273 : :
274 : 22 : end = copy + strlen(copy);
275 : :
276 : 22 : right = strchr(copy, '-');
277 [ + + ]: 22 : if (!right)
278 : 1 : goto err_parsing;
279 : :
280 : 21 : *right = '\0';
281 : 21 : ++right;
282 [ + + ]: 21 : if (end <= right)
283 : 1 : goto err_parsing;
284 : :
285 : 20 : errno = 0;
286 : 20 : priorities[0] = strtoul(copy, NULL, 0);
287 [ - + ]: 20 : if (errno != 0)
288 : 0 : goto err_parsing;
289 : :
290 : 20 : priorities[1] = strtoul(right, NULL, 0);
291 [ - + ]: 20 : if (errno != 0)
292 : 0 : goto err_parsing;
293 : :
294 [ + - - + ]: 20 : if (priorities[0] > INT_MAX || priorities[1] > INT_MAX)
295 [ # # ]: 0 : return bf_err_r(-EINVAL, "priorities can't be bigger than %d", INT_MAX);
296 : :
297 [ + + ]: 20 : if (priorities[0] == priorities[1])
298 [ + - ]: 2 : return bf_err_r(-EINVAL, "priorities must be different");
299 : :
300 [ + + + + ]: 18 : if (!priorities[0] || !priorities[1])
301 [ + - ]: 4 : return bf_err_r(-EINVAL, "priorities can't be 0");
302 : :
303 : 14 : hookopts->priorities[0] = (int)priorities[0];
304 : 14 : hookopts->priorities[1] = (int)priorities[1];
305 : 14 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_PRIORITIES);
306 : :
307 : 14 : return 0;
308 : :
309 : 2 : err_parsing:
310 [ - + ]: 2 : return bf_err_r(-EINVAL, "failed to parse '%s', expecting '$INT-$INT'",
311 : : raw_opt);
312 : : }
313 : :
314 : 4 : static void _bf_hookopts_priorities_dump(const struct bf_hookopts *hookopts,
315 : : prefix_t *prefix)
316 : : {
317 : : bf_assert(hookopts && prefix);
318 : :
319 [ + + ]: 4 : DUMP(prefix, "priorities: %d, %d", hookopts->priorities[0],
320 : : hookopts->priorities[1]);
321 : 4 : }
322 : :
323 : : static struct bf_hookopts_ops
324 : : {
325 : : const char *name;
326 : : enum bf_hookopts_type type;
327 : : uint32_t supported_by;
328 : : uint32_t required_by;
329 : : int (*parse)(struct bf_hookopts *, const char *);
330 : : void (*dump)(const struct bf_hookopts *, prefix_t *);
331 : : } _bf_hookopts_ops[] = {
332 : : [BF_HOOKOPTS_IFINDEX] = {.name = "ifindex",
333 : : .type = BF_HOOKOPTS_IFINDEX,
334 : : .required_by =
335 : : BF_FLAGS(BF_FLAVOR_XDP, BF_FLAVOR_TC),
336 : : .supported_by = 0,
337 : : .parse = _bf_hookopts_ifindex_parse,
338 : : .dump = _bf_hookopts_ifindex_dump},
339 : : [BF_HOOKOPTS_CGPATH] = {.name = "cgpath",
340 : : .type = BF_HOOKOPTS_CGPATH,
341 : : .required_by = BF_FLAGS(BF_FLAVOR_CGROUP),
342 : : .supported_by = 0,
343 : : .parse = _bf_hookopts_cgpath_parse,
344 : : .dump = _bf_hookopts_cgpath_dump},
345 : : [BF_HOOKOPTS_FAMILY] = {.name = "family",
346 : : .type = BF_HOOKOPTS_FAMILY,
347 : : .required_by = BF_FLAGS(BF_FLAVOR_NF),
348 : : .supported_by = 0,
349 : : .parse = _bf_hookopts_family_parse,
350 : : .dump = _bf_hookopts_family_dump},
351 : : [BF_HOOKOPTS_PRIORITIES] = {.name = "priorities",
352 : : .type = BF_HOOKOPTS_PRIORITIES,
353 : : .required_by = BF_FLAGS(BF_FLAVOR_NF),
354 : : .supported_by = 0,
355 : : .parse = _bf_hookopts_priorities_parse,
356 : : .dump = _bf_hookopts_priorities_dump},
357 : : };
358 : :
359 : : static_assert(ARRAY_SIZE(_bf_hookopts_ops) == _BF_HOOKOPTS_MAX,
360 : : "missing entries in bf_hookopts_ops array");
361 : :
362 : : #define _bf_hookopts_is_required(type, flavor) \
363 : : (_bf_hookopts_ops[type].required_by & BF_FLAG(flavor))
364 : :
365 : : #define _bf_hookopts_is_supported(type, flavor) \
366 : : ((_bf_hookopts_ops[type].supported_by & BF_FLAG(flavor)) || \
367 : : _bf_hookopts_is_required((type), (flavor)))
368 : :
369 : 79 : static struct bf_hookopts_ops *_bf_hookopts_get_ops(const char *key)
370 : : {
371 : : bf_assert(key);
372 : :
373 [ + + ]: 196 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
374 [ + + ]: 194 : if (bf_streq(_bf_hookopts_ops[type].name, key))
375 : 77 : return &_bf_hookopts_ops[type];
376 : : }
377 : :
378 : : return NULL;
379 : : }
380 : :
381 : 87 : int bf_hookopts_new(struct bf_hookopts **hookopts)
382 : : {
383 : : bf_assert(hookopts);
384 : :
385 : 87 : *hookopts = calloc(1, sizeof(struct bf_hookopts));
386 [ - + ]: 87 : if (!*hookopts)
387 [ # # ]: 0 : return bf_err_r(-ENOMEM, "failed to allocate a new bf_hookopts object");
388 : :
389 : : return 0;
390 : : }
391 : :
392 : 37 : int bf_hookopts_new_from_pack(struct bf_hookopts **hookopts,
393 : : bf_rpack_node_t node)
394 : : {
395 : 37 : _free_bf_hookopts_ struct bf_hookopts *_hookopts = NULL;
396 : : bf_rpack_node_t child;
397 : : int r;
398 : :
399 : : bf_assert(hookopts);
400 : :
401 : 37 : r = bf_hookopts_new(&_hookopts);
402 [ - + ]: 37 : if (r)
403 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_hookopts from pack");
404 : :
405 : 37 : r = bf_rpack_kv_node(node, "ifindex", &child);
406 [ - + ]: 37 : if (r)
407 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.ifindex");
408 [ + + ]: 37 : if (!bf_rpack_is_nil(child)) {
409 : 26 : r = bf_rpack_int(child, &_hookopts->ifindex);
410 [ - + ]: 26 : if (r)
411 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopt.ifindex");
412 : :
413 : 26 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_IFINDEX);
414 : : }
415 : :
416 : 37 : r = bf_rpack_kv_node(node, "cgpath", &child);
417 [ - + ]: 37 : if (r)
418 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.cgpath");
419 [ + + ]: 37 : if (!bf_rpack_is_nil(child)) {
420 : 3 : r = bf_rpack_str(child, (char **)&_hookopts->cgpath);
421 [ - + ]: 3 : if (r)
422 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.cgpath");
423 : :
424 : 3 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_CGPATH);
425 : : }
426 : :
427 : 37 : r = bf_rpack_kv_node(node, "family", &child);
428 [ - + ]: 37 : if (r)
429 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.family");
430 [ + + ]: 37 : if (!bf_rpack_is_nil(child)) {
431 : 9 : r = bf_rpack_uint(child, &_hookopts->family);
432 [ - + ]: 9 : if (r)
433 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.family");
434 : :
435 : 9 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_FAMILY);
436 : : }
437 : :
438 : 37 : r = bf_rpack_kv_node(node, "priorities", &child);
439 [ - + ]: 37 : if (r)
440 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.priorities");
441 [ + + ]: 37 : if (!bf_rpack_is_nil(child)) {
442 : : bf_rpack_node_t child, p_node;
443 : :
444 : 9 : r = bf_rpack_kv_array(node, "priorities", &child);
445 [ - + ]: 9 : if (r)
446 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_hookopts.priorities");
447 [ - + ]: 9 : if (bf_rpack_array_count(child) != 2) {
448 [ # # ]: 0 : return bf_err_r(
449 : : -EINVAL, "bf_hookopts.priorities pack expects only 2 values");
450 : : }
451 : :
452 [ + - + + : 54 : bf_rpack_array_foreach (child, p_node) {
+ + ]
453 : 18 : r = bf_rpack_int(p_node, &_hookopts->priorities[i]);
454 [ - + ]: 18 : if (r) {
455 [ # # ]: 0 : return bf_rpack_key_err(
456 : : r, "failed to unpack bf_hookopts.priorities value");
457 : : }
458 : : }
459 : :
460 : 9 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_PRIORITIES);
461 : : }
462 : :
463 : 37 : *hookopts = TAKE_PTR(_hookopts);
464 : :
465 : 37 : return 0;
466 : : }
467 : :
468 : 1005 : void bf_hookopts_clean(struct bf_hookopts *hookopts)
469 : : {
470 : : freep((void *)&hookopts->cgpath);
471 : 1005 : }
472 : :
473 : 1586 : void bf_hookopts_free(struct bf_hookopts **hookopts)
474 : : {
475 : : bf_assert(hookopts);
476 : :
477 [ + + ]: 1586 : if (!*hookopts)
478 : : return;
479 : :
480 : 87 : bf_hookopts_clean(*hookopts);
481 : : freep((void *)hookopts);
482 : : }
483 : :
484 : 83 : int bf_hookopts_pack(const struct bf_hookopts *hookopts, bf_wpack_t *pack)
485 : : {
486 : : bf_assert(hookopts);
487 : : bf_assert(pack);
488 : :
489 [ + + ]: 83 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_IFINDEX))
490 : 53 : bf_wpack_kv_int(pack, "ifindex", hookopts->ifindex);
491 : : else
492 : : bf_wpack_kv_nil(pack, "ifindex");
493 : :
494 [ + + ]: 83 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_CGPATH))
495 : 7 : bf_wpack_kv_str(pack, "cgpath", hookopts->cgpath);
496 : : else
497 : : bf_wpack_kv_nil(pack, "cgpath");
498 : :
499 [ + + ]: 83 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_FAMILY))
500 : 23 : bf_wpack_kv_uint(pack, "family", hookopts->family);
501 : : else
502 : : bf_wpack_kv_nil(pack, "family");
503 : :
504 [ + + ]: 83 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_PRIORITIES)) {
505 : 23 : bf_wpack_open_array(pack, "priorities");
506 : 23 : bf_wpack_int(pack, hookopts->priorities[0]);
507 : 23 : bf_wpack_int(pack, hookopts->priorities[1]);
508 : 23 : bf_wpack_close_array(pack);
509 : : } else {
510 : : bf_wpack_kv_nil(pack, "priorities");
511 : : }
512 : :
513 [ - + ]: 83 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
514 : : }
515 : :
516 : 15 : void bf_hookopts_dump(const struct bf_hookopts *hookopts, prefix_t *prefix)
517 : : {
518 : : bf_assert(hookopts && prefix);
519 : :
520 [ + + ]: 15 : DUMP(prefix, "struct bf_hookopts at %p", hookopts);
521 : :
522 : 15 : bf_dump_prefix_push(prefix);
523 [ + + ]: 15 : DUMP(prefix, "used_opts: 0x%08x", hookopts->used_opts);
524 : :
525 [ + + ]: 75 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
526 [ + + ]: 60 : if (type == _BF_HOOKOPTS_MAX - 1)
527 : 15 : bf_dump_prefix_last(prefix);
528 : :
529 [ + + ]: 60 : if (bf_hookopts_is_used(hookopts, type))
530 : 20 : _bf_hookopts_ops[type].dump(hookopts, prefix);
531 : : else
532 [ + + ]: 40 : DUMP(prefix, "%s: <unset>", _bf_hookopts_ops[type].name);
533 : : }
534 : :
535 : 15 : bf_dump_prefix_pop(prefix);
536 : 15 : }
537 : :
538 : 81 : int bf_hookopts_parse_opt(struct bf_hookopts *hookopts, const char *raw_opt)
539 : : {
540 : : char *value;
541 : : struct bf_hookopts_ops *ops;
542 : : int r;
543 : :
544 : : bf_assert(hookopts && raw_opt);
545 : :
546 : 81 : value = strchr(raw_opt, '=');
547 [ + + ]: 81 : if (!value)
548 : : return -ENOENT;
549 : :
550 : 79 : *value = '\0';
551 : 79 : ++value;
552 : :
553 : 79 : ops = _bf_hookopts_get_ops(raw_opt);
554 [ + + ]: 79 : if (!ops) {
555 [ + - ]: 2 : return bf_err_r(-ENOTSUP, "unknown hook option '%s', ignoring",
556 : : raw_opt);
557 : : }
558 : :
559 : 77 : r = ops->parse(hookopts, value);
560 : 77 : if (r < 0)
561 : : return r;
562 : :
563 : : return 0;
564 : : }
565 : :
566 : 2 : int bf_hookopts_parse_opts(struct bf_hookopts *hookopts, bf_list *raw_opts)
567 : : {
568 : : int r;
569 : :
570 : : bf_assert(hookopts && raw_opts);
571 : :
572 [ - + ]: 2 : if (!raw_opts)
573 : : return 0;
574 : :
575 [ + + + + : 10 : bf_list_foreach (raw_opts, raw_opt_node) {
+ + ]
576 : 3 : r = bf_hookopts_parse_opt(hookopts,
577 : : bf_list_node_get_data(raw_opt_node));
578 [ + - ]: 3 : if (r)
579 : : return r;
580 : : }
581 : :
582 : : return 0;
583 : : }
584 : :
585 : 47 : int bf_hookopts_validate(const struct bf_hookopts *hookopts, enum bf_hook hook)
586 : : {
587 : 47 : enum bf_flavor flavor = bf_hook_to_flavor(hook);
588 : :
589 : : bf_assert(hookopts);
590 : :
591 [ + + ]: 203 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
592 : : struct bf_hookopts_ops *ops = &_bf_hookopts_ops[type];
593 : 166 : bool is_used = bf_hookopts_is_used(hookopts, type);
594 : 166 : bool is_required = _bf_hookopts_is_required(type, flavor);
595 [ + - + + ]: 166 : bool is_supported = _bf_hookopts_is_supported(type, flavor);
596 : :
597 [ + + ]: 166 : if (is_required && !is_used) {
598 [ + - ]: 8 : return bf_err_r(-EINVAL,
599 : : "hook option '%s' is required for '%s' chains",
600 : : ops->name, bf_hook_to_str(hook));
601 : : }
602 : :
603 [ + + + + ]: 158 : if (is_used && !(is_supported | is_required)) {
604 [ + - ]: 2 : return bf_err_r(-ENOTSUP,
605 : : "hook option '%s' is not supported for '%s' chains",
606 : : ops->name, bf_hook_to_str(hook));
607 : : }
608 : : }
609 : :
610 : : return 0;
611 : : }
|