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