LCOV - code coverage report
Current view: top level - libbpfilter - matcher.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 18.4 % 403 74
Test Date: 2025-09-16 14:46:44 Functions: 23.6 % 55 13

            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 <ctype.h>
      19              : #include <endian.h>
      20              : #include <errno.h>
      21              : #include <inttypes.h>
      22              : #include <limits.h>
      23              : #include <stdint.h>
      24              : #include <stdlib.h>
      25              : #include <string.h>
      26              : 
      27              : #include "bpfilter/dump.h"
      28              : #include "bpfilter/helper.h"
      29              : #include "bpfilter/if.h"
      30              : #include "bpfilter/logger.h"
      31              : #include "bpfilter/pack.h"
      32              : #include "bpfilter/runtime.h"
      33              : 
      34              : #define INET4_ADDRSTRLEN 16
      35              : #define INET6_ADDRSTRLEN 46
      36              : 
      37              : #define BF_PAYLOAD_OPS(type, size, parser_cb, printer_cb)                      \
      38              :     [type] = {size, parser_cb, printer_cb}
      39              : 
      40              : extern int inet_pton(int, const char *, void *);
      41              : extern const char *inet_ntop(int, const void *, char *, socklen_t);
      42              : 
      43              : /**
      44              :  * Matcher definition.
      45              :  *
      46              :  * Matchers are criterias to match the packet against. A set of matcher defines
      47              :  * what a rule should match on.
      48              :  *
      49              :  * @todo `bf_matcher`'s payload should be a union of all the possible payload
      50              :  * types.
      51              :  */
      52              : struct bf_matcher
      53              : {
      54              :     /// Matcher type.
      55              :     enum bf_matcher_type type;
      56              :     /// Comparison operator.
      57              :     enum bf_matcher_op op;
      58              :     /// Total matcher size (including payload).
      59              :     size_t len;
      60              :     /// Payload to match the packet against (if any).
      61              :     uint8_t payload[];
      62              : };
      63              : 
      64            0 : int _bf_parse_iface(enum bf_matcher_type type, enum bf_matcher_op op,
      65              :                     void *payload, const char *raw_payload)
      66              : {
      67            0 :     bf_assert(payload && raw_payload);
      68              : 
      69              :     int idx;
      70              :     unsigned long ifindex;
      71              :     char *endptr;
      72              : 
      73            0 :     idx = bf_if_index_from_name(raw_payload);
      74            0 :     if (idx > 0) {
      75            0 :         *(uint32_t *)payload = (uint32_t)idx;
      76            0 :         return 0;
      77              :     }
      78              : 
      79            0 :     ifindex = strtoul(raw_payload, &endptr, BF_BASE_10);
      80            0 :     if (*endptr == '\0' && 0 < ifindex && ifindex <= UINT32_MAX) {
      81            0 :         *(uint32_t *)payload = (uint32_t)ifindex;
      82            0 :         return 0;
      83              :     }
      84              : 
      85            0 :     bf_err(
      86              :         "\"%s %s\" expects an interface name (e.g., \"eth0\", \"wlan0\") or a decimal interface index (e.g., \"1\", \"2\"), not '%s'",
      87              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
      88              : 
      89              :     return -EINVAL;
      90              : }
      91              : 
      92            0 : void _bf_print_iface(const void *payload)
      93              : {
      94            0 :     bf_assert(payload);
      95              : 
      96              :     const char *ifname;
      97            0 :     uint32_t ifindex = *(uint32_t *)payload;
      98              : 
      99            0 :     ifname = bf_if_name_from_index((int)ifindex);
     100            0 :     if (ifname)
     101            0 :         (void)fprintf(stdout, "%s", ifname);
     102              :     else
     103            0 :         (void)fprintf(stdout, "%" PRIu32, ifindex);
     104            0 : }
     105              : 
     106            0 : int _bf_parse_l3_proto(enum bf_matcher_type type, enum bf_matcher_op op,
     107              :                        void *payload, const char *raw_payload)
     108              : {
     109            0 :     bf_assert(payload && raw_payload);
     110              : 
     111              :     unsigned long ethertype;
     112              :     char *endptr;
     113              :     int r;
     114              : 
     115            0 :     r = bf_ethertype_from_str(raw_payload, payload);
     116            0 :     if (!r)
     117              :         return 0;
     118              : 
     119            0 :     ethertype = strtoul(raw_payload, &endptr, BF_BASE_10);
     120            0 :     if (*endptr == '\0' && ethertype <= UINT16_MAX) {
     121            0 :         *(uint16_t *)payload = (uint16_t)ethertype;
     122            0 :         return 0;
     123              :     }
     124              : 
     125            0 :     ethertype = strtoul(raw_payload, &endptr, BF_BASE_16);
     126            0 :     if (*endptr == '\0' && ethertype <= UINT16_MAX) {
     127            0 :         *(uint16_t *)payload = (uint16_t)ethertype;
     128            0 :         return 0;
     129              :     }
     130              : 
     131            0 :     bf_err(
     132              :         "\"%s %s\" expects an internet layer protocol name (e.g. \"IPv6\", case insensitive), or a valid decimal or hexadecimal IEEE 802 number, not '%s'",
     133              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     134              : 
     135              :     return -EINVAL;
     136              : }
     137              : 
     138            0 : void _bf_print_l3_proto(const void *payload)
     139              : {
     140            0 :     bf_assert(payload);
     141              : 
     142            0 :     const char *ethertype = bf_ethertype_to_str(*(uint16_t *)payload);
     143              : 
     144            0 :     if (ethertype)
     145            0 :         (void)fprintf(stdout, "%s", ethertype);
     146              :     else
     147            0 :         (void)fprintf(stdout, "0x%04" PRIx16, *(uint16_t *)payload);
     148            0 : }
     149              : 
     150           24 : int _bf_parse_l4_proto(enum bf_matcher_type type, enum bf_matcher_op op,
     151              :                        void *payload, const char *raw_payload)
     152              : {
     153           24 :     bf_assert(payload && raw_payload);
     154              : 
     155              :     unsigned long ipproto;
     156              :     char *endptr;
     157              :     int r;
     158              : 
     159           24 :     r = bf_ipproto_from_str(raw_payload, payload);
     160           24 :     if (!r)
     161              :         return 0;
     162              : 
     163            0 :     ipproto = strtoul(raw_payload, &endptr, BF_BASE_10);
     164            0 :     if (*endptr == '\0' && ipproto <= UINT8_MAX) {
     165            0 :         *(uint8_t *)payload = (uint8_t)ipproto;
     166            0 :         return 0;
     167              :     }
     168              : 
     169            0 :     bf_err(
     170              :         "\"%s %s\" expects a transport layer protocol name (e.g. \"ICMP\", case insensitive), or a valid decimal internet protocol number, not '%s'",
     171              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     172              : 
     173              :     return -EINVAL;
     174              : }
     175              : 
     176            0 : void _bf_print_l4_proto(const void *payload)
     177              : {
     178            0 :     bf_assert(payload);
     179              : 
     180            0 :     const char *ipproto = bf_ipproto_to_str(*(uint8_t *)payload);
     181              : 
     182            0 :     if (ipproto)
     183            0 :         (void)fprintf(stdout, "%s", ipproto);
     184              :     else
     185            0 :         (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
     186            0 : }
     187              : 
     188            0 : int _bf_parse_l4_port(enum bf_matcher_type type, enum bf_matcher_op op,
     189              :                       void *payload, const char *raw_payload)
     190              : {
     191            0 :     bf_assert(payload && raw_payload);
     192              : 
     193              :     unsigned long port;
     194              :     char *endptr;
     195              : 
     196            0 :     port = strtoul(raw_payload, &endptr, BF_BASE_10);
     197            0 :     if (*endptr == '\0' && port <= UINT16_MAX) {
     198            0 :         *(uint16_t *)payload = htobe16((uint16_t)port);
     199            0 :         return 0;
     200              :     }
     201              : 
     202            0 :     bf_err("\"%s %s\" expects a valid decimal port number, not '%s'",
     203              :            bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     204              : 
     205              :     return -EINVAL;
     206              : }
     207              : 
     208            0 : void _bf_print_l4_port(const void *payload)
     209              : {
     210            0 :     bf_assert(payload);
     211              : 
     212            0 :     (void)fprintf(stdout, "%" PRIu16, (uint16_t)be16toh(*(uint16_t *)payload));
     213            0 : }
     214              : 
     215              : #define BF_PORT_RANGE_MAX_LEN 16 // 65535-65535, with nul char, round to **2
     216              : 
     217            0 : static int _bf_parse_l4_port_range(enum bf_matcher_type type,
     218              :                                    enum bf_matcher_op op, void *payload,
     219              :                                    const char *raw_payload)
     220              : {
     221            0 :     bf_assert(payload && raw_payload);
     222              : 
     223              :     uint16_t *ports = (uint16_t *)payload;
     224              :     unsigned long port;
     225              :     char buf[BF_PORT_RANGE_MAX_LEN];
     226              :     char *first;
     227              :     char *second;
     228              :     char *endptr;
     229              : 
     230            0 :     bf_strncpy(buf, BF_PORT_RANGE_MAX_LEN, raw_payload);
     231              : 
     232            0 :     if (!isdigit(*raw_payload))
     233            0 :         goto err;
     234              : 
     235            0 :     first = strtok_r(buf, "-", &second);
     236            0 :     if (!first)
     237            0 :         goto err;
     238              : 
     239            0 :     if (!*second)
     240            0 :         goto err;
     241              : 
     242            0 :     port = strtoul(first, &endptr, BF_BASE_10);
     243            0 :     if (*endptr != '\0' || port > UINT16_MAX)
     244            0 :         goto err;
     245            0 :     ports[0] = (uint16_t)port;
     246              : 
     247            0 :     port = strtoul(second, &endptr, BF_BASE_10);
     248            0 :     if (*endptr != '\0' || port > UINT16_MAX)
     249            0 :         goto err;
     250            0 :     ports[1] = (uint16_t)port;
     251              : 
     252            0 :     if (ports[1] < ports[0])
     253            0 :         goto err;
     254              : 
     255              :     return 0;
     256              : 
     257            0 : err:
     258            0 :     bf_err(
     259              :         "\"%s %s\" expects two positive decimal port numbers as `$START-$END`, with `$START <= $END`, not '%s'",
     260              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     261              : 
     262              :     return -EINVAL;
     263              : }
     264              : 
     265            0 : void _bf_print_l4_port_range(const void *payload)
     266              : {
     267            0 :     bf_assert(payload);
     268              : 
     269              :     uint16_t *ports = (uint16_t *)payload;
     270              : 
     271            0 :     (void)fprintf(stdout, "%" PRIu16 "-%" PRIu16, ports[0], ports[1]);
     272            0 : }
     273              : 
     274            0 : static int _bf_parse_probability(enum bf_matcher_type type,
     275              :                                  enum bf_matcher_op op, void *payload,
     276              :                                  const char *raw_payload)
     277              : {
     278            0 :     bf_assert(payload && raw_payload);
     279              : 
     280              :     unsigned long proba;
     281              :     char *endptr;
     282              : 
     283            0 :     proba = strtoul(raw_payload, &endptr, BF_BASE_10);
     284            0 :     if (endptr[0] == '%' && endptr[1] == '\0' && proba <= 100) {
     285            0 :         *(uint8_t *)payload = (uint8_t)proba;
     286            0 :         return 0;
     287              :     }
     288              : 
     289            0 :     bf_err(
     290              :         "\"%s %s\" expects a valid decimal percentage value (i.e., within [0%%, 100%%]), not '%s'",
     291              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     292              : 
     293              :     return -EINVAL;
     294              : }
     295              : 
     296            0 : void _bf_print_probability(const void *payload)
     297              : {
     298            0 :     bf_assert(payload);
     299              : 
     300            0 :     (void)fprintf(stdout, "%" PRIu8 "%%", *(uint8_t *)payload);
     301            0 : }
     302              : 
     303           20 : static int _bf_parse_ipv4_addr(enum bf_matcher_type type, enum bf_matcher_op op,
     304              :                                void *payload, const char *raw_payload)
     305              : {
     306           20 :     bf_assert(payload && raw_payload);
     307              : 
     308              :     int r;
     309              : 
     310           20 :     r = inet_pton(AF_INET, raw_payload, payload);
     311           20 :     if (r == 1)
     312              :         return 0;
     313              : 
     314            0 :     bf_err(
     315              :         "\"%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' ",
     316              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     317              : 
     318              :     return -EINVAL;
     319              : }
     320              : 
     321            0 : void _bf_print_ipv4_addr(const void *payload)
     322              : {
     323            0 :     bf_assert(payload);
     324              : 
     325              :     char str[INET4_ADDRSTRLEN];
     326              : 
     327            0 :     if (inet_ntop(AF_INET, payload, str, INET4_ADDRSTRLEN))
     328            0 :         (void)fprintf(stdout, "%s", str);
     329              :     else
     330            0 :         (void)fprintf(stdout, "<failed to print IPv4 address>");
     331            0 : }
     332              : 
     333              : #define BF_IPV4_NET_MAX_LEN                                                    \
     334              :     32 // 255.255.255.255/32, with nul char, round to **2
     335              : 
     336            0 : static int _bf_parse_ipv4_net(enum bf_matcher_type type, enum bf_matcher_op op,
     337              :                               void *payload, const char *raw_payload)
     338              : {
     339            0 :     bf_assert(payload && raw_payload);
     340              : 
     341              :     struct bf_ip4_lpm_key *addr = payload;
     342              :     char buf[BF_IPV4_NET_MAX_LEN];
     343              :     char *strip, *strmask, *endptr;
     344              :     int r;
     345              : 
     346            0 :     bf_strncpy(buf, BF_IPV4_NET_MAX_LEN, raw_payload);
     347              : 
     348            0 :     if (!isdigit(*raw_payload))
     349            0 :         goto err;
     350              : 
     351            0 :     strip = strtok_r(buf, "/", &strmask);
     352            0 :     if (!strip || !*strmask)
     353            0 :         goto err;
     354              : 
     355            0 :     r = inet_pton(AF_INET, strip, &addr->data);
     356            0 :     if (r != 1)
     357            0 :         goto err;
     358              : 
     359            0 :     addr->prefixlen = strtoul(strmask, &endptr, BF_BASE_10);
     360            0 :     if (*endptr != '\0' || addr->prefixlen > 32)
     361            0 :         goto err;
     362              : 
     363              :     return 0;
     364              : 
     365            0 : err:
     366            0 :     bf_err(
     367              :         "\"%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' ",
     368              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     369              : 
     370              :     return -EINVAL;
     371              : }
     372              : 
     373            0 : void _bf_print_ipv4_net(const void *payload)
     374              : {
     375            0 :     bf_assert(payload);
     376              : 
     377              :     char str[INET4_ADDRSTRLEN];
     378              :     const struct bf_ip4_lpm_key *addr = payload;
     379              : 
     380            0 :     if (inet_ntop(AF_INET, &addr->data, str, INET4_ADDRSTRLEN))
     381            0 :         (void)fprintf(stdout, "%s/%u", str, addr->prefixlen);
     382              :     else
     383            0 :         (void)fprintf(stdout, "<failed to print IPv4 network>");
     384            0 : }
     385              : 
     386            0 : static int _bf_parse_ipv6_addr(enum bf_matcher_type type, enum bf_matcher_op op,
     387              :                                void *payload, const char *raw_payload)
     388              : {
     389            0 :     bf_assert(payload && raw_payload);
     390              : 
     391              :     int r;
     392              : 
     393            0 :     r = inet_pton(AF_INET6, raw_payload, payload);
     394            0 :     if (r == 1)
     395              :         return 0;
     396              : 
     397            0 :     bf_err(
     398              :         "\"%s %s\" expects an IPv6 address composed of 8 hexadecimal numbers (abbreviations are supported), not '%s' ",
     399              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     400              : 
     401              :     return -EINVAL;
     402              : }
     403              : 
     404            0 : void _bf_print_ipv6_addr(const void *payload)
     405              : {
     406            0 :     bf_assert(payload);
     407              : 
     408              :     char str[INET6_ADDRSTRLEN];
     409              : 
     410            0 :     if (inet_ntop(AF_INET6, payload, str, INET6_ADDRSTRLEN))
     411            0 :         (void)fprintf(stdout, "%s", str);
     412              :     else
     413            0 :         (void)fprintf(stdout, "<failed to print IPv6 address>");
     414            0 : }
     415              : 
     416              : #define BF_IPV6_NET_MAX_LEN (INET6_ADDRSTRLEN + 4)
     417              : 
     418            0 : static int _bf_parse_ipv6_net(enum bf_matcher_type type, enum bf_matcher_op op,
     419              :                               void *payload, const char *raw_payload)
     420              : {
     421            0 :     bf_assert(payload && raw_payload);
     422              : 
     423              :     struct bf_ip6_lpm_key *addr = payload;
     424              :     char buf[BF_IPV6_NET_MAX_LEN];
     425              :     char *strip, *strmask, *endptr;
     426              :     int r;
     427              : 
     428            0 :     bf_strncpy(buf, BF_IPV6_NET_MAX_LEN, raw_payload);
     429              : 
     430            0 :     if (!isalpha(*raw_payload) && !isdigit(*raw_payload) && *raw_payload != ':')
     431            0 :         goto err;
     432              : 
     433            0 :     strip = strtok_r(buf, "/", &strmask);
     434            0 :     if (!strip || !*strmask)
     435            0 :         goto err;
     436              : 
     437            0 :     r = inet_pton(AF_INET6, strip, &addr->data);
     438            0 :     if (r != 1)
     439            0 :         goto err;
     440              : 
     441            0 :     addr->prefixlen = strtoul(strmask, &endptr, BF_BASE_10);
     442            0 :     if (*endptr != '\0' || addr->prefixlen > 128)
     443            0 :         goto err;
     444              : 
     445              :     return 0;
     446              : 
     447            0 : err:
     448            0 :     bf_err(
     449              :         "\"%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' ",
     450              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     451              : 
     452              :     return -EINVAL;
     453              : }
     454              : 
     455            0 : void _bf_print_ipv6_net(const void *payload)
     456              : {
     457            0 :     bf_assert(payload);
     458              : 
     459              :     const struct bf_ip6_lpm_key *addr = payload;
     460              :     char str[INET6_ADDRSTRLEN];
     461              : 
     462            0 :     if (inet_ntop(AF_INET6, addr->data, str, INET6_ADDRSTRLEN))
     463            0 :         (void)fprintf(stdout, "%s/%u", str, addr->prefixlen);
     464              :     else
     465            0 :         (void)fprintf(stdout, "<failed to print IPv6 address>");
     466            0 : }
     467              : 
     468            0 : static int _bf_parse_tcp_flags(enum bf_matcher_type type, enum bf_matcher_op op,
     469              :                                void *payload, const char *raw_payload)
     470              : {
     471            0 :     bf_assert(payload && raw_payload);
     472              : 
     473              :     _cleanup_free_ char *_raw_payload = NULL;
     474              :     char *tmp;
     475              :     char *saveptr;
     476              :     char *token;
     477              :     uint8_t *flags = payload;
     478              : 
     479            0 :     _raw_payload = strdup(raw_payload);
     480              :     if (!raw_payload)
     481              :         goto err;
     482              : 
     483            0 :     *flags = 0;
     484              :     tmp = _raw_payload;
     485              : 
     486            0 :     while ((token = strtok_r(tmp, ",", &saveptr))) {
     487              :         enum bf_tcp_flag new_flag;
     488              :         int r;
     489              : 
     490            0 :         r = bf_tcp_flag_from_str(token, &new_flag);
     491            0 :         if (r)
     492            0 :             goto err;
     493              : 
     494            0 :         *flags |= (uint8_t)(1 << new_flag);
     495              : 
     496              :         tmp = NULL;
     497              :     }
     498              : 
     499              :     return 0;
     500              : 
     501              : err:
     502            0 :     bf_err(
     503              :         "\"%s %s\" expects a comma-separated list of one or more TCP flags (fin, syn, rst, psh, ack, urg, ece, or cwr), not '%s' ",
     504              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     505              : 
     506              :     return -EINVAL;
     507              : }
     508              : 
     509            0 : void _bf_print_tcp_flags(const void *payload)
     510              : {
     511            0 :     bf_assert(payload);
     512              : 
     513            0 :     uint8_t flag = *(uint8_t *)payload;
     514              : 
     515            0 :     for (uint32_t i = 0; i < _BF_TCP_MAX; ++i) {
     516            0 :         if (flag & (1 << i)) {
     517            0 :             flag &= ~(1 << i);
     518            0 :             (void)fprintf(stdout, "%s%s", bf_tcp_flag_to_str(i),
     519              :                           flag ? "," : "");
     520              :         }
     521              :     }
     522            0 : }
     523              : 
     524            0 : static int _bf_parse_icmp_type(enum bf_matcher_type type, enum bf_matcher_op op,
     525              :                                void *payload, const char *raw_payload)
     526              : {
     527            0 :     bf_assert(payload && raw_payload);
     528              : 
     529              :     unsigned long icmptype;
     530              :     char *endptr;
     531              :     int r;
     532              : 
     533            0 :     r = bf_icmp_type_from_str(raw_payload, payload);
     534            0 :     if (!r)
     535              :         return 0;
     536              : 
     537            0 :     icmptype = strtoul(raw_payload, &endptr, BF_BASE_10);
     538              : 
     539            0 :     if (*endptr == '\0' && icmptype <= UINT8_MAX) {
     540            0 :         *(uint8_t *)payload = (uint8_t)icmptype;
     541            0 :         return 0;
     542              :     }
     543              : 
     544            0 :     icmptype = strtoul(raw_payload, &endptr, BF_BASE_16);
     545            0 :     if (*endptr == '\0' && icmptype <= UINT8_MAX) {
     546            0 :         *(uint8_t *)payload = (uint8_t)icmptype;
     547            0 :         return 0;
     548              :     }
     549              : 
     550            0 :     bf_err(
     551              :         "\"%s %s\" expects an ICMP type name (e.g. \"echo-reply\", case insensitive), or or a decimal or hexadecimal ICMP type value, not '%s'",
     552              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     553              : 
     554              :     return -EINVAL;
     555              : }
     556              : 
     557            0 : void _bf_print_icmp_type(const void *payload)
     558              : {
     559            0 :     bf_assert(payload);
     560              : 
     561            0 :     const char *type = bf_icmp_type_to_str(*(uint8_t *)payload);
     562              : 
     563            0 :     if (type)
     564            0 :         (void)fprintf(stdout, "%s", type);
     565              :     else
     566            0 :         (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
     567            0 : }
     568              : 
     569            0 : static int _bf_parse_icmp_code(enum bf_matcher_type type, enum bf_matcher_op op,
     570              :                                void *payload, const char *raw_payload)
     571              : {
     572            0 :     bf_assert(payload && raw_payload);
     573              : 
     574              :     unsigned long code;
     575              :     char *endptr;
     576              : 
     577            0 :     code = strtoul(raw_payload, &endptr, BF_BASE_10);
     578            0 :     if (*endptr == '\0' && code <= UINT8_MAX) {
     579            0 :         *(uint8_t *)payload = (uint8_t)code;
     580            0 :         return 0;
     581              :     }
     582              : 
     583            0 :     code = strtoul(raw_payload, &endptr, BF_BASE_16);
     584            0 :     if (*endptr == '\0' && code <= UINT8_MAX) {
     585            0 :         *(uint8_t *)payload = (uint8_t)code;
     586            0 :         return 0;
     587              :     }
     588              : 
     589            0 :     bf_err(
     590              :         "\"%s %s\" expects a decimal or hexadecimal ICMP or ICMPv6 code value, not '%s'",
     591              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     592              : 
     593              :     return -EINVAL;
     594              : }
     595              : 
     596            0 : void _bf_print_icmp_code(const void *payload)
     597              : {
     598            0 :     bf_assert(payload);
     599              : 
     600            0 :     (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
     601            0 : }
     602              : 
     603            0 : static int _bf_parse_icmpv6_type(enum bf_matcher_type type,
     604              :                                  enum bf_matcher_op op, void *payload,
     605              :                                  const char *raw_payload)
     606              : {
     607            0 :     bf_assert(payload && raw_payload);
     608              : 
     609              :     unsigned long icmptype;
     610              :     char *endptr;
     611              :     int r;
     612              : 
     613            0 :     r = bf_icmpv6_type_from_str(raw_payload, payload);
     614            0 :     if (!r)
     615              :         return 0;
     616              : 
     617            0 :     icmptype = strtoul(raw_payload, &endptr, BF_BASE_10);
     618              : 
     619            0 :     if (*endptr == '\0' && icmptype <= UINT8_MAX) {
     620            0 :         *(uint8_t *)payload = (uint8_t)icmptype;
     621            0 :         return 0;
     622              :     }
     623              : 
     624            0 :     icmptype = strtoul(raw_payload, &endptr, BF_BASE_16);
     625            0 :     if (*endptr == '\0' && icmptype <= UINT8_MAX) {
     626            0 :         *(uint8_t *)payload = (uint8_t)icmptype;
     627            0 :         return 0;
     628              :     }
     629              : 
     630            0 :     bf_err(
     631              :         "\"%s %s\" expects an ICMPv6 type name (e.g. \"echo-reply\", case insensitive), or a decimal or hexadecimal ICMPv6 type value, not '%s'",
     632              :         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op), raw_payload);
     633              : 
     634              :     return -EINVAL;
     635              : }
     636              : 
     637            0 : void _bf_print_icmpv6_type(const void *payload)
     638              : {
     639            0 :     bf_assert(payload);
     640              : 
     641            0 :     const char *type = bf_icmpv6_type_to_str(*(uint8_t *)payload);
     642              : 
     643            0 :     if (type)
     644            0 :         (void)fprintf(stdout, "%s", type);
     645              :     else
     646            0 :         (void)fprintf(stdout, "%" PRIu8, *(uint8_t *)payload);
     647            0 : }
     648              : 
     649              : #define BF_MATCHER_OPS(op, payload_size, parse_cb, print_cb)                   \
     650              :     [op] = {payload_size, parse_cb, print_cb}
     651              : 
     652              : #define _BF_TCP_FLAGS_OFFSET 13
     653              : 
     654              : static struct bf_matcher_meta _bf_matcher_metas[_BF_MATCHER_TYPE_MAX] = {
     655              :     [BF_MATCHER_META_IFACE] =
     656              :         {
     657              :             .layer = BF_MATCHER_NO_LAYER,
     658              :             .ops =
     659              :                 {
     660              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t),
     661              :                                    _bf_parse_iface, _bf_print_iface),
     662              :                 },
     663              :         },
     664              :     [BF_MATCHER_META_L3_PROTO] =
     665              :         {
     666              :             .layer = BF_MATCHER_NO_LAYER,
     667              :             .ops =
     668              :                 {
     669              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
     670              :                                    _bf_parse_l3_proto, _bf_print_l3_proto),
     671              :                 },
     672              :         },
     673              :     [BF_MATCHER_META_L4_PROTO] =
     674              :         {
     675              :             .layer = BF_MATCHER_NO_LAYER,
     676              :             .ops =
     677              :                 {
     678              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
     679              :                                    _bf_parse_l4_proto, _bf_print_l4_proto),
     680              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint16_t),
     681              :                                    _bf_parse_l4_proto, _bf_print_l4_proto),
     682              :                 },
     683              :         },
     684              :     [BF_MATCHER_META_SPORT] =
     685              :         {
     686              :             .layer = BF_MATCHER_NO_LAYER,
     687              :             .ops =
     688              :                 {
     689              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
     690              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     691              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint16_t),
     692              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     693              :                     BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
     694              :                                    _bf_parse_l4_port_range,
     695              :                                    _bf_print_l4_port_range),
     696              :                 },
     697              :         },
     698              :     [BF_MATCHER_META_DPORT] =
     699              :         {
     700              :             .layer = BF_MATCHER_NO_LAYER,
     701              :             .ops =
     702              :                 {
     703              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
     704              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     705              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint16_t),
     706              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     707              :                     BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
     708              :                                    _bf_parse_l4_port_range,
     709              :                                    _bf_print_l4_port_range),
     710              :                 },
     711              :         },
     712              :     [BF_MATCHER_META_PROBABILITY] =
     713              :         {
     714              :             .layer = BF_MATCHER_NO_LAYER,
     715              :             .ops =
     716              :                 {
     717              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
     718              :                                    _bf_parse_probability,
     719              :                                    _bf_print_probability),
     720              :                 },
     721              :         },
     722              :     [BF_MATCHER_IP4_SADDR] =
     723              :         {
     724              :             .layer = BF_MATCHER_LAYER_3,
     725              :             .hdr_id = ETH_P_IP,
     726              :             .hdr_payload_size = sizeof(uint32_t),
     727              :             .hdr_payload_offset = offsetof(struct iphdr, saddr),
     728              :             .ops =
     729              :                 {
     730              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t),
     731              :                                    _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
     732              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint32_t),
     733              :                                    _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
     734              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint32_t),
     735              :                                    _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
     736              :                 },
     737              :         },
     738              :     [BF_MATCHER_IP4_DADDR] =
     739              :         {
     740              :             .layer = BF_MATCHER_LAYER_3,
     741              :             .hdr_id = ETH_P_IP,
     742              :             .hdr_payload_size = sizeof(uint32_t),
     743              :             .hdr_payload_offset = offsetof(struct iphdr, daddr),
     744              :             .ops =
     745              :                 {
     746              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint32_t),
     747              :                                    _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
     748              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint32_t),
     749              :                                    _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
     750              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint32_t),
     751              :                                    _bf_parse_ipv4_addr, _bf_print_ipv4_addr),
     752              :                 },
     753              :         },
     754              :     [BF_MATCHER_IP4_SNET] =
     755              :         {
     756              :             .layer = BF_MATCHER_LAYER_3,
     757              :             .hdr_id = ETH_P_IP,
     758              :             .hdr_payload_size = sizeof(uint32_t),
     759              :             .hdr_payload_offset = offsetof(struct iphdr, saddr),
     760              :             .ops =
     761              :                 {
     762              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct bf_ip4_lpm_key),
     763              :                                    _bf_parse_ipv4_net, _bf_print_ipv4_net),
     764              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(struct bf_ip4_lpm_key),
     765              :                                    _bf_parse_ipv4_net, _bf_print_ipv4_net),
     766              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct bf_ip4_lpm_key),
     767              :                                    _bf_parse_ipv4_net, _bf_print_ipv4_net),
     768              :                 },
     769              :         },
     770              :     [BF_MATCHER_IP4_DNET] =
     771              :         {
     772              :             .layer = BF_MATCHER_LAYER_3,
     773              :             .hdr_id = ETH_P_IP,
     774              :             .hdr_payload_size = sizeof(uint32_t),
     775              :             .hdr_payload_offset = offsetof(struct iphdr, daddr),
     776              :             .ops =
     777              :                 {
     778              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct bf_ip4_lpm_key),
     779              :                                    _bf_parse_ipv4_net, _bf_print_ipv4_net),
     780              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(struct bf_ip4_lpm_key),
     781              :                                    _bf_parse_ipv4_net, _bf_print_ipv4_net),
     782              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct bf_ip4_lpm_key),
     783              :                                    _bf_parse_ipv4_net, _bf_print_ipv4_net),
     784              :                 },
     785              :         },
     786              :     [BF_MATCHER_IP4_PROTO] =
     787              :         {
     788              :             .layer = BF_MATCHER_LAYER_3,
     789              :             .hdr_id = ETH_P_IP,
     790              :             .hdr_payload_size = sizeof(uint8_t),
     791              :             .hdr_payload_offset = offsetof(struct iphdr, protocol),
     792              :             .ops =
     793              :                 {
     794              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
     795              :                                    _bf_parse_l4_proto, _bf_print_l4_proto),
     796              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint8_t),
     797              :                                    _bf_parse_l4_proto, _bf_print_l4_proto),
     798              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
     799              :                                    _bf_parse_l4_proto, _bf_print_l4_proto),
     800              :                 },
     801              :         },
     802              :     [BF_MATCHER_IP6_SADDR] =
     803              :         {
     804              :             .layer = BF_MATCHER_LAYER_3,
     805              :             .hdr_id = ETH_P_IPV6,
     806              :             .hdr_payload_size = sizeof(struct in6_addr),
     807              :             .hdr_payload_offset = offsetof(struct ipv6hdr, saddr),
     808              :             .ops =
     809              :                 {
     810              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct in6_addr),
     811              :                                    _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
     812              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(struct in6_addr),
     813              :                                    _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
     814              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct in6_addr),
     815              :                                    _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
     816              :                 },
     817              :         },
     818              :     [BF_MATCHER_IP6_DADDR] =
     819              :         {
     820              :             .layer = BF_MATCHER_LAYER_3,
     821              :             .hdr_id = ETH_P_IPV6,
     822              :             .hdr_payload_size = sizeof(struct in6_addr),
     823              :             .hdr_payload_offset = offsetof(struct ipv6hdr, daddr),
     824              :             .ops =
     825              :                 {
     826              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct in6_addr),
     827              :                                    _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
     828              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(struct in6_addr),
     829              :                                    _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
     830              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct in6_addr),
     831              :                                    _bf_parse_ipv6_addr, _bf_print_ipv6_addr),
     832              :                 },
     833              :         },
     834              :     [BF_MATCHER_IP6_SNET] =
     835              :         {
     836              :             .layer = BF_MATCHER_LAYER_3,
     837              :             .hdr_id = ETH_P_IPV6,
     838              :             .hdr_payload_size = sizeof(struct in6_addr),
     839              :             .hdr_payload_offset = offsetof(struct ipv6hdr, saddr),
     840              :             .ops =
     841              :                 {
     842              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct bf_ip6_lpm_key),
     843              :                                    _bf_parse_ipv6_net, _bf_print_ipv6_net),
     844              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(struct bf_ip6_lpm_key),
     845              :                                    _bf_parse_ipv6_net, _bf_print_ipv6_net),
     846              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct bf_ip6_lpm_key),
     847              :                                    _bf_parse_ipv6_net, _bf_print_ipv6_net),
     848              :                 },
     849              :         },
     850              :     [BF_MATCHER_IP6_DNET] =
     851              :         {
     852              :             .layer = BF_MATCHER_LAYER_3,
     853              :             .hdr_id = ETH_P_IPV6,
     854              :             .hdr_payload_size = sizeof(struct in6_addr),
     855              :             .hdr_payload_offset = offsetof(struct ipv6hdr, daddr),
     856              :             .ops =
     857              :                 {
     858              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(struct bf_ip6_lpm_key),
     859              :                                    _bf_parse_ipv6_net, _bf_print_ipv6_net),
     860              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(struct bf_ip6_lpm_key),
     861              :                                    _bf_parse_ipv6_net, _bf_print_ipv6_net),
     862              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(struct bf_ip6_lpm_key),
     863              :                                    _bf_parse_ipv6_net, _bf_print_ipv6_net),
     864              :                 },
     865              :         },
     866              :     [BF_MATCHER_IP6_NEXTHDR] =
     867              :         {
     868              :             .layer = BF_MATCHER_LAYER_3,
     869              :             .hdr_id = ETH_P_IPV6,
     870              :             .hdr_payload_size = sizeof(uint8_t),
     871              :             .hdr_payload_offset = offsetof(struct ipv6hdr, nexthdr),
     872              :             .ops =
     873              :                 {
     874              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
     875              :                                    _bf_parse_l4_proto, _bf_print_l4_proto),
     876              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint8_t),
     877              :                                    _bf_parse_l4_proto, _bf_print_l4_proto),
     878              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
     879              :                                    _bf_parse_l4_proto, _bf_print_l4_proto),
     880              :                 },
     881              :         },
     882              :     [BF_MATCHER_TCP_SPORT] =
     883              :         {
     884              :             .layer = BF_MATCHER_LAYER_4,
     885              :             .hdr_id = IPPROTO_TCP,
     886              :             .hdr_payload_size = sizeof(uint16_t),
     887              :             .hdr_payload_offset = offsetof(struct tcphdr, source),
     888              :             .ops =
     889              :                 {
     890              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
     891              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     892              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint16_t),
     893              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     894              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint16_t),
     895              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     896              :                     BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
     897              :                                    _bf_parse_l4_port_range,
     898              :                                    _bf_print_l4_port_range),
     899              :                 },
     900              :         },
     901              :     [BF_MATCHER_TCP_DPORT] =
     902              :         {
     903              :             .layer = BF_MATCHER_LAYER_4,
     904              :             .hdr_id = IPPROTO_TCP,
     905              :             .hdr_payload_size = sizeof(uint16_t),
     906              :             .hdr_payload_offset = offsetof(struct tcphdr, dest),
     907              :             .ops =
     908              :                 {
     909              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
     910              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     911              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint16_t),
     912              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     913              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint16_t),
     914              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     915              :                     BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
     916              :                                    _bf_parse_l4_port_range,
     917              :                                    _bf_print_l4_port_range),
     918              :                 },
     919              :         },
     920              :     [BF_MATCHER_TCP_FLAGS] =
     921              :         {
     922              :             .layer = BF_MATCHER_LAYER_4,
     923              :             .hdr_id = IPPROTO_TCP,
     924              :             .hdr_payload_size = sizeof(uint8_t),
     925              :             .hdr_payload_offset = _BF_TCP_FLAGS_OFFSET,
     926              :             .ops =
     927              :                 {
     928              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
     929              :                                    _bf_parse_tcp_flags, _bf_print_tcp_flags),
     930              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint8_t),
     931              :                                    _bf_parse_tcp_flags, _bf_print_tcp_flags),
     932              :                     BF_MATCHER_OPS(BF_MATCHER_ANY, sizeof(uint8_t),
     933              :                                    _bf_parse_tcp_flags, _bf_print_tcp_flags),
     934              :                     BF_MATCHER_OPS(BF_MATCHER_ALL, sizeof(uint8_t),
     935              :                                    _bf_parse_tcp_flags, _bf_print_tcp_flags),
     936              :                 },
     937              :         },
     938              :     [BF_MATCHER_UDP_SPORT] =
     939              :         {
     940              :             .layer = BF_MATCHER_LAYER_4,
     941              :             .hdr_id = IPPROTO_UDP,
     942              :             .hdr_payload_size = sizeof(uint16_t),
     943              :             .hdr_payload_offset = offsetof(struct udphdr, source),
     944              :             .ops =
     945              :                 {
     946              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
     947              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     948              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint16_t),
     949              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     950              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint16_t),
     951              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     952              :                     BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
     953              :                                    _bf_parse_l4_port_range,
     954              :                                    _bf_print_l4_port_range),
     955              :                 },
     956              :         },
     957              :     [BF_MATCHER_UDP_DPORT] =
     958              :         {
     959              :             .layer = BF_MATCHER_LAYER_4,
     960              :             .hdr_id = IPPROTO_UDP,
     961              :             .hdr_payload_size = sizeof(uint16_t),
     962              :             .hdr_payload_offset = offsetof(struct udphdr, dest),
     963              :             .ops =
     964              :                 {
     965              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint16_t),
     966              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     967              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint16_t),
     968              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     969              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint16_t),
     970              :                                    _bf_parse_l4_port, _bf_print_l4_port),
     971              :                     BF_MATCHER_OPS(BF_MATCHER_RANGE, 2 * sizeof(uint16_t),
     972              :                                    _bf_parse_l4_port_range,
     973              :                                    _bf_print_l4_port_range),
     974              :                 },
     975              :         },
     976              :     [BF_MATCHER_ICMP_TYPE] =
     977              :         {
     978              :             .layer = BF_MATCHER_LAYER_4,
     979              :             .hdr_id = IPPROTO_ICMP,
     980              :             .hdr_payload_size = sizeof(uint8_t),
     981              :             .hdr_payload_offset = offsetof(struct icmphdr, type),
     982              :             .ops =
     983              :                 {
     984              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
     985              :                                    _bf_parse_icmp_type, _bf_print_icmp_type),
     986              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint8_t),
     987              :                                    _bf_parse_icmp_type, _bf_print_icmp_type),
     988              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
     989              :                                    _bf_parse_icmp_type, _bf_print_icmp_type),
     990              :                 },
     991              :         },
     992              :     [BF_MATCHER_ICMP_CODE] =
     993              :         {
     994              :             .layer = BF_MATCHER_LAYER_4,
     995              :             .hdr_id = IPPROTO_ICMP,
     996              :             .hdr_payload_size = sizeof(uint8_t),
     997              :             .hdr_payload_offset = offsetof(struct icmphdr, code),
     998              :             .ops =
     999              :                 {
    1000              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
    1001              :                                    _bf_parse_icmp_code, _bf_print_icmp_code),
    1002              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint8_t),
    1003              :                                    _bf_parse_icmp_code, _bf_print_icmp_code),
    1004              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
    1005              :                                    _bf_parse_icmp_code, _bf_print_icmp_code),
    1006              :                 },
    1007              :         },
    1008              :     [BF_MATCHER_ICMPV6_TYPE] =
    1009              :         {
    1010              :             .layer = BF_MATCHER_LAYER_4,
    1011              :             .hdr_id = IPPROTO_ICMPV6,
    1012              :             .hdr_payload_size = sizeof(uint8_t),
    1013              :             .hdr_payload_offset = offsetof(struct icmp6hdr, icmp6_type),
    1014              :             .ops =
    1015              :                 {
    1016              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
    1017              :                                    _bf_parse_icmpv6_type,
    1018              :                                    _bf_print_icmpv6_type),
    1019              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint8_t),
    1020              :                                    _bf_parse_icmpv6_type,
    1021              :                                    _bf_print_icmpv6_type),
    1022              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
    1023              :                                    _bf_parse_icmpv6_type,
    1024              :                                    _bf_print_icmpv6_type),
    1025              :                 },
    1026              :         },
    1027              :     [BF_MATCHER_ICMPV6_CODE] =
    1028              :         {
    1029              :             .layer = BF_MATCHER_LAYER_4,
    1030              :             .hdr_id = IPPROTO_ICMPV6,
    1031              :             .hdr_payload_size = sizeof(uint8_t),
    1032              :             .hdr_payload_offset = offsetof(struct icmp6hdr, icmp6_code),
    1033              :             .ops =
    1034              :                 {
    1035              :                     BF_MATCHER_OPS(BF_MATCHER_EQ, sizeof(uint8_t),
    1036              :                                    _bf_parse_icmp_code, _bf_print_icmp_code),
    1037              :                     BF_MATCHER_OPS(BF_MATCHER_NE, sizeof(uint8_t),
    1038              :                                    _bf_parse_icmp_code, _bf_print_icmp_code),
    1039              :                     BF_MATCHER_OPS(BF_MATCHER_IN, sizeof(uint8_t),
    1040              :                                    _bf_parse_icmp_code, _bf_print_icmp_code),
    1041              :                 },
    1042              :         },
    1043              : };
    1044              : 
    1045           76 : const struct bf_matcher_meta *bf_matcher_get_meta(enum bf_matcher_type type)
    1046              : {
    1047           76 :     if (type < 0 || _BF_MATCHER_TYPE_MAX <= type)
    1048              :         return NULL;
    1049              : 
    1050           75 :     return _bf_matcher_metas[type].layer == _BF_MATCHER_LAYER_UNDEFINED ?
    1051           75 :                NULL :
    1052              :                &_bf_matcher_metas[type];
    1053              : }
    1054              : 
    1055           76 : const struct bf_matcher_ops *bf_matcher_get_ops(enum bf_matcher_type type,
    1056              :                                                 enum bf_matcher_op op)
    1057              : {
    1058           76 :     const struct bf_matcher_meta *meta = bf_matcher_get_meta(type);
    1059              : 
    1060           76 :     if (!meta)
    1061              :         return NULL;
    1062              : 
    1063           75 :     return meta->ops[op].ref_payload_size ? &meta->ops[op] : NULL;
    1064              : }
    1065              : 
    1066           58 : int bf_matcher_new(struct bf_matcher **matcher, enum bf_matcher_type type,
    1067              :                    enum bf_matcher_op op, const void *payload,
    1068              :                    size_t payload_len)
    1069              : {
    1070           56 :     _free_bf_matcher_ struct bf_matcher *_matcher = NULL;
    1071              : 
    1072           58 :     bf_assert(matcher);
    1073           57 :     bf_assert((payload && payload_len) || (!payload && !payload_len));
    1074              : 
    1075           56 :     _matcher = malloc(sizeof(struct bf_matcher) + payload_len);
    1076           56 :     if (!_matcher)
    1077              :         return -ENOMEM;
    1078              : 
    1079           54 :     _matcher->type = type;
    1080           54 :     _matcher->op = op;
    1081           54 :     _matcher->len = sizeof(struct bf_matcher) + payload_len;
    1082           54 :     bf_memcpy(_matcher->payload, payload, payload_len);
    1083              : 
    1084           54 :     *matcher = TAKE_PTR(_matcher);
    1085              : 
    1086           54 :     return 0;
    1087              : }
    1088              : 
    1089            0 : int bf_matcher_new_from_raw(struct bf_matcher **matcher,
    1090              :                             enum bf_matcher_type type, enum bf_matcher_op op,
    1091              :                             const char *payload)
    1092              : {
    1093            0 :     _free_bf_matcher_ struct bf_matcher *_matcher = NULL;
    1094              :     const struct bf_matcher_ops *ops;
    1095              :     int r;
    1096              : 
    1097            0 :     bf_assert(matcher && payload);
    1098              : 
    1099            0 :     ops = bf_matcher_get_ops(type, op);
    1100            0 :     if (!ops) {
    1101            0 :         return bf_err_r(-ENOENT, "payload ops not found for '%s %s'",
    1102              :                         bf_matcher_type_to_str(type), bf_matcher_op_to_str(op));
    1103              :     }
    1104              : 
    1105            0 :     _matcher = malloc(sizeof(*_matcher) + ops->ref_payload_size);
    1106            0 :     if (!_matcher)
    1107              :         return -ENOMEM;
    1108              : 
    1109            0 :     _matcher->type = type;
    1110            0 :     _matcher->op = op;
    1111            0 :     _matcher->len = sizeof(*_matcher) + ops->ref_payload_size;
    1112              : 
    1113            0 :     r = ops->parse(_matcher->type, _matcher->op, &_matcher->payload, payload);
    1114            0 :     if (r)
    1115              :         return r;
    1116              : 
    1117            0 :     *matcher = TAKE_PTR(_matcher);
    1118              : 
    1119            0 :     return 0;
    1120              : }
    1121              : 
    1122           23 : int bf_matcher_new_from_pack(struct bf_matcher **matcher, bf_rpack_node_t node)
    1123              : {
    1124           23 :     _free_bf_matcher_ struct bf_matcher *_matcher = NULL;
    1125              :     enum bf_matcher_type type;
    1126              :     enum bf_matcher_op op;
    1127              :     const void *payload;
    1128              :     size_t payload_len;
    1129              :     int r;
    1130              : 
    1131           23 :     bf_assert(matcher);
    1132              : 
    1133           23 :     r = bf_rpack_kv_enum(node, "type", &type);
    1134              :     if (r)
    1135            0 :         return bf_rpack_key_err(r, "bf_matcher.type");
    1136              : 
    1137           23 :     r = bf_rpack_kv_enum(node, "op", &op);
    1138              :     if (r)
    1139            0 :         return bf_rpack_key_err(r, "bf_matcher.op");
    1140              : 
    1141           23 :     r = bf_rpack_kv_bin(node, "payload", &payload, &payload_len);
    1142           23 :     if (r)
    1143            0 :         return bf_rpack_key_err(r, "bf_matcher.payload");
    1144              : 
    1145           23 :     r = bf_matcher_new(&_matcher, type, op, payload, payload_len);
    1146           23 :     if (r)
    1147            0 :         return bf_err_r(r, "failed to create bf_matcher from pack");
    1148              : 
    1149           23 :     *matcher = TAKE_PTR(_matcher);
    1150              : 
    1151           23 :     return 0;
    1152              : }
    1153              : 
    1154          166 : void bf_matcher_free(struct bf_matcher **matcher)
    1155              : {
    1156          166 :     bf_assert(matcher);
    1157              : 
    1158          165 :     if (!*matcher)
    1159              :         return;
    1160              : 
    1161           54 :     free(*matcher);
    1162           54 :     *matcher = NULL;
    1163              : }
    1164              : 
    1165           25 : int bf_matcher_pack(const struct bf_matcher *matcher, bf_wpack_t *pack)
    1166              : {
    1167           25 :     bf_assert(matcher);
    1168           24 :     bf_assert(pack);
    1169              : 
    1170           23 :     bf_wpack_kv_int(pack, "type", matcher->type);
    1171           23 :     bf_wpack_kv_int(pack, "op", matcher->op);
    1172           23 :     bf_wpack_kv_bin(pack, "payload", matcher->payload,
    1173           23 :                     matcher->len - sizeof(*matcher));
    1174              : 
    1175           23 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
    1176              : }
    1177              : 
    1178            0 : void bf_matcher_dump(const struct bf_matcher *matcher, prefix_t *prefix)
    1179              : {
    1180            0 :     bf_assert(matcher);
    1181            0 :     bf_assert(prefix);
    1182              : 
    1183            0 :     DUMP(prefix, "struct bf_matcher at %p", matcher);
    1184              : 
    1185            0 :     bf_dump_prefix_push(prefix);
    1186              : 
    1187            0 :     DUMP(prefix, "type: %s", bf_matcher_type_to_str(matcher->type));
    1188            0 :     DUMP(prefix, "op: %s", bf_matcher_op_to_str(matcher->op));
    1189            0 :     DUMP(prefix, "len: %ld", matcher->len);
    1190            0 :     DUMP(bf_dump_prefix_last(prefix), "payload:");
    1191            0 :     bf_dump_prefix_push(prefix);
    1192            0 :     bf_dump_hex(prefix, matcher->payload,
    1193            0 :                 matcher->len - sizeof(struct bf_matcher));
    1194            0 :     bf_dump_prefix_pop(prefix);
    1195              : 
    1196            0 :     bf_dump_prefix_pop(prefix);
    1197            0 : }
    1198              : 
    1199           30 : enum bf_matcher_type bf_matcher_get_type(const struct bf_matcher *matcher)
    1200              : {
    1201           30 :     bf_assert(matcher);
    1202           30 :     return matcher->type;
    1203              : }
    1204              : 
    1205            0 : enum bf_matcher_op bf_matcher_get_op(const struct bf_matcher *matcher)
    1206              : {
    1207            0 :     bf_assert(matcher);
    1208            0 :     return matcher->op;
    1209              : }
    1210              : 
    1211            0 : const void *bf_matcher_payload(const struct bf_matcher *matcher)
    1212              : {
    1213            0 :     bf_assert(matcher);
    1214            0 :     return matcher->payload;
    1215              : }
    1216              : 
    1217            0 : size_t bf_matcher_payload_len(const struct bf_matcher *matcher)
    1218              : {
    1219            0 :     bf_assert(matcher);
    1220            0 :     return matcher->len - sizeof(*matcher);
    1221              : }
    1222              : 
    1223            0 : size_t bf_matcher_len(const struct bf_matcher *matcher)
    1224              : {
    1225            0 :     bf_assert(matcher);
    1226            0 :     return matcher->len;
    1227              : }
    1228              : 
    1229              : static const char *_bf_matcher_type_strs[] = {
    1230              :     [BF_MATCHER_META_IFACE] = "meta.iface",
    1231              :     [BF_MATCHER_META_L3_PROTO] = "meta.l3_proto",
    1232              :     [BF_MATCHER_META_L4_PROTO] = "meta.l4_proto",
    1233              :     [BF_MATCHER_META_PROBABILITY] = "meta.probability",
    1234              :     [BF_MATCHER_META_SPORT] = "meta.sport",
    1235              :     [BF_MATCHER_META_DPORT] = "meta.dport",
    1236              :     [BF_MATCHER_IP4_SADDR] = "ip4.saddr",
    1237              :     [BF_MATCHER_IP4_SNET] = "ip4.snet",
    1238              :     [BF_MATCHER_IP4_DADDR] = "ip4.daddr",
    1239              :     [BF_MATCHER_IP4_DNET] = "ip4.dnet",
    1240              :     [BF_MATCHER_IP4_PROTO] = "ip4.proto",
    1241              :     [BF_MATCHER_IP6_SADDR] = "ip6.saddr",
    1242              :     [BF_MATCHER_IP6_SNET] = "ip6.snet",
    1243              :     [BF_MATCHER_IP6_DADDR] = "ip6.daddr",
    1244              :     [BF_MATCHER_IP6_DNET] = "ip6.dnet",
    1245              :     [BF_MATCHER_IP6_NEXTHDR] = "ip6.nexthdr",
    1246              :     [BF_MATCHER_TCP_SPORT] = "tcp.sport",
    1247              :     [BF_MATCHER_TCP_DPORT] = "tcp.dport",
    1248              :     [BF_MATCHER_TCP_FLAGS] = "tcp.flags",
    1249              :     [BF_MATCHER_UDP_SPORT] = "udp.sport",
    1250              :     [BF_MATCHER_UDP_DPORT] = "udp.dport",
    1251              :     [BF_MATCHER_ICMP_TYPE] = "icmp.type",
    1252              :     [BF_MATCHER_ICMP_CODE] = "icmp.code",
    1253              :     [BF_MATCHER_ICMPV6_TYPE] = "icmpv6.type",
    1254              :     [BF_MATCHER_ICMPV6_CODE] = "icmpv6.code",
    1255              :     [BF_MATCHER_SET] = "<set>",
    1256              : };
    1257              : 
    1258              : static_assert(ARRAY_SIZE(_bf_matcher_type_strs) == _BF_MATCHER_TYPE_MAX,
    1259              :               "missing entries in the matcher type array");
    1260              : 
    1261           30 : const char *bf_matcher_type_to_str(enum bf_matcher_type type)
    1262              : {
    1263           30 :     if (type < 0 || _BF_MATCHER_TYPE_MAX <= type)
    1264              :         return "<invalid>";
    1265              : 
    1266           29 :     return _bf_matcher_type_strs[type];
    1267              : }
    1268              : 
    1269           59 : int bf_matcher_type_from_str(const char *str, enum bf_matcher_type *type)
    1270              : {
    1271           59 :     bf_assert(str);
    1272           58 :     bf_assert(type);
    1273              : 
    1274          732 :     for (size_t i = 0; i < _BF_MATCHER_TYPE_MAX; ++i) {
    1275          728 :         if (bf_streq(_bf_matcher_type_strs[i], str)) {
    1276           53 :             *type = i;
    1277           53 :             return 0;
    1278              :         }
    1279              :     }
    1280              : 
    1281              :     return -EINVAL;
    1282              : }
    1283              : 
    1284              : static const char *_bf_matcher_ops_strs[] = {
    1285              :     [BF_MATCHER_EQ] = "eq",   [BF_MATCHER_NE] = "not",
    1286              :     [BF_MATCHER_ANY] = "any", [BF_MATCHER_ALL] = "all",
    1287              :     [BF_MATCHER_IN] = "in",   [BF_MATCHER_RANGE] = "range",
    1288              : };
    1289              : 
    1290              : static_assert(ARRAY_SIZE(_bf_matcher_ops_strs) == _BF_MATCHER_OP_MAX);
    1291              : 
    1292            8 : const char *bf_matcher_op_to_str(enum bf_matcher_op op)
    1293              : {
    1294            8 :     bf_assert(0 <= op && op < _BF_MATCHER_OP_MAX);
    1295              : 
    1296            6 :     return _bf_matcher_ops_strs[op];
    1297              : }
    1298              : 
    1299            0 : int bf_matcher_op_from_str(const char *str, enum bf_matcher_op *op)
    1300              : {
    1301            0 :     bf_assert(str);
    1302            0 :     bf_assert(op);
    1303              : 
    1304            0 :     for (size_t i = 0; i < _BF_MATCHER_OP_MAX; ++i) {
    1305            0 :         if (bf_streq(_bf_matcher_ops_strs[i], str)) {
    1306            0 :             *op = i;
    1307            0 :             return 0;
    1308              :         }
    1309              :     }
    1310              : 
    1311              :     return -EINVAL;
    1312              : }
    1313              : 
    1314              : static const char *_bf_tcp_flags_strs[] = {
    1315              :     [BF_TCP_FIN] = "fin", [BF_TCP_SYN] = "syn", [BF_TCP_RST] = "rst",
    1316              :     [BF_TCP_PSH] = "psh", [BF_TCP_ACK] = "ack", [BF_TCP_URG] = "urg",
    1317              :     [BF_TCP_ECE] = "ece", [BF_TCP_CWR] = "cwr",
    1318              : };
    1319              : static_assert(ARRAY_SIZE(_bf_tcp_flags_strs) == _BF_TCP_MAX);
    1320              : 
    1321            0 : const char *bf_tcp_flag_to_str(enum bf_tcp_flag flag)
    1322              : {
    1323            0 :     bf_assert(0 <= flag && flag < _BF_TCP_MAX);
    1324              : 
    1325            0 :     return _bf_tcp_flags_strs[flag];
    1326              : }
    1327              : 
    1328            0 : int bf_tcp_flag_from_str(const char *str, enum bf_tcp_flag *flag)
    1329              : {
    1330            0 :     bf_assert(str);
    1331            0 :     bf_assert(flag);
    1332              : 
    1333            0 :     for (size_t i = 0; i < _BF_TCP_MAX; ++i) {
    1334            0 :         if (bf_streq_i(_bf_tcp_flags_strs[i], str)) {
    1335            0 :             *flag = i;
    1336            0 :             return 0;
    1337              :         }
    1338              :     }
    1339              : 
    1340              :     return -EINVAL;
    1341              : }
    1342              : 
    1343            0 : const char *bf_ethertype_to_str(uint16_t ethertype)
    1344              : {
    1345            0 :     switch (ethertype) {
    1346              :     case ETH_P_IP:
    1347              :         return "ipv4";
    1348            0 :     case ETH_P_IPV6:
    1349            0 :         return "ipv6";
    1350            0 :     default:
    1351            0 :         return NULL;
    1352              :     }
    1353              : }
    1354              : 
    1355            0 : int bf_ethertype_from_str(const char *str, uint16_t *ethertype)
    1356              : {
    1357            0 :     bf_assert(str && ethertype);
    1358              : 
    1359            0 :     if (bf_streq_i(str, "ipv4")) {
    1360            0 :         *ethertype = ETH_P_IP;
    1361            0 :         return 0;
    1362              :     }
    1363              : 
    1364            0 :     if (bf_streq_i(str, "ipv6")) {
    1365            0 :         *ethertype = ETH_P_IPV6;
    1366            0 :         return 0;
    1367              :     }
    1368              : 
    1369              :     return -EINVAL;
    1370              : }
    1371              : 
    1372              : static const char *_bf_ipproto_strs[UINT8_MAX + 1] = {
    1373              :     [IPPROTO_HOPOPTS] = "hop",   [IPPROTO_ICMP] = "icmp",
    1374              :     [IPPROTO_IGMP] = "igmp",     [IPPROTO_TCP] = "tcp",
    1375              :     [IPPROTO_UDP] = "udp",       [IPPROTO_ROUTING] = "routing",
    1376              :     [IPPROTO_FRAGMENT] = "frag", [IPPROTO_AH] = "ah",
    1377              :     [IPPROTO_DSTOPTS] = "dst",   [IPPROTO_ICMPV6] = "icmpv6",
    1378              :     [IPPROTO_MH] = "mh",
    1379              : };
    1380              : static_assert(ARRAY_SIZE(_bf_ipproto_strs) == (UINT8_MAX + 1),
    1381              :               "missing entries in IP protocols strings array");
    1382              : 
    1383            0 : const char *bf_ipproto_to_str(uint8_t ipproto)
    1384              : {
    1385            0 :     return _bf_ipproto_strs[ipproto];
    1386              : }
    1387              : 
    1388           24 : int bf_ipproto_from_str(const char *str, uint8_t *ipproto)
    1389              : {
    1390           24 :     bf_assert(str && ipproto);
    1391              : 
    1392          236 :     for (size_t i = 0; i <= UINT8_MAX; ++i) {
    1393          236 :         if (bf_streq_i(str, _bf_ipproto_strs[i])) {
    1394           24 :             *ipproto = (uint8_t)i;
    1395           24 :             return 0;
    1396              :         }
    1397              :     }
    1398              : 
    1399              :     return -EINVAL;
    1400              : }
    1401              : 
    1402              : #define ICMP_ROUTERADVERT 9
    1403              : #define ICMP_ROUTERSOLICIT 10
    1404              : 
    1405              : static const char *_bf_icmp_type_strs[UINT8_MAX + 1] = {
    1406              :     [ICMP_ECHOREPLY] = "echo-reply",
    1407              :     [ICMP_DEST_UNREACH] = "destination-unreachable",
    1408              :     [ICMP_SOURCE_QUENCH] = "source-quench",
    1409              :     [ICMP_REDIRECT] = "redirect",
    1410              :     [ICMP_ECHO] = "echo-request",
    1411              :     [ICMP_ROUTERADVERT] = "router-advertisement",
    1412              :     [ICMP_ROUTERSOLICIT] = "router-solicitation",
    1413              :     [ICMP_TIME_EXCEEDED] = "time-exceeded",
    1414              :     [ICMP_PARAMETERPROB] = "parameter-problem",
    1415              :     [ICMP_TIMESTAMP] = "timestamp-request",
    1416              :     [ICMP_TIMESTAMPREPLY] = "timestamp-reply",
    1417              :     [ICMP_INFO_REQUEST] = "info-request",
    1418              :     [ICMP_INFO_REPLY] = "info-reply",
    1419              :     [ICMP_ADDRESS] = "address-mask-request",
    1420              :     [ICMP_ADDRESSREPLY] = "address-mask-reply",
    1421              : };
    1422              : static_assert(ARRAY_SIZE(_bf_icmp_type_strs) == (UINT8_MAX + 1),
    1423              :               "missing entries in ICMP types strings array");
    1424              : 
    1425            0 : const char *bf_icmp_type_to_str(uint8_t type)
    1426              : {
    1427            0 :     return _bf_icmp_type_strs[type];
    1428              : }
    1429              : 
    1430            0 : int bf_icmp_type_from_str(const char *str, uint8_t *type)
    1431              : {
    1432            0 :     bf_assert(str && type);
    1433              : 
    1434            0 :     for (size_t i = 0; i <= UINT8_MAX; ++i) {
    1435            0 :         if (bf_streq_i(str, _bf_icmp_type_strs[i])) {
    1436            0 :             *type = (uint8_t)i;
    1437            0 :             return 0;
    1438              :         }
    1439              :     }
    1440              : 
    1441              :     return -EINVAL;
    1442              : }
    1443              : 
    1444              : #define ICMPV6_ND_ROUTERSOLICIT 133
    1445              : #define ICMPV6_ND_ROUTERADVERT 134
    1446              : #define ICMPV6_ND_NEIGHSOLICIT 135
    1447              : #define ICMPV6_ND_NEIGHADVERT 136
    1448              : 
    1449              : static const char *_bf_icmpv6_type_strs[UINT8_MAX + 1] = {
    1450              :     [ICMPV6_DEST_UNREACH] = "destination-unreachable",
    1451              :     [ICMPV6_PKT_TOOBIG] = "packet-too-big",
    1452              :     [ICMPV6_TIME_EXCEED] = "time-exceeded",
    1453              :     [ICMPV6_PARAMPROB] = "parameter-problem",
    1454              :     [ICMPV6_ECHO_REQUEST] = "echo-request",
    1455              :     [ICMPV6_ECHO_REPLY] = "echo-reply",
    1456              :     [ICMPV6_MGM_QUERY] = "mld-listener-query",
    1457              :     [ICMPV6_MGM_REPORT] = "mld-listener-report",
    1458              :     [ICMPV6_MGM_REDUCTION] = "mld-listener-reduction",
    1459              :     [ICMPV6_ND_ROUTERSOLICIT] = "nd-router-solicit",
    1460              :     [ICMPV6_ND_ROUTERADVERT] = "nd-router-advert",
    1461              :     [ICMPV6_ND_NEIGHSOLICIT] = "nd-neighbor-solicit",
    1462              :     [ICMPV6_ND_NEIGHADVERT] = "nd-neighbor-advert",
    1463              :     [ICMPV6_MLD2_REPORT] = "mld2-listener-report",
    1464              : };
    1465              : static_assert(ARRAY_SIZE(_bf_icmpv6_type_strs) == (UINT8_MAX + 1),
    1466              :               "missing entries in ICMPv6 types strings array");
    1467              : 
    1468            0 : const char *bf_icmpv6_type_to_str(uint8_t type)
    1469              : {
    1470            0 :     return _bf_icmpv6_type_strs[type];
    1471              : }
    1472              : 
    1473            0 : int bf_icmpv6_type_from_str(const char *str, uint8_t *type)
    1474              : {
    1475            0 :     bf_assert(str && type);
    1476              : 
    1477            0 :     for (size_t i = 0; i <= UINT8_MAX; ++i) {
    1478            0 :         if (bf_streq_i(str, _bf_icmpv6_type_strs[i])) {
    1479            0 :             *type = (uint8_t)i;
    1480            0 :             return 0;
    1481              :         }
    1482              :     }
    1483              : 
    1484              :     return -EINVAL;
    1485              : }
        

Generated by: LCOV version 2.0-1