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