LCOV - code coverage report
Current view: top level - libbpfilter - set.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 86.1 % 180 155
Test Date: 2025-09-16 14:46:44 Functions: 100.0 % 9 9

            Line data    Source code
       1              : // SPDX-License-Identifier: GPL-2.0-only
       2              : /*
       3              :  * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
       4              :  */
       5              : 
       6              : #include "bpfilter/set.h"
       7              : 
       8              : #include <errno.h>
       9              : #include <stdint.h>
      10              : #include <stdlib.h>
      11              : #include <string.h>
      12              : #include <time.h>
      13              : 
      14              : #include "bpfilter/dump.h"
      15              : #include "bpfilter/helper.h"
      16              : #include "bpfilter/list.h"
      17              : #include "bpfilter/logger.h"
      18              : #include "bpfilter/pack.h"
      19              : 
      20              : /// Mask value of matcher types supporting LPM trie maps.
      21              : #define _BF_SET_USE_TRIE_MASK                                                  \
      22              :     (BF_FLAGS(BF_MATCHER_IP4_SNET, BF_MATCHER_IP4_DNET, BF_MATCHER_IP6_SNET,   \
      23              :               BF_MATCHER_IP6_DNET))
      24              : 
      25           22 : int bf_set_new(struct bf_set **set, const char *name, enum bf_matcher_type *key,
      26              :                size_t n_comps)
      27              : {
      28           22 :     bf_assert(set && key);
      29              : 
      30           20 :     _free_bf_set_ struct bf_set *_set = NULL;
      31              :     uint32_t mask = 0;
      32              : 
      33              :     bf_static_assert(_BF_MATCHER_TYPE_MAX < 8 * sizeof(uint32_t),
      34              :                      "matcher type bitmask won't fit in 32 bits");
      35              : 
      36           20 :     if (n_comps == 0)
      37            1 :         return bf_err_r(-EINVAL, "at least 1 key component is required");
      38              : 
      39           19 :     if (n_comps > BF_SET_MAX_N_COMPS) {
      40            1 :         return bf_err_r(-E2BIG,
      41              :                         "a set key can't contain more than %d components",
      42              :                         BF_SET_MAX_N_COMPS);
      43              :     }
      44              : 
      45           18 :     _set = malloc(sizeof(*_set));
      46           18 :     if (!_set)
      47              :         return -ENOMEM;
      48              : 
      49           18 :     _set->name = NULL;
      50           18 :     if (name) {
      51            0 :         _set->name = strdup(name);
      52            0 :         if (!_set->name)
      53            0 :             return bf_err_r(-ENOMEM, "failed to allocate memory for set name");
      54              :     }
      55              : 
      56           18 :     memcpy(&(_set)->key, key, n_comps * sizeof(enum bf_matcher_type));
      57           18 :     _set->n_comps = n_comps;
      58           18 :     _set->elem_size = 0;
      59           18 :     _set->elems = bf_list_default(freep, NULL);
      60              : 
      61           49 :     for (size_t i = 0; i < n_comps; ++i) {
      62              :         const struct bf_matcher_ops *ops;
      63              : 
      64           32 :         ops = bf_matcher_get_ops(_set->key[i], BF_MATCHER_IN);
      65           32 :         if (!ops) {
      66            1 :             return bf_err_r(-ENOTSUP,
      67              :                             "matcher '%s' (%d) is not supported as a set key",
      68              :                             bf_matcher_type_to_str(_set->key[i]), _set->key[i]);
      69              :         }
      70           31 :         _set->elem_size += ops->ref_payload_size;
      71              : 
      72           31 :         mask |= BF_FLAG(_set->key[i]);
      73              :     }
      74              : 
      75           17 :     _set->use_trie = n_comps == 1 && mask & _BF_SET_USE_TRIE_MASK;
      76              : 
      77           17 :     if (n_comps > 1 && mask & _BF_SET_USE_TRIE_MASK) {
      78            2 :         return bf_err_r(
      79              :             -EINVAL,
      80              :             "network matchers can't be used in combination with other matchers in a set");
      81              :     }
      82              : 
      83           15 :     *set = TAKE_PTR(_set);
      84              : 
      85           15 :     return 0;
      86              : }
      87              : 
      88              : /**
      89              :  * @brief Parse a set's raw key into an array of `bf_matcher_type`.
      90              :  *
      91              :  * @param raw_key Raw set key, as a string of comma-separated matcher types
      92              :  *        enclosed in parentheses. Can't be NULL.
      93              :  * @param key Set key, parsed from `raw_key`. Can't be NULL.
      94              :  * @param n_comps Number of components in `key`. Can't be NULL.
      95              :  * @return 0 on success, or a negative error value on failure.
      96              :  */
      97           15 : static int _bf_set_parse_key(const char *raw_key, enum bf_matcher_type *key,
      98              :                              size_t *n_comps)
      99              : {
     100           15 :     bf_assert(raw_key && key && n_comps);
     101              : 
     102              :     _cleanup_free_ char *_raw_key = NULL;
     103              :     char *tmp, *saveptr, *token;
     104              : 
     105           15 :     _raw_key = strdup(raw_key);
     106           15 :     if (!_raw_key) {
     107            0 :         return bf_err_r(-ENOMEM, "failed to duplicate set raw key '%s'",
     108              :                         raw_key);
     109              :     }
     110              : 
     111           15 :     *n_comps = 0;
     112              : 
     113              :     tmp = _raw_key;
     114           42 :     while ((token = strtok_r(tmp, "(),", &saveptr))) {
     115              :         int r;
     116              : 
     117           30 :         if (*n_comps == BF_SET_MAX_N_COMPS) {
     118            1 :             return bf_err_r(-E2BIG, "set keys are limited to %d components",
     119              :                             BF_SET_MAX_N_COMPS);
     120              :         }
     121              : 
     122           29 :         token = bf_trim(token);
     123              : 
     124           29 :         r = bf_matcher_type_from_str(token, &key[*n_comps]);
     125           29 :         if (r)
     126            2 :             return bf_err_r(r, "failed to parse set key component '%s'", token);
     127              : 
     128              :         tmp = NULL;
     129           27 :         ++*n_comps;
     130              :     }
     131              : 
     132           12 :     if (!*n_comps)
     133            1 :         return bf_err_r(-EINVAL, "set key can't have no component");
     134              : 
     135              :     return 0;
     136              : }
     137              : 
     138              : /**
     139              :  * @brief Parse a raw element and insert it into a set.
     140              :  *
     141              :  * The element is parsed according to `set->key`.
     142              :  *
     143              :  * @param set Set to parse the element for. Can't be NULL.
     144              :  * @param raw_elem Raw element to parse. Can't be NULL.
     145              :  * @return 0 on success, or a negative error value on failure.
     146              :  */
     147           24 : static int _bf_set_parse_elem(struct bf_set *set, const char *raw_elem)
     148              : {
     149           24 :     bf_assert(set && raw_elem);
     150              : 
     151              :     _cleanup_free_ void *elem = NULL;
     152              :     _cleanup_free_ char *_raw_elem = NULL;
     153              :     char *tmp, *saveptr, *token;
     154              :     size_t elem_offset = 0;
     155              :     size_t comp_idx = 0;
     156              :     int r;
     157              : 
     158           24 :     _raw_elem = strdup(raw_elem);
     159           24 :     if (!_raw_elem) {
     160            0 :         return bf_err_r(-ENOMEM,
     161              :                         "failed to create a copy of the raw element '%s'",
     162              :                         raw_elem);
     163              :     }
     164              : 
     165           24 :     elem = malloc(set->elem_size);
     166           24 :     if (!elem)
     167            0 :         return bf_err_r(-ENOMEM, "failed to allocate a new set element");
     168              : 
     169              :     tmp = _raw_elem;
     170           68 :     while ((token = strtok_r(tmp, ",", &saveptr))) {
     171              :         const struct bf_matcher_ops *ops;
     172              : 
     173           45 :         if (comp_idx >= set->n_comps) {
     174            1 :             return bf_err_r(
     175              :                 -EINVAL,
     176              :                 "set element has more components than defined in the key '%s'",
     177              :                 token);
     178              :         }
     179              : 
     180           44 :         token = bf_trim(token);
     181              : 
     182           44 :         ops = bf_matcher_get_ops(set->key[comp_idx], BF_MATCHER_IN);
     183           44 :         if (!ops) {
     184            0 :             return bf_err_r(-EINVAL, "matcher type '%s' has no matcher_ops",
     185              :                             bf_matcher_type_to_str(set->key[comp_idx]));
     186              :         }
     187              : 
     188           44 :         r = ops->parse(set->key[comp_idx], BF_MATCHER_IN, elem + elem_offset,
     189              :                        token);
     190           44 :         if (r) {
     191            0 :             return bf_err_r(r, "failed to parse set element component '%s'",
     192              :                             token);
     193              :         }
     194              : 
     195           44 :         elem_offset += ops->ref_payload_size;
     196              :         tmp = NULL;
     197           44 :         ++comp_idx;
     198              :     }
     199              : 
     200           23 :     if (comp_idx != set->n_comps) {
     201            0 :         return bf_err_r(-EINVAL, "missing component in set element '%s'",
     202              :                         raw_elem);
     203              :     }
     204              : 
     205           23 :     r = bf_list_add_tail(&set->elems, elem);
     206           23 :     if (r)
     207            0 :         return bf_err_r(r, "failed to insert element into set");
     208              :     TAKE_PTR(elem);
     209              : 
     210              :     return 0;
     211              : }
     212              : 
     213           18 : int bf_set_new_from_raw(struct bf_set **set, const char *name,
     214              :                         const char *raw_key, const char *raw_payload)
     215              : {
     216           21 :     bf_assert(set && raw_key && raw_payload);
     217              : 
     218           15 :     _free_bf_set_ struct bf_set *_set = NULL;
     219              :     _cleanup_free_ char *_raw_payload = NULL;
     220              :     enum bf_matcher_type key[BF_SET_MAX_N_COMPS];
     221              :     char *raw_elem, *tmp, *saveptr;
     222              :     size_t n_comps;
     223              :     int r;
     224              : 
     225           15 :     bf_assert(raw_key);
     226           15 :     bf_assert(raw_payload);
     227              : 
     228           15 :     r = _bf_set_parse_key(raw_key, key, &n_comps);
     229           15 :     if (r)
     230            4 :         return bf_err_r(r, "failed to parse set key '%s'", raw_key);
     231              : 
     232           11 :     r = bf_set_new(&_set, name, key, n_comps);
     233           11 :     if (r)
     234              :         return r;
     235              : 
     236           11 :     _raw_payload = strdup(raw_payload);
     237           11 :     if (!_raw_payload)
     238            0 :         return bf_err_r(-ENOMEM, "failed to copy set raw payload '%s'",
     239              :                         raw_payload);
     240              : 
     241              :     tmp = _raw_payload;
     242           34 :     while ((raw_elem = strtok_r(tmp, "{};\n", &saveptr))) {
     243           24 :         raw_elem = bf_trim(raw_elem);
     244              : 
     245              :         /* While strtok_r() won't return empty token, the trimmed version of the
     246              :          * token can be empty! */
     247           24 :         if (raw_elem[0] == '\0')
     248            0 :             continue;
     249              : 
     250           24 :         r = _bf_set_parse_elem(_set, raw_elem);
     251           24 :         if (r)
     252            1 :             return bf_err_r(r, "failed to parse set element '%s'", raw_elem);
     253              : 
     254              :         tmp = NULL;
     255              :     }
     256              : 
     257           10 :     *set = TAKE_PTR(_set);
     258              : 
     259           10 :     return 0;
     260              : }
     261              : 
     262            1 : int bf_set_new_from_pack(struct bf_set **set, bf_rpack_node_t node)
     263              : {
     264            1 :     _free_bf_set_ struct bf_set *_set = NULL;
     265            1 :     _cleanup_free_ char *name = NULL;
     266              :     bf_rpack_node_t child, comp_node, elem_node;
     267              :     size_t n_comps = 0;
     268              :     enum bf_matcher_type key[BF_SET_MAX_N_COMPS];
     269              :     int r;
     270              : 
     271            1 :     bf_assert(set);
     272              : 
     273            1 :     r = bf_rpack_kv_node(node, "name", &child);
     274            1 :     if (r)
     275            0 :         return bf_rpack_key_err(r, "bf_set.name");
     276            1 :     if (!bf_rpack_is_nil(child)) {
     277            0 :         r = bf_rpack_str(child, &name);
     278            0 :         if (r)
     279            0 :             return bf_err_r(r, "failed to read set name from bf_set.name pack");
     280              :     }
     281              : 
     282            1 :     r = bf_rpack_kv_array(node, "key", &child);
     283            1 :     if (r)
     284            0 :         return bf_rpack_key_err(r, "bf_set.key");
     285            8 :     bf_rpack_array_foreach (child, comp_node) {
     286            3 :         ++n_comps;
     287            3 :         if (n_comps > BF_SET_MAX_N_COMPS) {
     288            0 :             return bf_err_r(
     289              :                 -E2BIG,
     290              :                 "bf_set.key in pack contains %lu key components, only %d allowed",
     291              :                 n_comps, BF_SET_MAX_N_COMPS);
     292              :         }
     293              : 
     294            3 :         r = bf_rpack_enum(comp_node, &key[i]);
     295              :         if (r)
     296            0 :             return bf_rpack_key_err(r, "bf_set.key");
     297              :     }
     298              : 
     299            1 :     r = bf_set_new(&_set, name, key, n_comps);
     300            1 :     if (r)
     301            0 :         return bf_err_r(r, "failed to create bf_set from pack");
     302              : 
     303            1 :     r = bf_rpack_kv_array(node, "elements", &child);
     304            1 :     if (r)
     305            0 :         return bf_rpack_key_err(r, "bf_set.elements");
     306            8 :     bf_rpack_array_foreach (child, elem_node) {
     307              :         const void *elem;
     308              :         size_t elem_len;
     309              : 
     310            3 :         r = bf_rpack_bin(elem_node, &elem, &elem_len);
     311            3 :         if (r)
     312            0 :             return bf_rpack_key_err(r, "bf_set.elements");
     313              : 
     314            3 :         if (elem_len != _set->elem_size) {
     315            0 :             return bf_err_r(-EINVAL,
     316              :                             "bf_set pack element is %lu bytes, it must be %lu",
     317              :                             elem_len, _set->elem_size);
     318              :         }
     319              : 
     320            3 :         r = bf_set_add_elem(_set, elem);
     321            3 :         if (r)
     322            0 :             return bf_err_r(r, "failed to insert element to bf_set");
     323              :     }
     324              : 
     325            1 :     *set = TAKE_PTR(_set);
     326              : 
     327            1 :     return 0;
     328              : }
     329              : 
     330           52 : void bf_set_free(struct bf_set **set)
     331              : {
     332           52 :     bf_assert(set);
     333              : 
     334           51 :     if (!*set)
     335              :         return;
     336              : 
     337           18 :     bf_list_clean(&(*set)->elems);
     338           18 :     freep((void *)&(*set)->name);
     339              :     freep((void *)set);
     340              : }
     341              : 
     342            3 : int bf_set_pack(const struct bf_set *set, bf_wpack_t *pack)
     343              : {
     344            3 :     bf_assert(set);
     345            2 :     bf_assert(pack);
     346              : 
     347            1 :     if (set->name)
     348              :         bf_wpack_kv_str(pack, "name", set->name);
     349              :     else
     350              :         bf_wpack_kv_nil(pack, "name");
     351              : 
     352            1 :     bf_wpack_open_array(pack, "key");
     353            4 :     for (size_t i = 0; i < set->n_comps; ++i)
     354            3 :         bf_wpack_enum(pack, set->key[i]);
     355            1 :     bf_wpack_close_array(pack);
     356              : 
     357            1 :     bf_wpack_open_array(pack, "elements");
     358            8 :     bf_list_foreach (&set->elems, elem_node)
     359            3 :         bf_wpack_bin(pack, bf_list_node_get_data(elem_node), set->elem_size);
     360            1 :     bf_wpack_close_array(pack);
     361              : 
     362            1 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     363              : }
     364              : 
     365            3 : void bf_set_dump(const struct bf_set *set, prefix_t *prefix)
     366              : {
     367            3 :     bf_assert(set && prefix);
     368              : 
     369            1 :     DUMP(prefix, "struct bf_set at %p", set);
     370            1 :     bf_dump_prefix_push(prefix);
     371              : 
     372            1 :     DUMP(prefix, "name: %s", set->name ?: "<anonymous>");
     373            1 :     DUMP(prefix, "key: bf_matcher_type[%zu]", set->n_comps);
     374            1 :     bf_dump_prefix_push(prefix);
     375            4 :     for (size_t i = 0; i < set->n_comps; ++i) {
     376            3 :         if (i == set->n_comps - 1)
     377            1 :             bf_dump_prefix_last(prefix);
     378              : 
     379            3 :         DUMP(prefix, "%s", bf_matcher_type_to_str(set->key[i]));
     380              :     }
     381            1 :     bf_dump_prefix_pop(prefix);
     382              : 
     383            1 :     DUMP(prefix, "elem_size: %lu", set->elem_size);
     384            1 :     DUMP(bf_dump_prefix_last(prefix), "elems: bf_list<bytes>[%lu]",
     385              :          bf_list_size(&set->elems));
     386              : 
     387            1 :     bf_dump_prefix_push(prefix);
     388           10 :     bf_list_foreach (&set->elems, elem_node) {
     389            4 :         if (bf_list_is_tail(&set->elems, elem_node))
     390            1 :             bf_dump_prefix_last(prefix);
     391              : 
     392            4 :         DUMP(prefix, "void * @ %p", bf_list_node_get_data(elem_node));
     393            4 :         bf_dump_prefix_push(prefix);
     394            4 :         bf_dump_hex(prefix, bf_list_node_get_data(elem_node), set->elem_size);
     395            4 :         bf_dump_prefix_pop(prefix);
     396              :     }
     397            1 :     bf_dump_prefix_pop(prefix);
     398              : 
     399            1 :     bf_dump_prefix_pop(prefix);
     400            1 : }
     401              : 
     402            6 : int bf_set_add_elem(struct bf_set *set, const void *elem)
     403              : {
     404            6 :     bf_assert(set && elem);
     405              : 
     406              :     _cleanup_free_ void *_elem = NULL;
     407              :     int r;
     408              : 
     409            4 :     _elem = malloc(set->elem_size);
     410            4 :     if (!_elem)
     411              :         return -ENOMEM;
     412              : 
     413            4 :     memcpy(_elem, elem, set->elem_size);
     414              : 
     415            4 :     r = bf_list_add_tail(&set->elems, _elem);
     416            4 :     if (r < 0)
     417            0 :         return r;
     418              : 
     419              :     TAKE_PTR(_elem);
     420              : 
     421              :     return 0;
     422              : }
        

Generated by: LCOV version 2.0-1