LCOV - code coverage report
Current view: top level - bpfilter/cgen/prog - map.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 67.0 % 221 148
Test Date: 2025-05-27 09:39:07 Functions: 71.4 % 21 15

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

Generated by: LCOV version 2.0-1