LCOV - code coverage report
Current view: top level - core - set.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 91.6 % 178 163
Test Date: 2025-08-19 17:27:08 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 "core/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 "core/dump.h"
      15              : #include "core/helper.h"
      16              : #include "core/list.h"
      17              : #include "core/logger.h"
      18              : #include "core/marsh.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 :     r = _bf_set_parse_key(raw_key, key, &n_comps);
     226           15 :     if (r)
     227            4 :         return bf_err_r(r, "failed to parse set key '%s'", raw_key);
     228              : 
     229           11 :     r = bf_set_new(&_set, name, key, n_comps);
     230           11 :     if (r)
     231              :         return r;
     232              : 
     233           11 :     _raw_payload = strdup(raw_payload);
     234           11 :     if (!_raw_payload)
     235            0 :         return bf_err_r(-ENOMEM, "failed to copy set raw payload '%s'",
     236              :                         raw_payload);
     237              : 
     238              :     tmp = _raw_payload;
     239           34 :     while ((raw_elem = strtok_r(tmp, "{};\n", &saveptr))) {
     240           24 :         raw_elem = bf_trim(raw_elem);
     241              : 
     242              :         /* While strtok_r() won't return empty token, the trimmed version of the
     243              :          * token can be empty! */
     244           24 :         if (raw_elem[0] == '\0')
     245            0 :             continue;
     246              : 
     247           24 :         r = _bf_set_parse_elem(_set, raw_elem);
     248           24 :         if (r)
     249            1 :             return bf_err_r(r, "failed to parse set element '%s'", raw_elem);
     250              : 
     251              :         tmp = NULL;
     252              :     }
     253              : 
     254           10 :     *set = TAKE_PTR(_set);
     255              : 
     256           10 :     return 0;
     257              : }
     258              : 
     259            3 : int bf_set_new_from_marsh(struct bf_set **set, const struct bf_marsh *marsh)
     260              : {
     261            3 :     bf_assert(set && marsh);
     262              : 
     263            1 :     _free_bf_set_ struct bf_set *_set = NULL;
     264              :     enum bf_matcher_type key[BF_SET_MAX_N_COMPS];
     265              :     struct bf_marsh *child = NULL;
     266              :     const char *name = NULL;
     267              :     size_t n_comps;
     268              :     int r;
     269              : 
     270            1 :     if (!(child = bf_marsh_next_child(marsh, child)))
     271              :         return -EINVAL;
     272            1 :     if (!bf_marsh_is_empty(child))
     273            0 :         name = child->data;
     274              : 
     275            1 :     if (!(child = bf_marsh_next_child(marsh, child)))
     276              :         return -EINVAL;
     277            1 :     memcpy(&n_comps, child->data, sizeof(n_comps));
     278              : 
     279            1 :     if (!(child = bf_marsh_next_child(marsh, child)))
     280              :         return -EINVAL;
     281            1 :     memcpy(key, child->data, n_comps * sizeof(enum bf_matcher_type));
     282              : 
     283            1 :     r = bf_set_new(&_set, name, key, n_comps);
     284            1 :     if (r < 0)
     285              :         return r;
     286              : 
     287            1 :     if (!(child = bf_marsh_next_child(marsh, child)))
     288              :         return -EINVAL;
     289            1 :     memcpy(&_set->elem_size, child->data, sizeof(_set->elem_size));
     290              : 
     291            1 :     if (!(child = bf_marsh_next_child(marsh, child)))
     292              :         return -EINVAL;
     293            4 :     for (size_t i = 0; i < child->data_len / _set->elem_size; ++i) {
     294            3 :         _cleanup_free_ void *elem = malloc(_set->elem_size);
     295            3 :         if (!elem)
     296              :             return -ENOMEM;
     297              : 
     298            3 :         memcpy(elem, child->data + (i * _set->elem_size), _set->elem_size);
     299            3 :         r = bf_list_add_tail(&_set->elems, elem);
     300            3 :         if (r < 0)
     301              :             return r;
     302              : 
     303              :         TAKE_PTR(elem);
     304              :     }
     305              : 
     306            1 :     *set = TAKE_PTR(_set);
     307              : 
     308            1 :     return 0;
     309              : }
     310              : 
     311           52 : void bf_set_free(struct bf_set **set)
     312              : {
     313           52 :     bf_assert(set);
     314              : 
     315           51 :     if (!*set)
     316              :         return;
     317              : 
     318           18 :     bf_list_clean(&(*set)->elems);
     319           18 :     freep((void *)&(*set)->name);
     320              :     freep((void *)set);
     321              : }
     322              : 
     323            3 : int bf_set_marsh(const struct bf_set *set, struct bf_marsh **marsh)
     324              : {
     325            3 :     bf_assert(set && marsh);
     326              : 
     327            1 :     _free_bf_marsh_ struct bf_marsh *_marsh = NULL;
     328              :     _cleanup_free_ uint8_t *data = NULL;
     329              :     size_t elem_idx = 0;
     330              :     int r;
     331              : 
     332            1 :     r = bf_marsh_new(&_marsh, NULL, 0);
     333            1 :     if (r < 0)
     334              :         return r;
     335              : 
     336            1 :     r = bf_marsh_add_child_raw(&_marsh, set->name,
     337            1 :                                set->name ? strlen(set->name) + 1 : 0);
     338            1 :     if (r < 0)
     339              :         return r;
     340              : 
     341            1 :     r = bf_marsh_add_child_raw(&_marsh, &set->n_comps, sizeof(set->n_comps));
     342            1 :     if (r < 0)
     343              :         return r;
     344              : 
     345            1 :     r = bf_marsh_add_child_raw(&_marsh, set->key,
     346            1 :                                set->n_comps * sizeof(enum bf_matcher_type));
     347            1 :     if (r < 0)
     348              :         return r;
     349              : 
     350            1 :     r = bf_marsh_add_child_raw(&_marsh, &set->elem_size,
     351              :                                sizeof(set->elem_size));
     352            1 :     if (r < 0)
     353              :         return r;
     354              : 
     355            1 :     data = malloc(set->elem_size * bf_list_size(&set->elems));
     356            1 :     if (!data)
     357            0 :         return bf_err_r(r, "failed to allocate memory for the set's content");
     358              : 
     359            7 :     bf_list_foreach (&set->elems, elem_node) {
     360            3 :         memcpy(data + (elem_idx * set->elem_size),
     361            3 :                bf_list_node_get_data(elem_node), set->elem_size);
     362            3 :         ++elem_idx;
     363              :     }
     364              : 
     365            1 :     r = bf_marsh_add_child_raw(&_marsh, data,
     366            1 :                                set->elem_size * bf_list_size(&set->elems));
     367            1 :     if (r < 0)
     368              :         return r;
     369              : 
     370            1 :     *marsh = TAKE_PTR(_marsh);
     371              : 
     372            1 :     return 0;
     373              : }
     374              : 
     375            3 : void bf_set_dump(const struct bf_set *set, prefix_t *prefix)
     376              : {
     377            3 :     bf_assert(set && prefix);
     378              : 
     379            1 :     DUMP(prefix, "struct bf_set at %p", set);
     380            1 :     bf_dump_prefix_push(prefix);
     381              : 
     382            1 :     DUMP(prefix, "name: %s", set->name ?: "<anonymous>");
     383            1 :     DUMP(prefix, "key: bf_matcher_type[%zu]", set->n_comps);
     384            1 :     bf_dump_prefix_push(prefix);
     385            4 :     for (size_t i = 0; i < set->n_comps; ++i) {
     386            3 :         if (i == set->n_comps - 1)
     387            1 :             bf_dump_prefix_last(prefix);
     388              : 
     389            3 :         DUMP(prefix, "%s", bf_matcher_type_to_str(set->key[i]));
     390              :     }
     391            1 :     bf_dump_prefix_pop(prefix);
     392              : 
     393            1 :     DUMP(prefix, "elem_size: %lu", set->elem_size);
     394            1 :     DUMP(bf_dump_prefix_last(prefix), "elems: bf_list<bytes>[%lu]",
     395              :          bf_list_size(&set->elems));
     396              : 
     397            1 :     bf_dump_prefix_push(prefix);
     398           10 :     bf_list_foreach (&set->elems, elem_node) {
     399            4 :         if (bf_list_is_tail(&set->elems, elem_node))
     400            1 :             bf_dump_prefix_last(prefix);
     401              : 
     402            4 :         DUMP(prefix, "void * @ %p", bf_list_node_get_data(elem_node));
     403            4 :         bf_dump_prefix_push(prefix);
     404            4 :         bf_dump_hex(prefix, bf_list_node_get_data(elem_node), set->elem_size);
     405            4 :         bf_dump_prefix_pop(prefix);
     406              :     }
     407            1 :     bf_dump_prefix_pop(prefix);
     408              : 
     409            1 :     bf_dump_prefix_pop(prefix);
     410            1 : }
     411              : 
     412            3 : int bf_set_add_elem(struct bf_set *set, void *elem)
     413              : {
     414            3 :     bf_assert(set && elem);
     415              : 
     416              :     _cleanup_free_ void *_elem = NULL;
     417              :     int r;
     418              : 
     419            1 :     _elem = malloc(set->elem_size);
     420            1 :     if (!_elem)
     421              :         return -ENOMEM;
     422              : 
     423            1 :     memcpy(_elem, elem, set->elem_size);
     424              : 
     425            1 :     r = bf_list_add_tail(&set->elems, _elem);
     426            1 :     if (r < 0)
     427            0 :         return r;
     428              : 
     429              :     TAKE_PTR(_elem);
     430              : 
     431              :     return 0;
     432              : }
        

Generated by: LCOV version 2.0-1