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 12 : const char *bf_hook_to_str(enum bf_hook hook)
43 : {
44 12 : return _bf_hook_strs[hook];
45 : }
46 :
47 4 : enum bf_hook bf_hook_from_str(const char *str)
48 : {
49 4 : bf_assert(str);
50 :
51 33 : for (enum bf_hook hook = 0; hook < _BF_HOOK_MAX; ++hook) {
52 30 : if (bf_streq(_bf_hook_strs[hook], str))
53 : return hook;
54 : }
55 :
56 : return -EINVAL;
57 : }
58 :
59 13 : 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 13 : return flavors[hook];
78 : }
79 :
80 10 : 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 10 : return attach_types[hook];
99 : }
100 :
101 10 : 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 10 : return prog_types[hook];
120 : }
121 :
122 7 : enum bf_nf_inet_hooks bf_hook_to_nf_hook(enum bf_hook hook)
123 : {
124 7 : switch (hook) {
125 : case BF_HOOK_NF_PRE_ROUTING:
126 : return BF_NF_INET_PRE_ROUTING;
127 1 : case BF_HOOK_NF_LOCAL_IN:
128 1 : return BF_NF_INET_LOCAL_IN;
129 1 : case BF_HOOK_NF_FORWARD:
130 1 : return BF_NF_INET_FORWARD;
131 1 : case BF_HOOK_NF_LOCAL_OUT:
132 1 : return BF_NF_INET_LOCAL_OUT;
133 1 : case BF_HOOK_NF_POST_ROUTING:
134 1 : return BF_NF_INET_POST_ROUTING;
135 2 : default:
136 2 : 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 6 : 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 1 : default:
156 1 : 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 6 : const char *bf_nf_hook_to_str(enum bf_nf_inet_hooks hook)
163 : {
164 6 : switch (hook) {
165 : case BF_NF_INET_PRE_ROUTING:
166 : return "nf_prerouting";
167 1 : case BF_NF_INET_LOCAL_IN:
168 1 : return "nf_input";
169 1 : case BF_NF_INET_FORWARD:
170 1 : return "nf_forward";
171 1 : case BF_NF_INET_LOCAL_OUT:
172 1 : return "nf_output";
173 1 : case BF_NF_INET_POST_ROUTING:
174 1 : return "nf_postrouting";
175 1 : default:
176 1 : bf_warn("unknown nf_inet_hooks value %d", hook);
177 : return NULL;
178 : }
179 : }
180 :
181 0 : static int _bf_hookopts_ifindex_parse(struct bf_hookopts *hookopts,
182 : const char *raw_opt)
183 : {
184 : unsigned long ifindex;
185 :
186 0 : bf_assert(hookopts && raw_opt);
187 :
188 0 : errno = 0;
189 0 : ifindex = strtoul(raw_opt, NULL, 0);
190 0 : if (errno != 0) {
191 0 : return bf_err_r(-errno, "failed to parse bf_hookopts type ifindex=%s",
192 : raw_opt);
193 : }
194 :
195 0 : if (ifindex > INT_MAX)
196 0 : return bf_err_r(-E2BIG, "ifindex is too big: %lu", ifindex);
197 :
198 0 : hookopts->ifindex = (int)ifindex;
199 0 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_IFINDEX);
200 :
201 0 : return 0;
202 : }
203 :
204 0 : static void _bf_hookopts_ifindex_dump(const struct bf_hookopts *hookopts,
205 : prefix_t *prefix)
206 : {
207 0 : bf_assert(hookopts && prefix);
208 :
209 0 : DUMP(prefix, "ifindex: %d", hookopts->ifindex);
210 0 : }
211 :
212 0 : static int _bf_hookopts_cgpath_parse(struct bf_hookopts *hookopts,
213 : const char *raw_opt)
214 : {
215 0 : bf_assert(hookopts && raw_opt);
216 :
217 0 : hookopts->cgpath = strdup(raw_opt);
218 0 : if (!hookopts->cgpath) {
219 0 : return bf_err_r(-ENOMEM, "failed to copy hook option cgpath=%s",
220 : raw_opt);
221 : }
222 :
223 0 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_CGPATH);
224 :
225 0 : return 0;
226 : }
227 :
228 0 : static void _bf_hookopts_cgpath_dump(const struct bf_hookopts *hookopts,
229 : prefix_t *prefix)
230 : {
231 0 : bf_assert(hookopts && prefix);
232 :
233 0 : DUMP(prefix, "cgpath: %s", hookopts->cgpath);
234 0 : }
235 :
236 7 : static int _bf_hookopts_family_parse(struct bf_hookopts *hookopts,
237 : const char *raw_opt)
238 : {
239 7 : bf_assert(hookopts && raw_opt);
240 :
241 4 : if (bf_streq("inet4", raw_opt))
242 1 : 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 2 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_FAMILY);
249 :
250 2 : return 0;
251 : }
252 :
253 0 : static void _bf_hookopts_family_dump(const struct bf_hookopts *hookopts,
254 : prefix_t *prefix)
255 : {
256 0 : bf_assert(hookopts && prefix);
257 :
258 0 : DUMP(prefix, "family: %s", hookopts->family == PF_INET ? "inet4" : "inet6");
259 0 : }
260 :
261 14 : 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 14 : bf_assert(hookopts && raw_opt);
269 :
270 11 : copy = strdup(raw_opt);
271 11 : if (!copy)
272 : return -ENOMEM;
273 :
274 11 : end = copy + strlen(copy);
275 :
276 11 : right = strchr(copy, '-');
277 11 : if (!right)
278 1 : goto err_parsing;
279 :
280 10 : *right = '\0';
281 10 : ++right;
282 10 : if (end <= right)
283 1 : goto err_parsing;
284 :
285 9 : errno = 0;
286 9 : priorities[0] = strtoul(copy, NULL, 0);
287 9 : if (errno != 0)
288 0 : goto err_parsing;
289 :
290 9 : priorities[1] = strtoul(right, NULL, 0);
291 9 : if (errno != 0)
292 0 : goto err_parsing;
293 :
294 9 : 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 9 : if (priorities[0] == priorities[1])
298 1 : return bf_err_r(-EINVAL, "priorities must be different");
299 :
300 8 : if (!priorities[0] || !priorities[1])
301 5 : return bf_err_r(-EINVAL, "priorities can't be 0");
302 :
303 3 : hookopts->priorities[0] = (int)priorities[0];
304 3 : hookopts->priorities[1] = (int)priorities[1];
305 3 : hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_PRIORITIES);
306 :
307 3 : 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 0 : static void _bf_hookopts_priorities_dump(const struct bf_hookopts *hookopts,
315 : prefix_t *prefix)
316 : {
317 0 : bf_assert(hookopts && prefix);
318 :
319 0 : DUMP(prefix, "priorities: %d, %d", hookopts->priorities[0],
320 : hookopts->priorities[1]);
321 0 : }
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 0 : static struct bf_hookopts_ops *_bf_hookopts_get_ops(const char *key)
370 : {
371 0 : bf_assert(key);
372 :
373 0 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
374 0 : if (bf_streq(_bf_hookopts_ops[type].name, key))
375 0 : return &_bf_hookopts_ops[type];
376 : }
377 :
378 : return NULL;
379 : }
380 :
381 0 : int bf_hookopts_new(struct bf_hookopts **hookopts)
382 : {
383 0 : bf_assert(hookopts);
384 :
385 0 : *hookopts = calloc(1, sizeof(struct bf_hookopts));
386 0 : if (!*hookopts)
387 0 : return bf_err_r(-ENOMEM, "failed to allocate a new bf_hookopts object");
388 :
389 : return 0;
390 : }
391 :
392 0 : int bf_hookopts_new_from_pack(struct bf_hookopts **hookopts,
393 : bf_rpack_node_t node)
394 : {
395 0 : _free_bf_hookopts_ struct bf_hookopts *_hookopts = NULL;
396 : bf_rpack_node_t child;
397 : int r;
398 :
399 0 : bf_assert(hookopts);
400 :
401 0 : r = bf_hookopts_new(&_hookopts);
402 0 : if (r)
403 0 : return bf_err_r(r, "failed to create bf_hookopts from pack");
404 :
405 0 : r = bf_rpack_kv_node(node, "ifindex", &child);
406 0 : if (r)
407 0 : return bf_rpack_key_err(r, "bf_hookopts.ifindex");
408 0 : if (!bf_rpack_is_nil(child)) {
409 0 : r = bf_rpack_int(child, &_hookopts->ifindex);
410 0 : if (r)
411 0 : return bf_rpack_key_err(r, "bf_hookopt.ifindex");
412 :
413 0 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_IFINDEX);
414 : }
415 :
416 0 : r = bf_rpack_kv_node(node, "cgpath", &child);
417 0 : if (r)
418 0 : return bf_rpack_key_err(r, "bf_hookopts.cgpath");
419 0 : if (!bf_rpack_is_nil(child)) {
420 0 : r = bf_rpack_str(child, (char **)&_hookopts->cgpath);
421 0 : if (r)
422 0 : return bf_rpack_key_err(r, "bf_hookopts.cgpath");
423 :
424 0 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_CGPATH);
425 : }
426 :
427 0 : r = bf_rpack_kv_node(node, "family", &child);
428 0 : if (r)
429 0 : return bf_rpack_key_err(r, "bf_hookopts.family");
430 0 : if (!bf_rpack_is_nil(child)) {
431 0 : r = bf_rpack_uint(child, &_hookopts->family);
432 0 : if (r)
433 0 : return bf_rpack_key_err(r, "bf_hookopts.family");
434 :
435 0 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_FAMILY);
436 : }
437 :
438 0 : r = bf_rpack_kv_node(node, "priorities", &child);
439 0 : if (r)
440 0 : return bf_rpack_key_err(r, "bf_hookopts.priorities");
441 0 : if (!bf_rpack_is_nil(child)) {
442 : bf_rpack_node_t child, p_node;
443 :
444 0 : r = bf_rpack_kv_array(node, "priorities", &child);
445 0 : if (r)
446 0 : return bf_rpack_key_err(r, "bf_hookopts.priorities");
447 0 : 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 0 : bf_rpack_array_foreach (child, p_node) {
453 0 : r = bf_rpack_int(p_node, &_hookopts->priorities[i]);
454 0 : if (r) {
455 0 : return bf_rpack_key_err(
456 : r, "failed to unpack bf_hookopts.priorities value");
457 : }
458 : }
459 :
460 0 : _hookopts->used_opts |= BF_FLAG(BF_HOOKOPTS_PRIORITIES);
461 : }
462 :
463 0 : *hookopts = TAKE_PTR(_hookopts);
464 :
465 0 : return 0;
466 : }
467 :
468 0 : void bf_hookopts_clean(struct bf_hookopts *hookopts)
469 : {
470 : freep((void *)&hookopts->cgpath);
471 0 : }
472 :
473 3 : void bf_hookopts_free(struct bf_hookopts **hookopts)
474 : {
475 3 : bf_assert(hookopts);
476 :
477 3 : if (!*hookopts)
478 : return;
479 :
480 0 : bf_hookopts_clean(*hookopts);
481 : freep((void *)hookopts);
482 : }
483 :
484 0 : int bf_hookopts_pack(const struct bf_hookopts *hookopts, bf_wpack_t *pack)
485 : {
486 0 : bf_assert(hookopts);
487 0 : bf_assert(pack);
488 :
489 0 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_IFINDEX))
490 0 : bf_wpack_kv_int(pack, "ifindex", hookopts->ifindex);
491 : else
492 : bf_wpack_kv_nil(pack, "ifindex");
493 :
494 0 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_CGPATH))
495 0 : bf_wpack_kv_str(pack, "cgpath", hookopts->cgpath);
496 : else
497 : bf_wpack_kv_nil(pack, "cgpath");
498 :
499 0 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_FAMILY))
500 0 : bf_wpack_kv_uint(pack, "family", hookopts->family);
501 : else
502 : bf_wpack_kv_nil(pack, "family");
503 :
504 0 : if (bf_hookopts_is_used(hookopts, BF_HOOKOPTS_PRIORITIES)) {
505 0 : bf_wpack_open_array(pack, "priorities");
506 0 : bf_wpack_int(pack, hookopts->priorities[0]);
507 0 : bf_wpack_int(pack, hookopts->priorities[1]);
508 0 : bf_wpack_close_array(pack);
509 : } else {
510 : bf_wpack_kv_nil(pack, "priorities");
511 : }
512 :
513 0 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
514 : }
515 :
516 0 : void bf_hookopts_dump(const struct bf_hookopts *hookopts, prefix_t *prefix)
517 : {
518 0 : bf_assert(hookopts && prefix);
519 :
520 0 : DUMP(prefix, "struct bf_hookopts at %p", hookopts);
521 :
522 0 : bf_dump_prefix_push(prefix);
523 0 : DUMP(prefix, "used_opts: 0x%08x", hookopts->used_opts);
524 :
525 0 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
526 0 : if (type == _BF_HOOKOPTS_MAX - 1)
527 0 : bf_dump_prefix_last(prefix);
528 :
529 0 : if (bf_hookopts_is_used(hookopts, type))
530 0 : _bf_hookopts_ops[type].dump(hookopts, prefix);
531 : else
532 0 : DUMP(prefix, "%s: <unset>", _bf_hookopts_ops[type].name);
533 : }
534 :
535 0 : bf_dump_prefix_pop(prefix);
536 0 : }
537 :
538 0 : 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 0 : bf_assert(hookopts && raw_opt);
545 :
546 0 : value = strchr(raw_opt, '=');
547 0 : if (!value)
548 : return -ENOENT;
549 :
550 0 : *value = '\0';
551 0 : ++value;
552 :
553 0 : ops = _bf_hookopts_get_ops(raw_opt);
554 0 : if (!ops) {
555 0 : return bf_err_r(-ENOTSUP, "unknown hook option '%s', ignoring",
556 : raw_opt);
557 : }
558 :
559 0 : r = ops->parse(hookopts, value);
560 0 : if (r < 0)
561 : return r;
562 :
563 : return 0;
564 : }
565 :
566 0 : int bf_hookopts_parse_opts(struct bf_hookopts *hookopts, bf_list *raw_opts)
567 : {
568 : int r;
569 :
570 0 : bf_assert(hookopts && raw_opts);
571 :
572 0 : if (!raw_opts)
573 : return 0;
574 :
575 0 : bf_list_foreach (raw_opts, raw_opt_node) {
576 0 : r = bf_hookopts_parse_opt(hookopts,
577 0 : bf_list_node_get_data(raw_opt_node));
578 0 : if (r)
579 : return r;
580 : }
581 :
582 : return 0;
583 : }
584 :
585 0 : int bf_hookopts_validate(const struct bf_hookopts *hookopts, enum bf_hook hook)
586 : {
587 0 : enum bf_flavor flavor = bf_hook_to_flavor(hook);
588 :
589 0 : bf_assert(hookopts);
590 :
591 0 : for (enum bf_hookopts_type type = 0; type < _BF_HOOKOPTS_MAX; ++type) {
592 : struct bf_hookopts_ops *ops = &_bf_hookopts_ops[type];
593 0 : bool is_used = bf_hookopts_is_used(hookopts, type);
594 0 : bool is_required = _bf_hookopts_is_required(type, flavor);
595 0 : bool is_supported = _bf_hookopts_is_supported(type, flavor);
596 :
597 0 : if (is_required && !is_used) {
598 0 : 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 0 : if (is_used && !(is_supported | is_required)) {
604 0 : 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 : }
|