LCOV - code coverage report
Current view: top level - bpfilter/cgen/prog - map.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 63.5 % 222 141
Test Date: 2025-08-19 17:27:08 Functions: 61.9 % 21 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/cgen/prog/map.h"
       7              : 
       8              : #include <linux/bpf.h>
       9              : 
      10              : #include <bpf/btf.h>
      11              : #include <errno.h>
      12              : #include <stdint.h>
      13              : #include <stdio.h>
      14              : #include <stdlib.h>
      15              : #include <string.h>
      16              : #include <unistd.h>
      17              : 
      18              : #include "bpfilter/ctx.h"
      19              : #include "core/bpf.h"
      20              : #include "core/dump.h"
      21              : #include "core/helper.h"
      22              : #include "core/logger.h"
      23              : #include "core/marsh.h"
      24              : 
      25           20 : static int _bf_map_new(struct bf_map **map, const char *name,
      26              :                        enum bf_map_type type, enum bpf_map_type bpf_type,
      27              :                        size_t key_size, size_t value_size, size_t n_elems)
      28              : {
      29           20 :     bf_assert(map && name);
      30              : 
      31           18 :     _free_bf_map_ struct bf_map *_map = NULL;
      32              : 
      33           18 :     if (name[0] == '\0')
      34            1 :         return bf_err_r(-EINVAL, "map name can't be empty");
      35              : 
      36           17 :     if (n_elems == 0)
      37            1 :         return bf_err_r(-EINVAL, "map can't be created without elements");
      38              : 
      39           16 :     _map = malloc(sizeof(*_map));
      40           16 :     if (!_map)
      41              :         return -ENOMEM;
      42              : 
      43           16 :     _map->type = type;
      44           16 :     _map->bpf_type = bpf_type;
      45           16 :     _map->key_size = key_size;
      46           16 :     _map->value_size = value_size;
      47           16 :     _map->n_elems = n_elems;
      48           16 :     _map->fd = -1;
      49              : 
      50           16 :     bf_strncpy(_map->name, BPF_OBJ_NAME_LEN, name);
      51              : 
      52           16 :     *map = TAKE_PTR(_map);
      53              : 
      54           16 :     return 0;
      55              : }
      56              : 
      57           20 : int bf_map_new(struct bf_map **map, const char *name, enum bf_map_type type,
      58              :                size_t key_size, size_t value_size, size_t n_elems)
      59              : {
      60              :     static enum bpf_map_type _map_type_to_bpf[_BF_MAP_TYPE_MAX] = {
      61              :         [BF_MAP_TYPE_COUNTERS] = BPF_MAP_TYPE_ARRAY,
      62              :         [BF_MAP_TYPE_PRINTER] = BPF_MAP_TYPE_ARRAY,
      63              :         [BF_MAP_TYPE_LOG] = BPF_MAP_TYPE_RINGBUF,
      64              :     };
      65              : 
      66           20 :     if (type == BF_MAP_TYPE_SET) {
      67            0 :         return bf_err_r(
      68              :             -EINVAL,
      69              :             "use bf_map_new_from_set() to create a bf_map from a bf_set");
      70              :     }
      71              : 
      72           20 :     return _bf_map_new(map, name, type, _map_type_to_bpf[type], key_size,
      73              :                        value_size, n_elems);
      74              : }
      75              : 
      76            0 : int bf_map_new_from_set(struct bf_map **map, const char *name,
      77              :                         const struct bf_set *set)
      78              : {
      79            0 :     return _bf_map_new(map, name, BF_MAP_TYPE_SET,
      80            0 :                        set->use_trie ? BPF_MAP_TYPE_LPM_TRIE :
      81              :                                        BPF_MAP_TYPE_HASH,
      82            0 :                        set->elem_size, 1, bf_list_size(&set->elems));
      83              : }
      84              : 
      85            3 : int bf_map_new_from_marsh(struct bf_map **map, int dir_fd,
      86              :                           const struct bf_marsh *marsh)
      87              : {
      88            1 :     _free_bf_map_ struct bf_map *_map = NULL;
      89              :     struct bf_marsh *elem = NULL;
      90              :     int r;
      91              : 
      92            3 :     bf_assert(map);
      93            2 :     bf_assert(marsh);
      94              : 
      95            1 :     _map = malloc(sizeof(*_map));
      96            1 :     if (!_map)
      97              :         return -ENOMEM;
      98              : 
      99            1 :     _map->fd = -1;
     100              : 
     101            1 :     if (!(elem = bf_marsh_next_child(marsh, elem)))
     102              :         return -EINVAL;
     103            1 :     memcpy(&_map->type, elem->data, sizeof(_map->type));
     104              : 
     105            1 :     if (!(elem = bf_marsh_next_child(marsh, elem)))
     106              :         return -EINVAL;
     107            1 :     memcpy(_map->name, elem->data, BPF_OBJ_NAME_LEN);
     108              : 
     109            1 :     if (!(elem = bf_marsh_next_child(marsh, elem)))
     110              :         return -EINVAL;
     111            1 :     memcpy(&_map->bpf_type, elem->data, sizeof(_map->bpf_type));
     112              : 
     113            1 :     if (!(elem = bf_marsh_next_child(marsh, elem)))
     114              :         return -EINVAL;
     115            1 :     memcpy(&_map->key_size, elem->data, sizeof(_map->key_size));
     116              : 
     117            1 :     if (!(elem = bf_marsh_next_child(marsh, elem)))
     118              :         return -EINVAL;
     119            1 :     memcpy(&_map->value_size, elem->data, sizeof(_map->value_size));
     120              : 
     121            1 :     if (!(elem = bf_marsh_next_child(marsh, elem)))
     122              :         return -EINVAL;
     123            1 :     memcpy(&_map->n_elems, elem->data, sizeof(_map->n_elems));
     124              : 
     125            1 :     if (bf_marsh_next_child(marsh, elem))
     126            0 :         return bf_err_r(-E2BIG, "too many elements in bf_map marsh");
     127              : 
     128            1 :     r = bf_bpf_obj_get(_map->name, dir_fd, &_map->fd);
     129            1 :     if (r < 0)
     130            0 :         return bf_err_r(r, "failed to open pinned BPF map '%s'", _map->name);
     131              : 
     132            1 :     *map = TAKE_PTR(_map);
     133              : 
     134            1 :     return 0;
     135              : }
     136              : 
     137           39 : void bf_map_free(struct bf_map **map)
     138              : {
     139           39 :     bf_assert(map);
     140              : 
     141           38 :     if (!*map)
     142              :         return;
     143              : 
     144           17 :     closep(&(*map)->fd);
     145              :     freep((void *)map);
     146              : }
     147              : 
     148            3 : int bf_map_marsh(const struct bf_map *map, struct bf_marsh **marsh)
     149              : {
     150            1 :     _free_bf_marsh_ struct bf_marsh *_marsh = NULL;
     151              :     int r;
     152              : 
     153            3 :     bf_assert(map);
     154            2 :     bf_assert(marsh);
     155              : 
     156            1 :     r = bf_marsh_new(&_marsh, NULL, 0);
     157            1 :     if (r < 0)
     158              :         return r;
     159              : 
     160            1 :     r = bf_marsh_add_child_raw(&_marsh, &map->type, sizeof(map->type));
     161            1 :     if (r < 0)
     162              :         return r;
     163              : 
     164            1 :     r = bf_marsh_add_child_raw(&_marsh, map->name, BPF_OBJ_NAME_LEN);
     165            1 :     if (r < 0)
     166              :         return r;
     167              : 
     168            1 :     r = bf_marsh_add_child_raw(&_marsh, &map->bpf_type, sizeof(map->bpf_type));
     169            1 :     if (r < 0)
     170              :         return r;
     171              : 
     172            1 :     r = bf_marsh_add_child_raw(&_marsh, &map->key_size, sizeof(map->key_size));
     173            1 :     if (r < 0)
     174              :         return r;
     175              : 
     176            1 :     r = bf_marsh_add_child_raw(&_marsh, &map->value_size,
     177              :                                sizeof(map->value_size));
     178            1 :     if (r < 0)
     179              :         return r;
     180              : 
     181            1 :     r = bf_marsh_add_child_raw(&_marsh, &map->n_elems, sizeof(map->n_elems));
     182            1 :     if (r < 0)
     183              :         return r;
     184              : 
     185            1 :     *marsh = TAKE_PTR(_marsh);
     186              : 
     187            1 :     return 0;
     188              : }
     189              : 
     190            0 : static const char *_bf_map_type_to_str(enum bf_map_type type)
     191              : {
     192              :     static const char *type_strs[] = {
     193              :         [BF_MAP_TYPE_COUNTERS] = "BF_MAP_TYPE_COUNTERS",
     194              :         [BF_MAP_TYPE_PRINTER] = "BF_MAP_TYPE_PRINTER",
     195              :         [BF_MAP_TYPE_LOG] = "BF_MAP_TYPE_LOG",
     196              :         [BF_MAP_TYPE_SET] = "BF_MAP_TYPE_SET",
     197              :     };
     198              : 
     199              :     static_assert(ARRAY_SIZE(type_strs) == _BF_MAP_TYPE_MAX,
     200              :                   "missing entries in _bf_map_type_strs array");
     201            0 :     bf_assert(0 <= type && type < _BF_MAP_TYPE_MAX);
     202              : 
     203            0 :     return type_strs[type];
     204              : }
     205              : 
     206            0 : static const char *_bf_bpf_type_to_str(enum bpf_map_type type)
     207              : {
     208              :     static const char *type_strs[] = {
     209              :         [BPF_MAP_TYPE_ARRAY] = "BPF_MAP_TYPE_ARRAY",
     210              :         [BPF_MAP_TYPE_RINGBUF] = "BPF_MAP_TYPE_RINGBUF",
     211              :         [BPF_MAP_TYPE_LPM_TRIE] = "BPF_MAP_TYPE_LPM_TRIE",
     212              :         [BPF_MAP_TYPE_HASH] = "BPF_MAP_TYPE_HASH",
     213              :     };
     214              : 
     215            0 :     return type_strs[type] ? type_strs[type] : "<no mapping>";
     216              : }
     217              : 
     218            3 : void bf_map_dump(const struct bf_map *map, prefix_t *prefix)
     219              : {
     220            3 :     bf_assert(map);
     221            2 :     bf_assert(prefix);
     222              : 
     223            1 :     DUMP(prefix, "struct bf_map at %p", map);
     224              : 
     225            1 :     bf_dump_prefix_push(prefix);
     226            1 :     DUMP(prefix, "type: %s", _bf_map_type_to_str(map->type));
     227            1 :     DUMP(prefix, "name: %s", map->name);
     228            1 :     DUMP(prefix, "bpf_type: %s", _bf_bpf_type_to_str(map->bpf_type));
     229            1 :     DUMP(prefix, "key_size: %lu", map->key_size);
     230            1 :     DUMP(prefix, "value_size: %lu", map->value_size);
     231              : 
     232            1 :     if (map->n_elems == BF_MAP_N_ELEMS_UNKNOWN) {
     233            0 :         DUMP(prefix, "n_elems: unknown");
     234              :     } else {
     235            1 :         DUMP(prefix, "n_elems: %lu", map->n_elems);
     236              :     }
     237              : 
     238            1 :     bf_dump_prefix_last(prefix);
     239            1 :     DUMP(prefix, "fd: %d", map->fd);
     240              : 
     241            1 :     bf_dump_prefix_pop(prefix);
     242            1 : }
     243              : 
     244              : #define _free_bf_btf_ __attribute__((__cleanup__(_bf_btf_free)))
     245              : 
     246              : struct bf_btf
     247              : {
     248              :     struct btf *btf;
     249              :     uint32_t key_type_id;
     250              :     uint32_t value_type_id;
     251              :     int fd;
     252              : };
     253              : 
     254              : static void _bf_btf_free(struct bf_btf **btf);
     255              : 
     256            6 : static int _bf_btf_new(struct bf_btf **btf)
     257              : {
     258            5 :     _free_bf_btf_ struct bf_btf *_btf = NULL;
     259              : 
     260            6 :     bf_assert(btf);
     261              : 
     262            5 :     _btf = malloc(sizeof(struct bf_btf));
     263            5 :     if (!_btf)
     264              :         return -ENOMEM;
     265              : 
     266            5 :     _btf->fd = -1;
     267              : 
     268            5 :     _btf->btf = btf__new_empty();
     269            5 :     if (!_btf->btf)
     270            0 :         return -errno;
     271              : 
     272            5 :     *btf = TAKE_PTR(_btf);
     273              : 
     274            5 :     return 0;
     275              : }
     276              : 
     277           16 : static void _bf_btf_free(struct bf_btf **btf)
     278              : {
     279           16 :     bf_assert(btf);
     280              : 
     281           15 :     if (!*btf)
     282              :         return;
     283              : 
     284            5 :     btf__free((*btf)->btf);
     285            5 :     closep(&(*btf)->fd);
     286              :     freep((void *)btf);
     287              : }
     288              : 
     289            0 : static int _bf_btf_load(struct bf_btf *btf)
     290              : {
     291            0 :     union bpf_attr attr = {};
     292              :     int token_fd;
     293              :     const void *raw;
     294              :     int r;
     295              : 
     296            0 :     bf_assert(btf);
     297              : 
     298            0 :     raw = btf__raw_data(btf->btf, &attr.btf_size);
     299            0 :     if (!raw)
     300            0 :         return bf_err_r(errno, "failed to request BTF raw data");
     301              : 
     302            0 :     attr.btf = bf_ptr_to_u64(raw);
     303              : 
     304            0 :     token_fd = bf_ctx_token();
     305            0 :     if (token_fd != -1) {
     306            0 :         attr.btf_token_fd = token_fd;
     307            0 :         attr.btf_flags |= BPF_F_TOKEN_FD;
     308              :     }
     309              : 
     310            0 :     r = bf_bpf(BPF_BTF_LOAD, &attr);
     311            0 :     if (r < 0)
     312            0 :         return bf_err_r(r, "failed to load BTF data");
     313              : 
     314            0 :     btf->fd = r;
     315              : 
     316            0 :     return 0;
     317              : }
     318              : 
     319              : /**
     320              :  * Create the BTF data for the map.
     321              :  *
     322              :  * @param map Map to create the BTF data for. @c map.type will define the
     323              :  *        exact content of the BTF object. Can't be NULL.
     324              :  * @return A @ref bf_btf structure on success, or NULL on error. The
     325              :  *         @ref bf_btf structure is owned by the caller.
     326              :  */
     327            2 : static struct bf_btf *_bf_map_make_btf(const struct bf_map *map)
     328              : {
     329            2 :     _free_bf_btf_ struct bf_btf *btf = NULL;
     330              :     struct btf *kbtf;
     331              :     int r;
     332              : 
     333            2 :     bf_assert(map);
     334              : 
     335            2 :     r = _bf_btf_new(&btf);
     336            2 :     if (r < 0)
     337              :         return NULL;
     338              : 
     339            2 :     kbtf = btf->btf;
     340              : 
     341            2 :     switch (map->type) {
     342            0 :     case BF_MAP_TYPE_COUNTERS:
     343            0 :         btf__add_int(kbtf, "u64", 8, 0);
     344            0 :         btf->key_type_id = btf__add_int(kbtf, "u32", 4, 0);
     345            0 :         btf->value_type_id = btf__add_struct(kbtf, "bf_counters", 16);
     346            0 :         btf__add_field(kbtf, "packets", 1, 0, 0);
     347            0 :         btf__add_field(kbtf, "bytes", 1, 64, 0);
     348              :         break;
     349              :     case BF_MAP_TYPE_PRINTER:
     350              :     case BF_MAP_TYPE_SET:
     351              :     case BF_MAP_TYPE_LOG:
     352              :         // No BTF data available for this map types
     353              :         return NULL;
     354            0 :     default:
     355            0 :         bf_err_r(-ENOTSUP, "bf_map type %d is not supported", map->type);
     356              :         return NULL;
     357              :     }
     358              : 
     359            0 :     r = _bf_btf_load(btf);
     360            0 :     if (r) {
     361            0 :         bf_err_r(r, "failed to load BTF data");
     362            0 :         return NULL;
     363              :     }
     364              : 
     365            0 :     return TAKE_PTR(btf);
     366              : }
     367              : 
     368            4 : int bf_map_create(struct bf_map *map, uint32_t flags)
     369              : {
     370              :     int token_fd;
     371            4 :     union bpf_attr attr = {};
     372            3 :     _free_bf_btf_ struct bf_btf *btf = NULL;
     373              :     int r;
     374              : 
     375            4 :     bf_assert(map);
     376              : 
     377            3 :     if (map->key_size == BF_MAP_KEY_SIZE_UNKNOWN) {
     378            0 :         return bf_err_r(
     379              :             -EINVAL,
     380              :             "can't create a map with BF_MAP_KEY_SIZE_UNKNOWN key size");
     381              :     }
     382              : 
     383            3 :     if (map->value_size == BF_MAP_VALUE_SIZE_UNKNOWN) {
     384            0 :         return bf_err_r(
     385              :             -EINVAL,
     386              :             "can't create a map with BF_MAP_VALUE_SIZE_UNKNOWN value size");
     387              :     }
     388              : 
     389            3 :     if (map->n_elems == BF_MAP_N_ELEMS_UNKNOWN) {
     390            1 :         return bf_err_r(
     391              :             -EINVAL,
     392              :             "can't create a map with BF_MAP_N_ELEMS_UNKNOWN number of elements");
     393              :     }
     394              : 
     395            2 :     attr.map_type = map->bpf_type;
     396            2 :     attr.key_size = map->key_size;
     397            2 :     attr.value_size = map->value_size;
     398            2 :     attr.max_entries = map->n_elems;
     399            2 :     attr.map_flags = flags;
     400              : 
     401              :     // NO_PREALLOC is *required* for LPM_TRIE map
     402            2 :     if (map->bpf_type == BPF_MAP_TYPE_LPM_TRIE)
     403            0 :         attr.map_flags |= BPF_F_NO_PREALLOC;
     404              : 
     405            2 :     if ((token_fd = bf_ctx_token()) != -1) {
     406            0 :         attr.map_token_fd = token_fd;
     407            0 :         attr.map_flags |= BPF_F_TOKEN_FD;
     408              :     }
     409              : 
     410              :     /** The BTF data is not mandatory to use the map, but a good addition.
     411              :      * Hence, bpfilter will try to make the BTF data available, but will
     412              :      * ignore if that fails. @ref _bf_map_make_btf is used to isolate the
     413              :      * BTF data generation: if it fails we ignore the issue, but if it
     414              :      * succeeds we update the @c bpf_attr structure with the BTF details.
     415              :      * There is some boilerplate for @ref bf_btf structure, it could be
     416              :      * simpler, but the current implementation ensure the BTF data is properly
     417              :      * freed on error, without preventing the BPF map to be created. */
     418            2 :     btf = _bf_map_make_btf(map);
     419            2 :     if (btf) {
     420            0 :         attr.btf_fd = btf->fd;
     421            0 :         attr.btf_key_type_id = btf->key_type_id;
     422            0 :         attr.btf_value_type_id = btf->value_type_id;
     423              :     }
     424              : 
     425            2 :     (void)snprintf(attr.map_name, BPF_OBJ_NAME_LEN, "%s", map->name);
     426              : 
     427            2 :     r = bf_bpf(BPF_MAP_CREATE, &attr);
     428            2 :     if (r < 0)
     429            1 :         return bf_err_r(r, "failed to create BPF map '%s'", map->name);
     430              : 
     431            1 :     map->fd = r;
     432              : 
     433            1 :     return 0;
     434              : }
     435              : 
     436            1 : void bf_map_destroy(struct bf_map *map)
     437              : {
     438            1 :     bf_assert(map);
     439              : 
     440            0 :     closep(&map->fd);
     441            0 : }
     442              : 
     443            0 : int bf_map_pin(const struct bf_map *map, int dir_fd)
     444              : {
     445              :     int r;
     446              : 
     447            0 :     bf_assert(map);
     448              : 
     449            0 :     r = bf_bpf_obj_pin(map->name, map->fd, dir_fd);
     450            0 :     if (r < 0)
     451            0 :         return bf_err_r(r, "failed to pin BPF map '%s'", map->name);
     452              : 
     453              :     return 0;
     454              : }
     455              : 
     456            0 : void bf_map_unpin(const struct bf_map *map, int dir_fd)
     457              : {
     458              :     int r;
     459              : 
     460            0 :     bf_assert(map);
     461              : 
     462            0 :     r = unlinkat(dir_fd, map->name, 0);
     463            0 :     if (r < 0 && errno != ENOENT) {
     464              :         // Do not warn on ENOENT, we want the file to be gone!
     465            0 :         bf_warn_r(
     466              :             errno,
     467              :             "failed to unlink BPF map '%s', assuming the map is not pinned",
     468              :             map->name);
     469              :     }
     470            0 : }
     471              : 
     472            0 : int bf_map_set_key_size(struct bf_map *map, size_t key_size)
     473              : {
     474            0 :     bf_assert(key_size != 0);
     475              : 
     476            0 :     if (map->fd != -1) {
     477            0 :         return bf_err_r(
     478              :             -EPERM,
     479              :             "can't change the size of the map key once it has been created");
     480              :     }
     481              : 
     482            0 :     map->key_size = key_size;
     483              : 
     484            0 :     return 0;
     485              : }
     486              : 
     487            0 : int bf_map_set_value_size(struct bf_map *map, size_t value_size)
     488              : {
     489            0 :     bf_assert(value_size != 0);
     490              : 
     491            0 :     if (map->fd != -1) {
     492            0 :         return bf_err_r(
     493              :             -EPERM,
     494              :             "can't change the size of the map value once it has been created");
     495              :     }
     496              : 
     497            0 :     map->value_size = value_size;
     498              : 
     499            0 :     return 0;
     500              : }
     501              : 
     502            2 : int bf_map_set_n_elems(struct bf_map *map, size_t n_elems)
     503              : {
     504            2 :     bf_assert(n_elems != 0);
     505              : 
     506            2 :     if (map->fd != -1) {
     507            1 :         return bf_err_r(
     508              :             -EPERM,
     509              :             "can't change the number of elements in a map once the map has been created");
     510              :     }
     511              : 
     512            1 :     map->n_elems = n_elems;
     513              : 
     514            1 :     return 0;
     515              : }
     516              : 
     517            3 : int bf_map_set_elem(const struct bf_map *map, void *key, void *value)
     518              : {
     519            3 :     union bpf_attr attr = {};
     520              : 
     521            6 :     bf_assert(map && key && value);
     522              : 
     523            0 :     attr.map_fd = map->fd;
     524            0 :     attr.key = (unsigned long long)key;
     525            0 :     attr.value = (unsigned long long)value;
     526            0 :     attr.flags = BPF_ANY;
     527              : 
     528            0 :     return bf_bpf(BPF_MAP_UPDATE_ELEM, &attr);
     529              : }
        

Generated by: LCOV version 2.0-1