Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "bpfilter/matcher.h"
7 : :
8 : : #include <linux/icmp.h>
9 : : #include <linux/icmpv6.h>
10 : : #include <linux/if_ether.h>
11 : : #include <linux/in.h>
12 : : #include <linux/in6.h>
13 : : #include <linux/ip.h>
14 : : #include <linux/ipv6.h>
15 : : #include <linux/tcp.h>
16 : : #include <linux/udp.h>
17 : :
18 : : #include <assert.h>
19 : : #include <ctype.h>
20 : : #include <endian.h>
21 : : #include <errno.h>
22 : : #include <inttypes.h>
23 : : #include <limits.h>
24 : : #include <math.h>
25 : : #include <stdint.h>
26 : : #include <stdlib.h>
27 : : #include <string.h>
28 : :
29 : : #include "bpfilter/dump.h"
30 : : #include "bpfilter/helper.h"
31 : : #include "bpfilter/hook.h"
32 : : #include "bpfilter/if.h"
33 : : #include "bpfilter/logger.h"
34 : : #include "bpfilter/pack.h"
35 : : #include "bpfilter/runtime.h"
36 : :
37 : : #define INET4_ADDRSTRLEN 16
38 : : #define INET6_ADDRSTRLEN 46
39 : : #define BF_DSCP_MAX 63
40 : :
41 : : #define BF_PAYLOAD_OPS(type, size, parser_cb, printer_cb) \
42 : : [type] = {size, parser_cb, printer_cb}
43 : :
44 : : extern int inet_pton(int, const char *, void *);
45 : : extern const char *inet_ntop(int, const void *, char *, socklen_t);
46 : :
47 : : /**
48 : : * Matcher definition.
49 : : *
50 : : * Matchers are criterias to match the packet against. A set of matcher defines
51 : : * what a rule should match on.
52 : : *
53 : : * @todo `bf_matcher`'s payload should be a union of all the possible payload
54 : : * types.
55 : : */
56 : : struct bf_matcher
57 : : {
58 : : /// Matcher type.
59 : : enum bf_matcher_type type;
60 : : /// Comparison operator.
61 : : enum bf_matcher_op op;
62 : : /// Negate the comparison result.
63 : : bool negate;
64 : : /// Total matcher size (including payload).
65 : : size_t len;
66 : : /// Payload to match the packet against (if any).
67 : : uint8_t payload[];
68 : : };
69 : :
70 : : /**
71 : : * @brief Parse a string into an unsigned long.
72 : : *
73 : : * Tries base-10 first. If that fails to consume the full string, retries
74 : : * as base-16 (accepts "0x" prefix). Returns -EINVAL if neither succeeds.
75 : : *
76 : : * @param str String to parse. Must not be NULL.
77 : : * @param value Parsed value. Must not be NULL.
78 : : * @return 0 on success, negative errno on failure.
79 : : */
80 : 578 : static int _bf_strtoul(const char *str, unsigned long *value)
81 : : {
82 : : char *endptr;
83 : :
84 : : assert(str);
85 : : assert(value);
86 : :
87 [ - + ]: 578 : if (*str == '\0')
88 : : return -EINVAL;
89 : :
90 : 578 : errno = 0;
91 : 578 : *value = strtoul(str, &endptr, BF_BASE_10);
92 [ + + - + ]: 578 : if (*endptr != '\0' || errno == ERANGE) {
93 : 253 : errno = 0;
94 : 253 : *value = strtoul(str, &endptr, BF_BASE_16);
95 [ + + - + ]: 253 : if (*endptr != '\0' || errno == ERANGE)
96 : : return -EINVAL;
97 : : }
98 : :
99 : : return 0;
100 : : }
101 : :
102 : 46 : static int _bf_parse_u32(enum bf_matcher_type type, enum bf_matcher_op op,
103 : : void *payload, const char *raw_payload)
104 : : {
105 : : unsigned long value;
106 : :
107 : : assert(payload);
108 : : assert(raw_payload);
109 : :
110 [ + + + + ]: 46 : if (!_bf_strtoul(raw_payload, &value) && value <= UINT32_MAX) {
111 : 36 : *(uint32_t *)payload = (uint32_t)value;
112 : 36 : return 0;
113 : : }
114 : :
115 [ + - ]: 10 : return bf_err_r(
116 : : -EINVAL,
117 : : "\"%s %s\" expects a valid 32-bit value in decimal or hexadecimal notation, not '%s'",
118 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
119 : : }
120 : :
121 : 0 : static void _bf_print_u32(const void *payload)
122 : : {
123 : : assert(payload);
124 : :
125 : 0 : (void)fprintf(stdout, "0x%" PRIx32, *(uint32_t *)payload);
126 : 0 : }
127 : :
128 : : #define BF_U32_RANGE_MAX_LEN 32 /* 4294967295-4294967295 */
129 : :
130 : 24 : static int _bf_parse_u32_range(enum bf_matcher_type type, enum bf_matcher_op op,
131 : : void *payload, const char *raw_payload)
132 : : {
133 : : uint32_t *range;
134 : : char buf[BF_U32_RANGE_MAX_LEN];
135 : : char *first;
136 : : char *second;
137 : : unsigned long value;
138 : :
139 : : assert(payload);
140 : : assert(raw_payload);
141 : :
142 : : range = (uint32_t *)payload;
143 : :
144 : 24 : bf_strncpy(buf, BF_U32_RANGE_MAX_LEN, raw_payload);
145 : :
146 [ - + ]: 24 : if (!isdigit(*raw_payload))
147 : 0 : goto err;
148 : :
149 : 24 : first = strtok_r(buf, "-", &second);
150 [ - + ]: 24 : if (!first)
151 : 0 : goto err;
152 : :
153 [ + + ]: 24 : if (!*second)
154 : 1 : goto err;
155 : :
156 [ + + - + ]: 23 : if (_bf_strtoul(first, &value) || value > UINT32_MAX)
157 : 1 : goto err;
158 : 22 : range[0] = (uint32_t)value;
159 : :
160 [ + - - + ]: 22 : if (_bf_strtoul(second, &value) || value > UINT32_MAX)
161 : 0 : goto err;
162 : 22 : range[1] = (uint32_t)value;
163 : :
164 [ + + ]: 22 : if (range[1] < range[0])
165 : 2 : goto err;
166 : :
167 : : return 0;
168 : :
169 : 4 : err:
170 [ + - ]: 4 : bf_err(
171 : : "\"%s %s\" expects two positive decimal and hexadecimal integers as `$START-$END`, with `$START <= $END`, not '%s'",
172 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
173 : :
174 : : return -EINVAL;
175 : : }
176 : :
177 : 0 : static void _bf_print_u32_range(const void *payload)
178 : : {
179 : : assert(payload);
180 : :
181 : : uint32_t *range = (uint32_t *)payload;
182 : :
183 : 0 : (void)fprintf(stdout, "0x%" PRIx32 "-0x%" PRIx32, range[0], range[1]);
184 : 0 : }
185 : :
186 : 42 : int _bf_parse_iface(enum bf_matcher_type type, enum bf_matcher_op op,
187 : : void *payload, const char *raw_payload)
188 : : {
189 : : assert(payload);
190 : : assert(raw_payload);
191 : :
192 : : int r;
193 : :
194 : 42 : r = bf_if_index_from_str(raw_payload, (uint32_t *)payload);
195 [ + + ]: 42 : if (r) {
196 [ + - ]: 11 : return bf_err_r(
197 : : r,
198 : : "\"%s %s\" expects an interface name (e.g., \"eth0\", \"wlan0\") or a decimal interface index (e.g., \"1\", \"2\"), not '%s'",
199 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op),
200 : : raw_payload);
201 : : }
202 : :
203 : : return 0;
204 : : }
205 : :
206 : 2 : void _bf_print_iface(const void *payload)
207 : : {
208 : : assert(payload);
209 : :
210 : : const char *ifname;
211 : 2 : uint32_t ifindex = *(uint32_t *)payload;
212 : :
213 : 2 : ifname = bf_if_name_from_index((int)ifindex);
214 [ + + ]: 2 : if (ifname)
215 : 1 : (void)fprintf(stdout, "%s", ifname);
216 : : else
217 : 1 : (void)fprintf(stdout, "%" PRIu32, ifindex);
218 : 2 : }
219 : :
220 : 89 : int _bf_parse_l3_proto(enum bf_matcher_type type, enum bf_matcher_op op,
221 : : void *payload, const char *raw_payload)
222 : : {
223 : : unsigned long value;
224 : :
225 : : assert(payload);
226 : : assert(raw_payload);
227 : :
228 [ + + ]: 89 : if (!bf_ethertype_from_str(raw_payload, payload))
229 : : return 0;
230 : :
231 [ + + + + ]: 34 : if (!_bf_strtoul(raw_payload, &value) && value <= UINT16_MAX) {
232 : 28 : *(uint16_t *)payload = (uint16_t)value;
233 : 28 : return 0;
234 : : }
235 : :
236 [ + - ]: 6 : return bf_err_r(
237 : : -EINVAL,
238 : : "\"%s %s\" expects an internet layer protocol name (e.g. \"IPv6\", case insensitive), or a valid decimal or hexadecimal IEEE 802 number, not '%s'",
239 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
240 : : }
241 : :
242 : 2 : void _bf_print_l3_proto(const void *payload)
243 : : {
244 : : assert(payload);
245 : :
246 : 2 : const char *ethertype = bf_ethertype_to_str(*(uint16_t *)payload);
247 : :
248 [ + + ]: 2 : if (ethertype)
249 : 1 : (void)fprintf(stdout, "%s", ethertype);
250 : : else
251 : 1 : (void)fprintf(stdout, "0x%04" PRIx16, *(uint16_t *)payload);
252 : 2 : }
253 : :
254 : 365 : int _bf_parse_l4_proto(enum bf_matcher_type type, enum bf_matcher_op op,
255 : : void *payload, const char *raw_payload)
256 : : {
257 : : unsigned long value;
258 : :
259 : : assert(payload);
260 : : assert(raw_payload);
261 : :
262 [ + + ]: 365 : if (!bf_ipproto_from_str(raw_payload, payload))
263 : : return 0;
264 : :
265 [ + + + + ]: 100 : if (!_bf_strtoul(raw_payload, &value) && value <= UINT8_MAX) {
266 : 66 : *(uint8_t *)payload = (uint8_t)value;
267 : 66 : return 0;
268 : : }
269 : :
270 [ + - ]: 34 : return bf_err_r(
271 : : -EINVAL,
272 : : "\"%s %s\" expects a transport layer protocol name (e.g. \"ICMP\", case insensitive), or a valid decimal or hexadecimal internet protocol number, not '%s'",
273 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
274 : : }
275 : :
276 : 13 : void _bf_print_l4_proto(const void *payload)
277 : : {
278 : : assert(payload);
279 : :
280 : 13 : const char *ipproto = bf_ipproto_to_str(*(uint8_t *)payload);
281 : :
282 [ + + ]: 13 : if (ipproto)
283 : 12 : (void)fprintf(stdout, "%s", ipproto);
284 : : else
285 : 1 : (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
286 : 13 : }
287 : :
288 : 710 : int _bf_parse_l4_port(enum bf_matcher_type type, enum bf_matcher_op op,
289 : : void *payload, const char *raw_payload)
290 : : {
291 : : assert(payload);
292 : : assert(raw_payload);
293 : :
294 : : unsigned long port;
295 : : char *endptr;
296 : :
297 : 710 : port = strtoul(raw_payload, &endptr, BF_BASE_10);
298 [ + + + + ]: 710 : if (*endptr == '\0' && port <= UINT16_MAX) {
299 : 649 : *(uint16_t *)payload = htobe16((uint16_t)port);
300 : 649 : return 0;
301 : : }
302 : :
303 [ + - ]: 61 : bf_err("\"%s %s\" expects a valid decimal port number, not '%s'",
304 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
305 : :
306 : : return -EINVAL;
307 : : }
308 : :
309 : 5 : void _bf_print_l4_port(const void *payload)
310 : : {
311 : : assert(payload);
312 : :
313 : 5 : (void)fprintf(stdout, "%" PRIu16, (uint16_t)be16toh(*(uint16_t *)payload));
314 : 5 : }
315 : :
316 : : #define BF_PORT_RANGE_MAX_LEN 16 // 65535-65535, with nul char, round to **2
317 : :
318 : 242 : static int _bf_parse_l4_port_range(enum bf_matcher_type type,
319 : : enum bf_matcher_op op, void *payload,
320 : : const char *raw_payload)
321 : : {
322 : : assert(payload);
323 : : assert(raw_payload);
324 : :
325 : : uint16_t *ports = (uint16_t *)payload;
326 : : unsigned long port;
327 : : char buf[BF_PORT_RANGE_MAX_LEN];
328 : : char *first;
329 : : char *second;
330 : : char *endptr;
331 : :
332 : 242 : bf_strncpy(buf, BF_PORT_RANGE_MAX_LEN, raw_payload);
333 : :
334 [ + + ]: 242 : if (!isdigit(*raw_payload))
335 : 25 : goto err;
336 : :
337 : 217 : first = strtok_r(buf, "-", &second);
338 [ - + ]: 217 : if (!first)
339 : 0 : goto err;
340 : :
341 [ + + ]: 217 : if (!*second)
342 : 14 : goto err;
343 : :
344 : 203 : port = strtoul(first, &endptr, BF_BASE_10);
345 [ + + + + ]: 203 : if (*endptr != '\0' || port > UINT16_MAX)
346 : 18 : goto err;
347 : 185 : ports[0] = (uint16_t)port;
348 : :
349 : 185 : port = strtoul(second, &endptr, BF_BASE_10);
350 [ + - + + ]: 185 : if (*endptr != '\0' || port > UINT16_MAX)
351 : 1 : goto err;
352 : 184 : ports[1] = (uint16_t)port;
353 : :
354 [ + + ]: 184 : if (ports[1] < ports[0])
355 : 7 : goto err;
356 : :
357 : : return 0;
358 : :
359 : 65 : err:
360 [ + - ]: 65 : bf_err(
361 : : "\"%s %s\" expects two positive decimal port numbers as `$START-$END`, with `$START <= $END`, not '%s'",
362 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
363 : :
364 : : return -EINVAL;
365 : : }
366 : :
367 : 1 : void _bf_print_l4_port_range(const void *payload)
368 : : {
369 : : assert(payload);
370 : :
371 : : uint16_t *ports = (uint16_t *)payload;
372 : :
373 : 1 : (void)fprintf(stdout, "%" PRIu16 "-%" PRIu16, ports[0], ports[1]);
374 : 1 : }
375 : :
376 : 97 : static int _bf_parse_probability(enum bf_matcher_type type,
377 : : enum bf_matcher_op op, void *payload,
378 : : const char *raw_payload)
379 : : {
380 : : assert(payload);
381 : : assert(raw_payload);
382 : :
383 : : double proba;
384 : : char *endptr;
385 : :
386 : 97 : proba = strtod(raw_payload, &endptr);
387 [ + + + - : 97 : if (endptr[0] == '%' && endptr[1] == '\0' && proba >= 0.0 &&
+ + + + ]
388 : : proba <= 100.0) {
389 : 86 : *(float *)payload = (float)proba;
390 : 86 : return 0;
391 : : }
392 : :
393 [ + - ]: 11 : bf_err(
394 : : "\"%s %s\" expects a valid percentage value (i.e., within [0%%, 100%%], e.g., \"50%%\" or \"33.33%%\"), not '%s'",
395 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
396 : :
397 : : return -EINVAL;
398 : : }
399 : :
400 : 4 : static void _bf_print_probability(const void *payload)
401 : : {
402 : : assert(payload);
403 : :
404 : 4 : float proba = *(float *)payload;
405 [ + + ]: 4 : if (proba == floorf(proba))
406 : 2 : (void)fprintf(stdout, "%.0f%%", proba);
407 : : else
408 : 2 : (void)fprintf(stdout, "%g%%", proba);
409 : 4 : }
410 : :
411 : 38 : static int _bf_parse_mark(enum bf_matcher_type type, enum bf_matcher_op op,
412 : : void *payload, const char *raw_payload)
413 : : {
414 : : long long mark;
415 : : char *endptr;
416 : :
417 : : assert(payload);
418 : : assert(raw_payload);
419 : :
420 : : (void)type;
421 : : (void)op;
422 : :
423 : 38 : mark = strtoll(raw_payload, &endptr, 0);
424 [ + + ]: 38 : if (*endptr) {
425 [ + - ]: 3 : return bf_err_r(-EINVAL,
426 : : "mark value '%s' can't be parsed as a positive integer",
427 : : raw_payload);
428 : : }
429 [ + + ]: 35 : if (mark < 0) {
430 [ + - ]: 2 : return bf_err_r(-EINVAL, "mark should be positive, not '%s'",
431 : : raw_payload);
432 : : }
433 [ + + ]: 33 : if (mark > UINT32_MAX)
434 [ + - ]: 2 : return bf_err_r(-EINVAL, "mark should be at most 0x%x", UINT32_MAX);
435 : :
436 : 31 : *(uint32_t *)payload = (uint32_t)mark;
437 : :
438 : 31 : return 0;
439 : : }
440 : :
441 : 1 : void _bf_print_mark(const void *payload)
442 : : {
443 : : assert(payload);
444 : :
445 : 1 : (void)fprintf(stdout, "0x%" PRIx32, *(uint32_t *)payload);
446 : 1 : }
447 : :
448 : 216 : static int _bf_parse_ipv4_addr(enum bf_matcher_type type, enum bf_matcher_op op,
449 : : void *payload, const char *raw_payload)
450 : : {
451 : : assert(payload);
452 : : assert(raw_payload);
453 : :
454 : : int r;
455 : :
456 : 216 : r = inet_pton(AF_INET, raw_payload, payload);
457 [ + + ]: 216 : if (r == 1)
458 : : return 0;
459 : :
460 [ + - ]: 6 : bf_err(
461 : : "\"%s %s\" expects an IPv4 address in dotted-decimal format, \"ddd.ddd.ddd.ddd\", where ddd is a decimal number of up to three digits in the range 0 to 255, not '%s' ",
462 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
463 : :
464 : : return -EINVAL;
465 : : }
466 : :
467 : 34 : void _bf_print_ipv4_addr(const void *payload)
468 : : {
469 : : assert(payload);
470 : :
471 : : char str[INET4_ADDRSTRLEN];
472 : :
473 [ + - ]: 34 : if (inet_ntop(AF_INET, payload, str, INET4_ADDRSTRLEN))
474 : 34 : (void)fprintf(stdout, "%s", str);
475 : : else
476 : 0 : (void)fprintf(stdout, "<failed to print IPv4 address>");
477 : 34 : }
478 : :
479 : : #define BF_IPV4_NET_MAX_LEN \
480 : : 32 // 255.255.255.255/32, with nul char, round to **2
481 : :
482 : 111 : static int _bf_parse_ipv4_net(enum bf_matcher_type type, enum bf_matcher_op op,
483 : : void *payload, const char *raw_payload)
484 : : {
485 : : assert(payload);
486 : : assert(raw_payload);
487 : :
488 : : struct bf_ip4_lpm_key *addr = payload;
489 : : char buf[BF_IPV4_NET_MAX_LEN];
490 : : char *strip, *strmask, *endptr;
491 : : int r;
492 : :
493 : 111 : bf_strncpy(buf, BF_IPV4_NET_MAX_LEN, raw_payload);
494 : :
495 [ + + ]: 111 : if (!isdigit(*raw_payload))
496 : 4 : goto err;
497 : :
498 : 107 : strip = strtok_r(buf, "/", &strmask);
499 [ + - + + ]: 107 : if (!strip || !*strmask)
500 : 1 : goto err;
501 : :
502 : 106 : r = inet_pton(AF_INET, strip, &addr->data);
503 [ + + ]: 106 : if (r != 1)
504 : 1 : goto err;
505 : :
506 : 105 : addr->prefixlen = strtoul(strmask, &endptr, BF_BASE_10);
507 [ + + + + ]: 105 : if (*endptr != '\0' || addr->prefixlen > 32)
508 : 13 : goto err;
509 : :
510 : : return 0;
511 : :
512 : 19 : err:
513 [ + - ]: 19 : bf_err(
514 : : "\"%s %s\" expects an IPv4 network address in dotted-decimal format, \"ddd.ddd.ddd.ddd\", where ddd is a decimal number of up to three digits in the range 0 to 255 followed by a subnet mask (e.g., \"124.24.12.5/30\"), not '%s' ",
515 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
516 : :
517 : : return -EINVAL;
518 : : }
519 : :
520 : 1 : void _bf_print_ipv4_net(const void *payload)
521 : : {
522 : : assert(payload);
523 : :
524 : : char str[INET4_ADDRSTRLEN];
525 : : const struct bf_ip4_lpm_key *addr = payload;
526 : :
527 [ + - ]: 1 : if (inet_ntop(AF_INET, &addr->data, str, INET4_ADDRSTRLEN))
528 : 1 : (void)fprintf(stdout, "%s/%u", str, addr->prefixlen);
529 : : else
530 : 0 : (void)fprintf(stdout, "<failed to print IPv4 network>");
531 : 1 : }
532 : :
533 : 213 : static int _bf_parse_ipv6_addr(enum bf_matcher_type type, enum bf_matcher_op op,
534 : : void *payload, const char *raw_payload)
535 : : {
536 : : assert(payload);
537 : : assert(raw_payload);
538 : :
539 : : int r;
540 : :
541 : 213 : r = inet_pton(AF_INET6, raw_payload, payload);
542 [ + + ]: 213 : if (r == 1)
543 : : return 0;
544 : :
545 [ + - ]: 36 : bf_err(
546 : : "\"%s %s\" expects an IPv6 address composed of 8 hexadecimal numbers (abbreviations are supported), not '%s' ",
547 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
548 : :
549 : : return -EINVAL;
550 : : }
551 : :
552 : 1 : void _bf_print_ipv6_addr(const void *payload)
553 : : {
554 : : assert(payload);
555 : :
556 : : char str[INET6_ADDRSTRLEN];
557 : :
558 [ + - ]: 1 : if (inet_ntop(AF_INET6, payload, str, INET6_ADDRSTRLEN))
559 : 1 : (void)fprintf(stdout, "%s", str);
560 : : else
561 : 0 : (void)fprintf(stdout, "<failed to print IPv6 address>");
562 : 1 : }
563 : :
564 : : #define BF_IPV6_NET_MAX_LEN (INET6_ADDRSTRLEN + 4)
565 : :
566 : 306 : static int _bf_parse_ipv6_net(enum bf_matcher_type type, enum bf_matcher_op op,
567 : : void *payload, const char *raw_payload)
568 : : {
569 : : assert(payload);
570 : : assert(raw_payload);
571 : :
572 : : struct bf_ip6_lpm_key *addr = payload;
573 : : char buf[BF_IPV6_NET_MAX_LEN];
574 : : char *strip, *strmask, *endptr;
575 : : int r;
576 : :
577 : 306 : bf_strncpy(buf, BF_IPV6_NET_MAX_LEN, raw_payload);
578 : :
579 [ + + + + : 306 : if (!isalpha(*raw_payload) && !isdigit(*raw_payload) && *raw_payload != ':')
+ + ]
580 : 12 : goto err;
581 : :
582 : 294 : strip = strtok_r(buf, "/", &strmask);
583 [ + - + + ]: 294 : if (!strip || !*strmask)
584 : 29 : goto err;
585 : :
586 : 265 : r = inet_pton(AF_INET6, strip, &addr->data);
587 [ + + ]: 265 : if (r != 1)
588 : 13 : goto err;
589 : :
590 : 252 : addr->prefixlen = strtoul(strmask, &endptr, BF_BASE_10);
591 [ + + + + ]: 252 : if (*endptr != '\0' || addr->prefixlen > 128)
592 : 33 : goto err;
593 : :
594 : : return 0;
595 : :
596 : 87 : err:
597 [ + - ]: 87 : bf_err(
598 : : "\"%s %s\" expects an IPv6 network address composed of 8 hexadecimal numbers (abbreviations are supported) followed by a subnet mask (e.g., \"2001:db8:85a3::/48\"), not '%s' ",
599 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
600 : :
601 : : return -EINVAL;
602 : : }
603 : :
604 : 1 : void _bf_print_ipv6_net(const void *payload)
605 : : {
606 : : assert(payload);
607 : :
608 : : const struct bf_ip6_lpm_key *addr = payload;
609 : : char str[INET6_ADDRSTRLEN];
610 : :
611 [ + - ]: 1 : if (inet_ntop(AF_INET6, addr->data, str, INET6_ADDRSTRLEN))
612 : 1 : (void)fprintf(stdout, "%s/%u", str, addr->prefixlen);
613 : : else
614 : 0 : (void)fprintf(stdout, "<failed to print IPv6 address>");
615 : 1 : }
616 : :
617 : 147 : static int _bf_parse_tcp_flags(enum bf_matcher_type type, enum bf_matcher_op op,
618 : : void *payload, const char *raw_payload)
619 : : {
620 : : assert(payload);
621 : : assert(raw_payload);
622 : :
623 : : _cleanup_free_ char *_raw_payload = NULL;
624 : : char *tmp;
625 : : char *saveptr;
626 : : char *token;
627 : : uint8_t *flags = payload;
628 : :
629 : 147 : _raw_payload = strdup(raw_payload);
630 [ - + ]: 147 : if (!_raw_payload)
631 : 0 : goto err;
632 : :
633 : 147 : *flags = 0;
634 : : tmp = _raw_payload;
635 : :
636 [ + + ]: 454 : while ((token = strtok_r(tmp, ",", &saveptr))) {
637 : : enum bf_tcp_flag new_flag;
638 : : int r;
639 : :
640 : 316 : r = bf_tcp_flag_from_str(token, &new_flag);
641 [ + + ]: 316 : if (r)
642 : 9 : goto err;
643 : :
644 : 307 : *flags |= (uint8_t)(1 << new_flag);
645 : :
646 : : tmp = NULL;
647 : : }
648 : :
649 : : return 0;
650 : :
651 : 9 : err:
652 [ + - ]: 9 : bf_err(
653 : : "\"%s %s\" expects a comma-separated list of one or more TCP flags (fin, syn, rst, psh, ack, urg, ece, or cwr), not '%s' ",
654 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
655 : :
656 : : return -EINVAL;
657 : : }
658 : :
659 : 1 : void _bf_print_tcp_flags(const void *payload)
660 : : {
661 : : assert(payload);
662 : :
663 : 1 : uint8_t flag = *(uint8_t *)payload;
664 : :
665 [ + + ]: 9 : for (uint32_t i = 0; i < _BF_TCP_MAX; ++i) {
666 [ + + ]: 8 : if (flag & (1 << i)) {
667 : 2 : flag &= ~(1 << i);
668 [ + + ]: 3 : (void)fprintf(stdout, "%s%s", bf_tcp_flag_to_str(i),
669 : : flag ? "," : "");
670 : : }
671 : : }
672 : 1 : }
673 : :
674 : 88 : static int _bf_parse_icmp_type(enum bf_matcher_type type, enum bf_matcher_op op,
675 : : void *payload, const char *raw_payload)
676 : : {
677 : : unsigned long value;
678 : :
679 : : assert(payload);
680 : : assert(raw_payload);
681 : :
682 [ + + ]: 88 : if (!bf_icmp_type_from_str(raw_payload, payload))
683 : : return 0;
684 : :
685 [ + + + + ]: 56 : if (!_bf_strtoul(raw_payload, &value) && value <= UINT8_MAX) {
686 : 44 : *(uint8_t *)payload = (uint8_t)value;
687 : 44 : return 0;
688 : : }
689 : :
690 [ + - ]: 12 : return bf_err_r(
691 : : -EINVAL,
692 : : "\"%s %s\" expects an ICMP type name (e.g. \"echo-reply\", case insensitive), or a decimal or hexadecimal ICMP type value, not '%s'",
693 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
694 : : }
695 : :
696 : 4 : void _bf_print_icmp_type(const void *payload)
697 : : {
698 : : assert(payload);
699 : :
700 : 4 : const char *type = bf_icmp_type_to_str(*(uint8_t *)payload);
701 : :
702 [ + + ]: 4 : if (type)
703 : 3 : (void)fprintf(stdout, "%s", type);
704 : : else
705 : 1 : (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
706 : 4 : }
707 : :
708 : 166 : static int _bf_parse_icmp_code(enum bf_matcher_type type, enum bf_matcher_op op,
709 : : void *payload, const char *raw_payload)
710 : : {
711 : : unsigned long value;
712 : :
713 : : assert(payload);
714 : : assert(raw_payload);
715 : :
716 [ + + + + ]: 166 : if (!_bf_strtoul(raw_payload, &value) && value <= UINT8_MAX) {
717 : 142 : *(uint8_t *)payload = (uint8_t)value;
718 : 142 : return 0;
719 : : }
720 : :
721 [ + - ]: 24 : return bf_err_r(
722 : : -EINVAL,
723 : : "\"%s %s\" expects a decimal or hexadecimal ICMP or ICMPv6 code value, not '%s'",
724 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
725 : : }
726 : :
727 : 3 : void _bf_print_icmp_code(const void *payload)
728 : : {
729 : : assert(payload);
730 : :
731 : 3 : (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
732 : 3 : }
733 : :
734 : 87 : static int _bf_parse_dscp(enum bf_matcher_type type, enum bf_matcher_op op,
735 : : void *payload, const char *raw_payload)
736 : : {
737 : : unsigned long value;
738 : :
739 : : assert(payload);
740 : : assert(raw_payload);
741 : :
742 [ + + ]: 87 : if (!bf_dscp_class_from_str(raw_payload, payload))
743 : : return 0;
744 : :
745 [ + + + + ]: 73 : if (!_bf_strtoul(raw_payload, &value) && value <= BF_DSCP_MAX) {
746 : 39 : *(uint8_t *)payload = (uint8_t)value;
747 : 39 : return 0;
748 : : }
749 : :
750 [ + - ]: 34 : return bf_err_r(
751 : : -EINVAL,
752 : : "\"%s %s\" expects a DSCP value (0-63, decimal/hex) or class name, not '%s'",
753 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
754 : : }
755 : :
756 : 1 : static void _bf_print_dscp(const void *payload)
757 : : {
758 : : const char *name;
759 : :
760 : : assert(payload);
761 : :
762 : 1 : name = bf_dscp_class_to_str(*(uint8_t *)payload);
763 : :
764 [ + - ]: 1 : if (name)
765 : 1 : (void)fprintf(stdout, "%s", name);
766 : : else
767 : 0 : (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
768 : 1 : }
769 : :
770 : 96 : static int _bf_parse_icmpv6_type(enum bf_matcher_type type,
771 : : enum bf_matcher_op op, void *payload,
772 : : const char *raw_payload)
773 : : {
774 : : unsigned long value;
775 : :
776 : : assert(payload);
777 : : assert(raw_payload);
778 : :
779 [ + + ]: 96 : if (!bf_icmpv6_type_from_str(raw_payload, payload))
780 : : return 0;
781 : :
782 [ + + + + ]: 58 : if (!_bf_strtoul(raw_payload, &value) && value <= UINT8_MAX) {
783 : 46 : *(uint8_t *)payload = (uint8_t)value;
784 : 46 : return 0;
785 : : }
786 : :
787 [ + - ]: 12 : return bf_err_r(
788 : : -EINVAL,
789 : : "\"%s %s\" expects an ICMPv6 type name (e.g. \"echo-reply\", case insensitive), or a decimal or hexadecimal ICMPv6 type value, not '%s'",
790 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
791 : : }
792 : :
793 : 2 : void _bf_print_icmpv6_type(const void *payload)
794 : : {
795 : : assert(payload);
796 : :
797 : 2 : const char *type = bf_icmpv6_type_to_str(*(uint8_t *)payload);
798 : :
799 [ + + ]: 2 : if (type)
800 : 1 : (void)fprintf(stdout, "%s", type);
801 : : else
802 : 1 : (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
803 : 2 : }
804 : :
805 : : #define BF_MATCHER_OPS(op, payload_size, parse_cb, print_cb) \
806 : : [op] = {payload_size, parse_cb, print_cb}
807 : :
808 : : #define _BF_TCP_FLAGS_OFFSET 13
809 : :
810 : : static_assert(_BF_HOOK_MAX <= 8 * sizeof(uint32_t),
811 : : "too many hooks for unsupported_hooks bitmask");
812 : :
813 : : // Comma-separated cgroup_sock_addr hook lists, for use inside `BF_FLAGS()`.
814 : : #define _BF_HOOKS_CGROUP_SOCK_ADDR_IP4 \
815 : : BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4, BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG4
816 : : #define _BF_HOOKS_CGROUP_SOCK_ADDR_IP6 \
817 : : BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6, BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG6
818 : : #define _BF_HOOKS_CGROUP_SOCK_ADDR_ALL \
819 : : _BF_HOOKS_CGROUP_SOCK_ADDR_IP4, _BF_HOOKS_CGROUP_SOCK_ADDR_IP6
820 : :
821 : : static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = {
822 : : [BF_MATCHER_META_IFACE] =
823 : : {
824 : : .layer = BF_MATCHER_NO_LAYER,
825 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
826 : : .ops =
827 : : {
828 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t),
829 : : _bf_parse_iface, _bf_print_iface),
830 : : },
831 : : },
832 : : [BF_MATCHER_META_L3_PROTO] =
833 : : {
834 : : .layer = BF_MATCHER_NO_LAYER,
835 : : .ops =
836 : : {
837 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
838 : : _bf_parse_l3_proto, _bf_print_l3_proto),
839 : : },
840 : : },
841 : : [BF_MATCHER_META_L4_PROTO] =
842 : : {
843 : : .layer = BF_MATCHER_NO_LAYER,
844 : : .ops =
845 : : {
846 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
847 : : _bf_parse_l4_proto, _bf_print_l4_proto),
848 : : },
849 : : },
850 : : [BF_MATCHER_META_SPORT] =
851 : : {
852 : : .layer = BF_MATCHER_NO_LAYER,
853 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
854 : : .ops =
855 : : {
856 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
857 : : _bf_parse_l4_port, _bf_print_l4_port),
858 : : BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
859 : : _bf_parse_l4_port_range,
860 : : _bf_print_l4_port_range),
861 : : },
862 : : },
863 : : [BF_MATCHER_META_DPORT] =
864 : : {
865 : : .layer = BF_MATCHER_NO_LAYER,
866 : : .ops =
867 : : {
868 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
869 : : _bf_parse_l4_port, _bf_print_l4_port),
870 : : BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
871 : : _bf_parse_l4_port_range,
872 : : _bf_print_l4_port_range),
873 : : },
874 : : },
875 : : [BF_MATCHER_META_PROBABILITY] =
876 : : {
877 : : .layer = BF_MATCHER_NO_LAYER,
878 : : .ops =
879 : : {
880 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(float),
881 : : _bf_parse_probability,
882 : : _bf_print_probability),
883 : : },
884 : : },
885 : : [BF_MATCHER_META_MARK] =
886 : : {
887 : : .layer = BF_MATCHER_NO_LAYER,
888 : : .unsupported_hooks =
889 : : BF_FLAGS(BF_HOOK_XDP, _BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
890 : : .ops =
891 : : {
892 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t),
893 : : _bf_parse_mark, _bf_print_mark),
894 : : },
895 : : },
896 : : [BF_MATCHER_META_FLOW_HASH] =
897 : : {
898 : : .layer = BF_MATCHER_NO_LAYER,
899 : : .unsupported_hooks =
900 : : BF_FLAGS(BF_HOOK_XDP, BF_HOOK_CGROUP_SKB_INGRESS,
901 : : BF_HOOK_CGROUP_SKB_EGRESS, BF_HOOK_NF_FORWARD,
902 : : BF_HOOK_NF_LOCAL_IN, BF_HOOK_NF_LOCAL_OUT,
903 : : BF_HOOK_NF_POST_ROUTING, BF_HOOK_NF_PRE_ROUTING,
904 : : _BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
905 : : .ops =
906 : : {
907 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t),
908 : : _bf_parse_u32, _bf_print_u32),
909 : : BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint32_t),
910 : : _bf_parse_u32_range, _bf_print_u32_range),
911 : : },
912 : : },
913 : : [BF_MATCHER_META_FLOW_PROBABILITY] =
914 : : {
915 : : .layer = BF_MATCHER_NO_LAYER,
916 : : .unsupported_hooks = BF_FLAGS(
917 : : BF_HOOK_NF_FORWARD, BF_HOOK_NF_LOCAL_IN, BF_HOOK_NF_LOCAL_OUT,
918 : : BF_HOOK_NF_POST_ROUTING, BF_HOOK_NF_PRE_ROUTING,
919 : : _BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
920 : : .ops =
921 : : {
922 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(float),
923 : : _bf_parse_probability,
924 : : _bf_print_probability),
925 : : },
926 : : },
927 : : [BF_MATCHER_IP4_SADDR] =
928 : : {
929 : : .layer = BF_MATCHER_LAYER_3,
930 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP6,
931 : : BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4),
932 : : .hdr_id = ETH_P_IP,
933 : : .hdr_payload_size = sizeof(uint32_t),
934 : : .hdr_payload_offset = offsetof(struct iphdr, saddr),
935 : : .ops =
936 : : {
937 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t),
938 : : _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
939 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint32_t),
940 : : _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
941 : : },
942 : : },
943 : : [BF_MATCHER_IP4_DADDR] =
944 : : {
945 : : .layer = BF_MATCHER_LAYER_3,
946 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP6),
947 : : .hdr_id = ETH_P_IP,
948 : : .hdr_payload_size = sizeof(uint32_t),
949 : : .hdr_payload_offset = offsetof(struct iphdr, daddr),
950 : : .ops =
951 : : {
952 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t),
953 : : _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
954 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint32_t),
955 : : _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
956 : : },
957 : : },
958 : : [BF_MATCHER_IP4_SNET] =
959 : : {
960 : : .layer = BF_MATCHER_LAYER_3,
961 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP6,
962 : : BF_HOOK_CGROUP_SOCK_ADDR_CONNECT4),
963 : : .hdr_id = ETH_P_IP,
964 : : .hdr_payload_size = sizeof(uint32_t),
965 : : .hdr_payload_offset = offsetof(struct iphdr, saddr),
966 : : .ops =
967 : : {
968 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct bf_ip4_lpm_key),
969 : : _bf_parse_ipv4_net, _bf_print_ipv4_net),
970 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct bf_ip4_lpm_key),
971 : : _bf_parse_ipv4_net, _bf_print_ipv4_net),
972 : : },
973 : : },
974 : : [BF_MATCHER_IP4_DNET] =
975 : : {
976 : : .layer = BF_MATCHER_LAYER_3,
977 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP6),
978 : : .hdr_id = ETH_P_IP,
979 : : .hdr_payload_size = sizeof(uint32_t),
980 : : .hdr_payload_offset = offsetof(struct iphdr, daddr),
981 : : .ops =
982 : : {
983 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct bf_ip4_lpm_key),
984 : : _bf_parse_ipv4_net, _bf_print_ipv4_net),
985 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct bf_ip4_lpm_key),
986 : : _bf_parse_ipv4_net, _bf_print_ipv4_net),
987 : : },
988 : : },
989 : : [BF_MATCHER_IP4_PROTO] =
990 : : {
991 : : .layer = BF_MATCHER_LAYER_3,
992 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP6),
993 : : .hdr_id = ETH_P_IP,
994 : : .hdr_payload_size = sizeof(uint8_t),
995 : : .hdr_payload_offset = offsetof(struct iphdr, protocol),
996 : : .ops =
997 : : {
998 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
999 : : _bf_parse_l4_proto, _bf_print_l4_proto),
1000 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
1001 : : _bf_parse_l4_proto, _bf_print_l4_proto),
1002 : : },
1003 : : },
1004 : : [BF_MATCHER_IP4_DSCP] =
1005 : : {
1006 : : .layer = BF_MATCHER_LAYER_3,
1007 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1008 : : .hdr_id = ETH_P_IP,
1009 : : .hdr_payload_size = sizeof(uint8_t),
1010 : : .hdr_payload_offset = offsetof(struct iphdr, tos),
1011 : : .ops =
1012 : : {
1013 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
1014 : : _bf_parse_dscp, _bf_print_dscp),
1015 : : },
1016 : : },
1017 : : [BF_MATCHER_IP6_SADDR] =
1018 : : {
1019 : : .layer = BF_MATCHER_LAYER_3,
1020 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP4,
1021 : : BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6),
1022 : : .hdr_id = ETH_P_IPV6,
1023 : : .hdr_payload_size = sizeof(struct in6_addr),
1024 : : .hdr_payload_offset = offsetof(struct ipv6hdr, saddr),
1025 : : .ops =
1026 : : {
1027 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct in6_addr),
1028 : : _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
1029 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct in6_addr),
1030 : : _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
1031 : : },
1032 : : },
1033 : : [BF_MATCHER_IP6_DADDR] =
1034 : : {
1035 : : .layer = BF_MATCHER_LAYER_3,
1036 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP4),
1037 : : .hdr_id = ETH_P_IPV6,
1038 : : .hdr_payload_size = sizeof(struct in6_addr),
1039 : : .hdr_payload_offset = offsetof(struct ipv6hdr, daddr),
1040 : : .ops =
1041 : : {
1042 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct in6_addr),
1043 : : _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
1044 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct in6_addr),
1045 : : _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
1046 : : },
1047 : : },
1048 : : [BF_MATCHER_IP6_SNET] =
1049 : : {
1050 : : .layer = BF_MATCHER_LAYER_3,
1051 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP4,
1052 : : BF_HOOK_CGROUP_SOCK_ADDR_CONNECT6),
1053 : : .hdr_id = ETH_P_IPV6,
1054 : : .hdr_payload_size = sizeof(struct in6_addr),
1055 : : .hdr_payload_offset = offsetof(struct ipv6hdr, saddr),
1056 : : .ops =
1057 : : {
1058 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct bf_ip6_lpm_key),
1059 : : _bf_parse_ipv6_net, _bf_print_ipv6_net),
1060 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct bf_ip6_lpm_key),
1061 : : _bf_parse_ipv6_net, _bf_print_ipv6_net),
1062 : : },
1063 : : },
1064 : : [BF_MATCHER_IP6_DNET] =
1065 : : {
1066 : : .layer = BF_MATCHER_LAYER_3,
1067 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_IP4),
1068 : : .hdr_id = ETH_P_IPV6,
1069 : : .hdr_payload_size = sizeof(struct in6_addr),
1070 : : .hdr_payload_offset = offsetof(struct ipv6hdr, daddr),
1071 : : .ops =
1072 : : {
1073 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct bf_ip6_lpm_key),
1074 : : _bf_parse_ipv6_net, _bf_print_ipv6_net),
1075 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct bf_ip6_lpm_key),
1076 : : _bf_parse_ipv6_net, _bf_print_ipv6_net),
1077 : : },
1078 : : },
1079 : : [BF_MATCHER_IP6_NEXTHDR] =
1080 : : {
1081 : : .layer = BF_MATCHER_LAYER_3,
1082 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1083 : : .hdr_id = ETH_P_IPV6,
1084 : : .hdr_payload_size = sizeof(uint8_t),
1085 : : .hdr_payload_offset = offsetof(struct ipv6hdr, nexthdr),
1086 : : .ops =
1087 : : {
1088 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
1089 : : _bf_parse_l4_proto, _bf_print_l4_proto),
1090 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
1091 : : _bf_parse_l4_proto, _bf_print_l4_proto),
1092 : : },
1093 : : },
1094 : : [BF_MATCHER_IP6_DSCP] =
1095 : : {
1096 : : .layer = BF_MATCHER_LAYER_3,
1097 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1098 : : .hdr_id = ETH_P_IPV6,
1099 : : .hdr_payload_size = sizeof(uint8_t),
1100 : : .hdr_payload_offset = 0,
1101 : : .ops =
1102 : : {
1103 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
1104 : : _bf_parse_dscp, _bf_print_dscp),
1105 : : },
1106 : : },
1107 : : [BF_MATCHER_TCP_SPORT] =
1108 : : {
1109 : : .layer = BF_MATCHER_LAYER_4,
1110 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1111 : : .hdr_id = IPPROTO_TCP,
1112 : : .hdr_payload_size = sizeof(uint16_t),
1113 : : .hdr_payload_offset = offsetof(struct tcphdr, source),
1114 : : .ops =
1115 : : {
1116 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
1117 : : _bf_parse_l4_port, _bf_print_l4_port),
1118 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint16_t),
1119 : : _bf_parse_l4_port, _bf_print_l4_port),
1120 : : BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
1121 : : _bf_parse_l4_port_range,
1122 : : _bf_print_l4_port_range),
1123 : : },
1124 : : },
1125 : : [BF_MATCHER_TCP_DPORT] =
1126 : : {
1127 : : .layer = BF_MATCHER_LAYER_4,
1128 : : .unsupported_hooks = BF_FLAGS(BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG4,
1129 : : BF_HOOK_CGROUP_SOCK_ADDR_SENDMSG6),
1130 : : .hdr_id = IPPROTO_TCP,
1131 : : .hdr_payload_size = sizeof(uint16_t),
1132 : : .hdr_payload_offset = offsetof(struct tcphdr, dest),
1133 : : .ops =
1134 : : {
1135 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
1136 : : _bf_parse_l4_port, _bf_print_l4_port),
1137 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint16_t),
1138 : : _bf_parse_l4_port, _bf_print_l4_port),
1139 : : BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
1140 : : _bf_parse_l4_port_range,
1141 : : _bf_print_l4_port_range),
1142 : : },
1143 : : },
1144 : : [BF_MATCHER_TCP_FLAGS] =
1145 : : {
1146 : : .layer = BF_MATCHER_LAYER_4,
1147 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1148 : : .hdr_id = IPPROTO_TCP,
1149 : : .hdr_payload_size = sizeof(uint8_t),
1150 : : .hdr_payload_offset = _BF_TCP_FLAGS_OFFSET,
1151 : : .ops =
1152 : : {
1153 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
1154 : : _bf_parse_tcp_flags, _bf_print_tcp_flags),
1155 : : BF_MATCHER_OPS(BF_MATCHER_ANY, sizeof(uint8_t),
1156 : : _bf_parse_tcp_flags, _bf_print_tcp_flags),
1157 : : BF_MATCHER_OPS(BF_MATCHER_ALL, sizeof(uint8_t),
1158 : : _bf_parse_tcp_flags, _bf_print_tcp_flags),
1159 : : },
1160 : : },
1161 : : [BF_MATCHER_UDP_SPORT] =
1162 : : {
1163 : : .layer = BF_MATCHER_LAYER_4,
1164 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1165 : : .hdr_id = IPPROTO_UDP,
1166 : : .hdr_payload_size = sizeof(uint16_t),
1167 : : .hdr_payload_offset = offsetof(struct udphdr, source),
1168 : : .ops =
1169 : : {
1170 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
1171 : : _bf_parse_l4_port, _bf_print_l4_port),
1172 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint16_t),
1173 : : _bf_parse_l4_port, _bf_print_l4_port),
1174 : : BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
1175 : : _bf_parse_l4_port_range,
1176 : : _bf_print_l4_port_range),
1177 : : },
1178 : : },
1179 : : [BF_MATCHER_UDP_DPORT] =
1180 : : {
1181 : : .layer = BF_MATCHER_LAYER_4,
1182 : : .hdr_id = IPPROTO_UDP,
1183 : : .hdr_payload_size = sizeof(uint16_t),
1184 : : .hdr_payload_offset = offsetof(struct udphdr, dest),
1185 : : .ops =
1186 : : {
1187 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
1188 : : _bf_parse_l4_port, _bf_print_l4_port),
1189 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint16_t),
1190 : : _bf_parse_l4_port, _bf_print_l4_port),
1191 : : BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
1192 : : _bf_parse_l4_port_range,
1193 : : _bf_print_l4_port_range),
1194 : : },
1195 : : },
1196 : : [BF_MATCHER_ICMP_TYPE] =
1197 : : {
1198 : : .layer = BF_MATCHER_LAYER_4,
1199 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1200 : : .hdr_id = IPPROTO_ICMP,
1201 : : .hdr_payload_size = sizeof(uint8_t),
1202 : : .hdr_payload_offset = offsetof(struct icmphdr, type),
1203 : : .ops =
1204 : : {
1205 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
1206 : : _bf_parse_icmp_type, _bf_print_icmp_type),
1207 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
1208 : : _bf_parse_icmp_type, _bf_print_icmp_type),
1209 : : },
1210 : : },
1211 : : [BF_MATCHER_ICMP_CODE] =
1212 : : {
1213 : : .layer = BF_MATCHER_LAYER_4,
1214 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1215 : : .hdr_id = IPPROTO_ICMP,
1216 : : .hdr_payload_size = sizeof(uint8_t),
1217 : : .hdr_payload_offset = offsetof(struct icmphdr, code),
1218 : : .ops =
1219 : : {
1220 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
1221 : : _bf_parse_icmp_code, _bf_print_icmp_code),
1222 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
1223 : : _bf_parse_icmp_code, _bf_print_icmp_code),
1224 : : },
1225 : : },
1226 : : [BF_MATCHER_ICMPV6_TYPE] =
1227 : : {
1228 : : .layer = BF_MATCHER_LAYER_4,
1229 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1230 : : .hdr_id = IPPROTO_ICMPV6,
1231 : : .hdr_payload_size = sizeof(uint8_t),
1232 : : .hdr_payload_offset = offsetof(struct icmp6hdr, icmp6_type),
1233 : : .ops =
1234 : : {
1235 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
1236 : : _bf_parse_icmpv6_type,
1237 : : _bf_print_icmpv6_type),
1238 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
1239 : : _bf_parse_icmpv6_type,
1240 : : _bf_print_icmpv6_type),
1241 : : },
1242 : : },
1243 : : [BF_MATCHER_ICMPV6_CODE] =
1244 : : {
1245 : : .layer = BF_MATCHER_LAYER_4,
1246 : : .unsupported_hooks = BF_FLAGS(_BF_HOOKS_CGROUP_SOCK_ADDR_ALL),
1247 : : .hdr_id = IPPROTO_ICMPV6,
1248 : : .hdr_payload_size = sizeof(uint8_t),
1249 : : .hdr_payload_offset = offsetof(struct icmp6hdr, icmp6_code),
1250 : : .ops =
1251 : : {
1252 : : BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
1253 : : _bf_parse_icmp_code, _bf_print_icmp_code),
1254 : : BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
1255 : : _bf_parse_icmp_code, _bf_print_icmp_code),
1256 : : },
1257 : : },
1258 : : [BF_MATCHER_SET] =
1259 : : {
1260 : : .layer = BF_MATCHER_NO_LAYER,
1261 : : },
1262 : : };
1263 : :
1264 : 36106 : const struct bf_matcher_meta *bf_matcher_get_meta(enum bf_matcher_type type)
1265 : : {
1266 [ + - ]: 36106 : if (type < 0 || _BF_MATCHER_TYPE_MAX <= type)
1267 : : return NULL;
1268 : :
1269 : 36106 : return _bf_matcher_metas[type].layer == _BF_MATCHER_LAYER_UNDEFINED ?
1270 [ + - ]: 36106 : NULL :
1271 : : &_bf_matcher_metas[type];
1272 : : }
1273 : :
1274 : 5249 : const struct bf_matcher_ops *bf_matcher_get_ops(enum bf_matcher_type type,
1275 : : enum bf_matcher_op op)
1276 : : {
1277 : 5249 : const struct bf_matcher_meta *meta = bf_matcher_get_meta(type);
1278 : :
1279 [ + - ]: 5249 : if (!meta)
1280 : : return NULL;
1281 : :
1282 [ + + ]: 5249 : return meta->ops[op].ref_payload_size ? &meta->ops[op] : NULL;
1283 : : }
1284 : :
1285 : 9562 : int bf_matcher_new(struct bf_matcher **matcher, enum bf_matcher_type type,
1286 : : enum bf_matcher_op op, const void *payload,
1287 : : size_t payload_len, bool negate)
1288 : : {
1289 : 9562 : _free_bf_matcher_ struct bf_matcher *_matcher = NULL;
1290 : :
1291 : : assert(matcher);
1292 : : assert((payload && payload_len) || (!payload && !payload_len));
1293 : :
1294 : 9562 : _matcher = malloc(sizeof(struct bf_matcher) + payload_len);
1295 [ + - ]: 9562 : if (!_matcher)
1296 : : return -ENOMEM;
1297 : :
1298 : 9562 : _matcher->type = type;
1299 : 9562 : _matcher->op = op;
1300 : 9562 : _matcher->negate = negate;
1301 : 9562 : _matcher->len = sizeof(struct bf_matcher) + payload_len;
1302 : 9562 : bf_memcpy(_matcher->payload, payload, payload_len);
1303 : :
1304 : 9562 : *matcher = TAKE_PTR(_matcher);
1305 : :
1306 : 9562 : return 0;
1307 : : }
1308 : :
1309 : 2639 : int bf_matcher_new_from_raw(struct bf_matcher **matcher,
1310 : : enum bf_matcher_type type, enum bf_matcher_op op,
1311 : : const char *payload, bool negate)
1312 : : {
1313 : 2639 : _free_bf_matcher_ struct bf_matcher *_matcher = NULL;
1314 : : const struct bf_matcher_ops *ops;
1315 : : int r;
1316 : :
1317 : : assert(matcher);
1318 : : assert(payload);
1319 : :
1320 : 2639 : ops = bf_matcher_get_ops(type, op);
1321 [ - + ]: 2639 : if (!ops) {
1322 [ # # ]: 0 : return bf_err_r(-ENOENT, "payload ops not found for '%s %s'",
1323 : : bf_matcher_type_to_str(type), bf_matcher_op_to_str(op));
1324 : : }
1325 : :
1326 : 2639 : _matcher = malloc(sizeof(*_matcher) + ops->ref_payload_size);
1327 [ + - ]: 2639 : if (!_matcher)
1328 : : return -ENOMEM;
1329 : :
1330 : 2639 : _matcher->type = type;
1331 : 2639 : _matcher->op = op;
1332 : 2639 : _matcher->negate = negate;
1333 : 2639 : _matcher->len = sizeof(*_matcher) + ops->ref_payload_size;
1334 : :
1335 : 2639 : r = ops->parse(_matcher->type, _matcher->op, &_matcher->payload, payload);
1336 [ + + ]: 2639 : if (r)
1337 : : return r;
1338 : :
1339 : 2194 : *matcher = TAKE_PTR(_matcher);
1340 : :
1341 : 2194 : return 0;
1342 : : }
1343 : :
1344 : 8122 : int bf_matcher_new_from_pack(struct bf_matcher **matcher, bf_rpack_node_t node)
1345 : : {
1346 : 8122 : _free_bf_matcher_ struct bf_matcher *_matcher = NULL;
1347 : : enum bf_matcher_type type;
1348 : : enum bf_matcher_op op;
1349 : : const void *payload;
1350 : : size_t payload_len;
1351 : 8122 : bool negate = false;
1352 : : int r;
1353 : :
1354 : : assert(matcher);
1355 : :
1356 [ - + - + : 8122 : r = bf_rpack_kv_enum(node, "type", &type, 0, _BF_MATCHER_TYPE_MAX);
- - ]
1357 : : if (r)
1358 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_matcher.type");
1359 : :
1360 [ - + - + : 8122 : r = bf_rpack_kv_enum(node, "op", &op, 0, _BF_MATCHER_OP_MAX);
- - ]
1361 : : if (r)
1362 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_matcher.op");
1363 : :
1364 [ + + ]: 8122 : if (bf_rpack_kv_contains(node, "negate")) {
1365 : 8121 : r = bf_rpack_kv_bool(node, "negate", &negate);
1366 [ - + ]: 8121 : if (r)
1367 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_matcher.negate");
1368 : : }
1369 : :
1370 : 8122 : r = bf_rpack_kv_bin(node, "payload", &payload, &payload_len);
1371 [ - + ]: 8122 : if (r)
1372 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_matcher.payload");
1373 : :
1374 : 8122 : r = bf_matcher_new(&_matcher, type, op, payload, payload_len, negate);
1375 [ - + ]: 8122 : if (r)
1376 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_matcher from pack");
1377 : :
1378 : 8122 : *matcher = TAKE_PTR(_matcher);
1379 : :
1380 : 8122 : return 0;
1381 : : }
1382 : :
1383 : 46430 : void bf_matcher_free(struct bf_matcher **matcher)
1384 : : {
1385 : : assert(matcher);
1386 : :
1387 [ + + ]: 46430 : if (!*matcher)
1388 : : return;
1389 : :
1390 : 12201 : free(*matcher);
1391 : 12201 : *matcher = NULL;
1392 : : }
1393 : :
1394 : 5615 : int bf_matcher_pack(const struct bf_matcher *matcher, bf_wpack_t *pack)
1395 : : {
1396 : : assert(matcher);
1397 : : assert(pack);
1398 : :
1399 : 5615 : bf_wpack_kv_int(pack, "type", matcher->type);
1400 : 5615 : bf_wpack_kv_int(pack, "op", matcher->op);
1401 : 5615 : bf_wpack_kv_bool(pack, "negate", matcher->negate);
1402 : 5615 : bf_wpack_kv_bin(pack, "payload", matcher->payload,
1403 : 5615 : matcher->len - sizeof(*matcher));
1404 : :
1405 [ - + ]: 5615 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
1406 : : }
1407 : :
1408 : 102 : void bf_matcher_dump(const struct bf_matcher *matcher, prefix_t *prefix)
1409 : : {
1410 : : assert(matcher);
1411 : : assert(prefix);
1412 : :
1413 [ - + ]: 102 : DUMP(prefix, "struct bf_matcher at %p", matcher);
1414 : :
1415 : 102 : bf_dump_prefix_push(prefix);
1416 : :
1417 [ - + ]: 102 : DUMP(prefix, "type: %s", bf_matcher_type_to_str(matcher->type));
1418 [ - + ]: 102 : DUMP(prefix, "op: %s", bf_matcher_op_to_str(matcher->op));
1419 [ - + - - ]: 102 : DUMP(prefix, "negate: %s", matcher->negate ? "true" : "false");
1420 [ - + ]: 102 : DUMP(prefix, "len: %ld", matcher->len);
1421 [ - + ]: 102 : DUMP(bf_dump_prefix_last(prefix), "payload:");
1422 : 102 : bf_dump_prefix_push(prefix);
1423 : 102 : bf_dump_hex(prefix, matcher->payload,
1424 : 102 : matcher->len - sizeof(struct bf_matcher));
1425 : 102 : bf_dump_prefix_pop(prefix);
1426 : :
1427 : 102 : bf_dump_prefix_pop(prefix);
1428 : 102 : }
1429 : :
1430 : 85798 : enum bf_matcher_type bf_matcher_get_type(const struct bf_matcher *matcher)
1431 : : {
1432 : : assert(matcher);
1433 : 85798 : return matcher->type;
1434 : : }
1435 : :
1436 : 5457 : enum bf_matcher_op bf_matcher_get_op(const struct bf_matcher *matcher)
1437 : : {
1438 : : assert(matcher);
1439 : 5457 : return matcher->op;
1440 : : }
1441 : :
1442 : 8912 : const void *bf_matcher_payload(const struct bf_matcher *matcher)
1443 : : {
1444 : : assert(matcher);
1445 : 8912 : return matcher->payload;
1446 : : }
1447 : :
1448 : 61 : size_t bf_matcher_payload_len(const struct bf_matcher *matcher)
1449 : : {
1450 : : assert(matcher);
1451 : 61 : return matcher->len - sizeof(*matcher);
1452 : : }
1453 : :
1454 : 1 : size_t bf_matcher_len(const struct bf_matcher *matcher)
1455 : : {
1456 : : assert(matcher);
1457 : 1 : return matcher->len;
1458 : : }
1459 : :
1460 : 2831 : bool bf_matcher_get_negate(const struct bf_matcher *matcher)
1461 : : {
1462 : : assert(matcher);
1463 : 2831 : return matcher->negate;
1464 : : }
1465 : :
1466 : : static const char *_bf_matcher_type_strs[] = {
1467 : : [BF_MATCHER_META_IFACE] = "meta.iface",
1468 : : [BF_MATCHER_META_L3_PROTO] = "meta.l3_proto",
1469 : : [BF_MATCHER_META_L4_PROTO] = "meta.l4_proto",
1470 : : [BF_MATCHER_META_PROBABILITY] = "meta.probability",
1471 : : [BF_MATCHER_META_SPORT] = "meta.sport",
1472 : : [BF_MATCHER_META_DPORT] = "meta.dport",
1473 : : [BF_MATCHER_META_MARK] = "meta.mark",
1474 : : [BF_MATCHER_META_FLOW_HASH] = "meta.flow_hash",
1475 : : [BF_MATCHER_META_FLOW_PROBABILITY] = "meta.flow_probability",
1476 : : [BF_MATCHER_IP4_SADDR] = "ip4.saddr",
1477 : : [BF_MATCHER_IP4_SNET] = "ip4.snet",
1478 : : [BF_MATCHER_IP4_DADDR] = "ip4.daddr",
1479 : : [BF_MATCHER_IP4_DNET] = "ip4.dnet",
1480 : : [BF_MATCHER_IP4_PROTO] = "ip4.proto",
1481 : : [BF_MATCHER_IP4_DSCP] = "ip4.dscp",
1482 : : [BF_MATCHER_IP6_SADDR] = "ip6.saddr",
1483 : : [BF_MATCHER_IP6_SNET] = "ip6.snet",
1484 : : [BF_MATCHER_IP6_DADDR] = "ip6.daddr",
1485 : : [BF_MATCHER_IP6_DNET] = "ip6.dnet",
1486 : : [BF_MATCHER_IP6_NEXTHDR] = "ip6.nexthdr",
1487 : : [BF_MATCHER_IP6_DSCP] = "ip6.dscp",
1488 : : [BF_MATCHER_TCP_SPORT] = "tcp.sport",
1489 : : [BF_MATCHER_TCP_DPORT] = "tcp.dport",
1490 : : [BF_MATCHER_TCP_FLAGS] = "tcp.flags",
1491 : : [BF_MATCHER_UDP_SPORT] = "udp.sport",
1492 : : [BF_MATCHER_UDP_DPORT] = "udp.dport",
1493 : : [BF_MATCHER_ICMP_TYPE] = "icmp.type",
1494 : : [BF_MATCHER_ICMP_CODE] = "icmp.code",
1495 : : [BF_MATCHER_ICMPV6_TYPE] = "icmpv6.type",
1496 : : [BF_MATCHER_ICMPV6_CODE] = "icmpv6.code",
1497 : : [BF_MATCHER_SET] = "<set>",
1498 : : };
1499 : :
1500 : : static_assert_enum_mapping(_bf_matcher_type_strs, _BF_MATCHER_TYPE_MAX);
1501 : :
1502 : 1271 : const char *bf_matcher_type_to_str(enum bf_matcher_type type)
1503 : : {
1504 [ + - ]: 1271 : if (type < 0 || _BF_MATCHER_TYPE_MAX <= type)
1505 : : return "<invalid>";
1506 : :
1507 : 1271 : return _bf_matcher_type_strs[type];
1508 : : }
1509 : :
1510 : 2832 : int bf_matcher_type_from_str(const char *str, enum bf_matcher_type *type)
1511 : : {
1512 : : assert(str);
1513 : : assert(type);
1514 : :
1515 [ + + ]: 47047 : for (size_t i = 0; i < _BF_MATCHER_TYPE_MAX; ++i) {
1516 [ + + ]: 47042 : if (bf_streq(_bf_matcher_type_strs[i], str)) {
1517 : 2827 : *type = i;
1518 : 2827 : return 0;
1519 : : }
1520 : : }
1521 : :
1522 : : return -EINVAL;
1523 : : }
1524 : :
1525 : : static const char *_bf_matcher_ops_strs[] = {
1526 : : [BF_MATCHER_EQ] = "eq", [BF_MATCHER_ANY] = "any",
1527 : : [BF_MATCHER_ALL] = "all", [BF_MATCHER_IN] = "in",
1528 : : [BF_MATCHER_RANGE] = "range",
1529 : : };
1530 : :
1531 : : static_assert_enum_mapping(_bf_matcher_ops_strs, _BF_MATCHER_OP_MAX);
1532 : :
1533 : 1070 : const char *bf_matcher_op_to_str(enum bf_matcher_op op)
1534 : : {
1535 : : assert(0 <= op && op < _BF_MATCHER_OP_MAX);
1536 : :
1537 : 1070 : return _bf_matcher_ops_strs[op];
1538 : : }
1539 : :
1540 : 1884 : int bf_matcher_op_from_str(const char *str, enum bf_matcher_op *op)
1541 : : {
1542 : : assert(str);
1543 : : assert(op);
1544 : :
1545 [ + + ]: 3660 : for (size_t i = 0; i < _BF_MATCHER_OP_MAX; ++i) {
1546 [ + + ]: 3657 : if (bf_streq(_bf_matcher_ops_strs[i], str)) {
1547 : 1881 : *op = i;
1548 : 1881 : return 0;
1549 : : }
1550 : : }
1551 : :
1552 : : return -EINVAL;
1553 : : }
1554 : :
1555 : : static const char *_bf_tcp_flags_strs[] = {
1556 : : [BF_TCP_FIN] = "fin", [BF_TCP_SYN] = "syn", [BF_TCP_RST] = "rst",
1557 : : [BF_TCP_PSH] = "psh", [BF_TCP_ACK] = "ack", [BF_TCP_URG] = "urg",
1558 : : [BF_TCP_ECE] = "ece", [BF_TCP_CWR] = "cwr",
1559 : : };
1560 : : static_assert_enum_mapping(_bf_tcp_flags_strs, _BF_TCP_MAX);
1561 : :
1562 : 10 : const char *bf_tcp_flag_to_str(enum bf_tcp_flag flag)
1563 : : {
1564 : : assert(0 <= flag && flag < _BF_TCP_MAX);
1565 : :
1566 : 10 : return _bf_tcp_flags_strs[flag];
1567 : : }
1568 : :
1569 : 326 : int bf_tcp_flag_from_str(const char *str, enum bf_tcp_flag *flag)
1570 : : {
1571 : : assert(str);
1572 : : assert(flag);
1573 : :
1574 [ + + ]: 1452 : for (size_t i = 0; i < _BF_TCP_MAX; ++i) {
1575 [ + + ]: 1441 : if (bf_streq_i(_bf_tcp_flags_strs[i], str)) {
1576 : 315 : *flag = i;
1577 : 315 : return 0;
1578 : : }
1579 : : }
1580 : :
1581 : : return -EINVAL;
1582 : : }
1583 : :
1584 : 4 : const char *bf_ethertype_to_str(uint16_t ethertype)
1585 : : {
1586 [ + + + ]: 4 : switch (ethertype) {
1587 : : case ETH_P_IP:
1588 : : return "ipv4";
1589 : 1 : case ETH_P_IPV6:
1590 : 1 : return "ipv6";
1591 : 1 : default:
1592 : 1 : return NULL;
1593 : : }
1594 : : }
1595 : :
1596 : 92 : int bf_ethertype_from_str(const char *str, uint16_t *ethertype)
1597 : : {
1598 : : assert(str);
1599 : : assert(ethertype);
1600 : :
1601 [ + + ]: 92 : if (bf_streq_i(str, "ipv4")) {
1602 : 29 : *ethertype = ETH_P_IP;
1603 : 29 : return 0;
1604 : : }
1605 : :
1606 [ + + ]: 63 : if (bf_streq_i(str, "ipv6")) {
1607 : 28 : *ethertype = ETH_P_IPV6;
1608 : 28 : return 0;
1609 : : }
1610 : :
1611 : : return -EINVAL;
1612 : : }
1613 : :
1614 : : static const char *_bf_ipproto_strs[UINT8_MAX + 1] = {
1615 : : [IPPROTO_HOPOPTS] = "hop", [IPPROTO_ICMP] = "icmp",
1616 : : [IPPROTO_IGMP] = "igmp", [IPPROTO_TCP] = "tcp",
1617 : : [IPPROTO_UDP] = "udp", [IPPROTO_ROUTING] = "routing",
1618 : : [IPPROTO_FRAGMENT] = "frag", [IPPROTO_AH] = "ah",
1619 : : [IPPROTO_DSTOPTS] = "dst", [IPPROTO_ICMPV6] = "icmpv6",
1620 : : [IPPROTO_MH] = "mh",
1621 : : };
1622 : :
1623 : 16 : const char *bf_ipproto_to_str(uint8_t ipproto)
1624 : : {
1625 : 16 : return _bf_ipproto_strs[ipproto];
1626 : : }
1627 : :
1628 : 368 : int bf_ipproto_from_str(const char *str, uint8_t *ipproto)
1629 : : {
1630 : : assert(str);
1631 : : assert(ipproto);
1632 : :
1633 [ + + ]: 29881 : for (size_t i = 0; i <= UINT8_MAX; ++i) {
1634 [ + + ]: 29780 : if (bf_streq_i(str, _bf_ipproto_strs[i])) {
1635 : 267 : *ipproto = (uint8_t)i;
1636 : 267 : return 0;
1637 : : }
1638 : : }
1639 : :
1640 : : return -EINVAL;
1641 : : }
1642 : :
1643 : : /* DSCP class name to codepoint mapping, based on the IANA DSCP
1644 : : * registry (https://www.iana.org/assignments/dscp-registry).
1645 : : * BE is an alias for CS0. */
1646 : : static const struct
1647 : : {
1648 : : const char *name;
1649 : : uint8_t dscp;
1650 : : } _bf_dscp_classes[] = {
1651 : : {"cs0", 0}, {"cs1", 8}, {"cs2", 16}, {"cs3", 24}, {"cs4", 32},
1652 : : {"cs5", 40}, {"cs6", 48}, {"cs7", 56}, {"af11", 10}, {"af12", 12},
1653 : : {"af13", 14}, {"af21", 18}, {"af22", 20}, {"af23", 22}, {"af31", 26},
1654 : : {"af32", 28}, {"af33", 30}, {"af41", 34}, {"af42", 36}, {"af43", 38},
1655 : : {"ef", 46}, {"voice-admit", 44}, {"le", 1}, {"nqb", 45}, {"be", 0},
1656 : : };
1657 : :
1658 : 5 : const char *bf_dscp_class_to_str(uint8_t dscp)
1659 : : {
1660 [ + + ]: 79 : for (size_t i = 0; i < ARRAY_SIZE(_bf_dscp_classes); ++i) {
1661 [ + + ]: 77 : if (_bf_dscp_classes[i].dscp == dscp)
1662 : 3 : return _bf_dscp_classes[i].name;
1663 : : }
1664 : :
1665 : : return NULL;
1666 : : }
1667 : :
1668 : 95 : int bf_dscp_class_from_str(const char *str, uint8_t *dscp)
1669 : : {
1670 : : assert(str);
1671 : : assert(dscp);
1672 : :
1673 [ + + ]: 2321 : for (size_t i = 0; i < ARRAY_SIZE(_bf_dscp_classes); ++i) {
1674 [ + + ]: 2245 : if (bf_streq_i(str, _bf_dscp_classes[i].name)) {
1675 : 19 : *dscp = _bf_dscp_classes[i].dscp;
1676 : 19 : return 0;
1677 : : }
1678 : : }
1679 : :
1680 : : return -EINVAL;
1681 : : }
1682 : :
1683 : : #define ICMP_ROUTERADVERT 9
1684 : : #define ICMP_ROUTERSOLICIT 10
1685 : :
1686 : : static const char *_bf_icmp_type_strs[UINT8_MAX + 1] = {
1687 : : [ICMP_ECHOREPLY] = "echo-reply",
1688 : : [ICMP_DEST_UNREACH] = "destination-unreachable",
1689 : : [ICMP_SOURCE_QUENCH] = "source-quench",
1690 : : [ICMP_REDIRECT] = "redirect",
1691 : : [ICMP_ECHO] = "echo-request",
1692 : : [ICMP_ROUTERADVERT] = "router-advertisement",
1693 : : [ICMP_ROUTERSOLICIT] = "router-solicitation",
1694 : : [ICMP_TIME_EXCEEDED] = "time-exceeded",
1695 : : [ICMP_PARAMETERPROB] = "parameter-problem",
1696 : : [ICMP_TIMESTAMP] = "timestamp-request",
1697 : : [ICMP_TIMESTAMPREPLY] = "timestamp-reply",
1698 : : [ICMP_INFO_REQUEST] = "info-request",
1699 : : [ICMP_INFO_REPLY] = "info-reply",
1700 : : [ICMP_ADDRESS] = "address-mask-request",
1701 : : [ICMP_ADDRESSREPLY] = "address-mask-reply",
1702 : : };
1703 : :
1704 : 6 : const char *bf_icmp_type_to_str(uint8_t type)
1705 : : {
1706 : 6 : return _bf_icmp_type_strs[type];
1707 : : }
1708 : :
1709 : 91 : int bf_icmp_type_from_str(const char *str, uint8_t *type)
1710 : : {
1711 : : assert(str);
1712 : : assert(type);
1713 : :
1714 [ + + ]: 14765 : for (size_t i = 0; i <= UINT8_MAX; ++i) {
1715 [ + + ]: 14708 : if (bf_streq_i(str, _bf_icmp_type_strs[i])) {
1716 : 34 : *type = (uint8_t)i;
1717 : 34 : return 0;
1718 : : }
1719 : : }
1720 : :
1721 : : return -EINVAL;
1722 : : }
1723 : :
1724 : : #define ICMPV6_ND_ROUTERSOLICIT 133
1725 : : #define ICMPV6_ND_ROUTERADVERT 134
1726 : : #define ICMPV6_ND_NEIGHSOLICIT 135
1727 : : #define ICMPV6_ND_NEIGHADVERT 136
1728 : :
1729 : : static const char *_bf_icmpv6_type_strs[UINT8_MAX + 1] = {
1730 : : [ICMPV6_DEST_UNREACH] = "destination-unreachable",
1731 : : [ICMPV6_PKT_TOOBIG] = "packet-too-big",
1732 : : [ICMPV6_TIME_EXCEED] = "time-exceeded",
1733 : : [ICMPV6_PARAMPROB] = "parameter-problem",
1734 : : [ICMPV6_ECHO_REQUEST] = "echo-request",
1735 : : [ICMPV6_ECHO_REPLY] = "echo-reply",
1736 : : [ICMPV6_MGM_QUERY] = "mld-listener-query",
1737 : : [ICMPV6_MGM_REPORT] = "mld-listener-report",
1738 : : [ICMPV6_MGM_REDUCTION] = "mld-listener-reduction",
1739 : : [ICMPV6_ND_ROUTERSOLICIT] = "nd-router-solicit",
1740 : : [ICMPV6_ND_ROUTERADVERT] = "nd-router-advert",
1741 : : [ICMPV6_ND_NEIGHSOLICIT] = "nd-neighbor-solicit",
1742 : : [ICMPV6_ND_NEIGHADVERT] = "nd-neighbor-advert",
1743 : : [ICMPV6_MLD2_REPORT] = "mld2-listener-report",
1744 : : };
1745 : :
1746 : 4 : const char *bf_icmpv6_type_to_str(uint8_t type)
1747 : : {
1748 : 4 : return _bf_icmpv6_type_strs[type];
1749 : : }
1750 : :
1751 : 99 : int bf_icmpv6_type_from_str(const char *str, uint8_t *type)
1752 : : {
1753 : : assert(str);
1754 : : assert(type);
1755 : :
1756 [ + + ]: 20230 : for (size_t i = 0; i <= UINT8_MAX; ++i) {
1757 [ + + ]: 20171 : if (bf_streq_i(str, _bf_icmpv6_type_strs[i])) {
1758 : 40 : *type = (uint8_t)i;
1759 : 40 : return 0;
1760 : : }
1761 : : }
1762 : :
1763 : : return -EINVAL;
1764 : : }
|