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