LCOV - code coverage report
Current view: top level - bpfilter/cgen/prog - map.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 68.6 % 223 153
Test Date: 2025-05-09 22:39:08 Functions: 76.2 % 21 16

            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 :     _cleanup_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 :     _cleanup_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 :     _cleanup_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            2 : 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            2 :     bf_assert(0 <= type && type < _BF_MAP_TYPE_MAX);
     169              : 
     170            2 :     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 _cleanup_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 :     _cleanup_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 :     _cleanup_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            2 :     case BF_MAP_TYPE_PRINTER:
     317              :     case BF_MAP_TYPE_SET:
     318            2 :         bf_warn("bf_map type %s is not yet supported",
     319              :                 _bf_map_type_to_str(map->type));
     320              :         return NULL;
     321            0 :     default:
     322            0 :         bf_err_r(-ENOTSUP, "bf_map type %d is not supported", map->type);
     323              :         return NULL;
     324              :     }
     325              : 
     326            0 :     r = _bf_btf_load(btf);
     327            0 :     if (r) {
     328            0 :         bf_err_r(r, "failed to load BTF data");
     329            0 :         return NULL;
     330              :     }
     331              : 
     332            0 :     return TAKE_PTR(btf);
     333              : }
     334              : 
     335            4 : int bf_map_create(struct bf_map *map, uint32_t flags)
     336              : {
     337              :     int token_fd;
     338            4 :     union bpf_attr attr = {};
     339            3 :     _cleanup_bf_btf_ struct bf_btf *btf = NULL;
     340              :     int r;
     341              : 
     342            4 :     bf_assert(map);
     343              : 
     344            3 :     if (map->key_size == BF_MAP_KEY_SIZE_UNKNOWN) {
     345            0 :         return bf_err_r(
     346              :             -EINVAL,
     347              :             "can't create a map with BF_MAP_KEY_SIZE_UNKNOWN key size");
     348              :     }
     349              : 
     350            3 :     if (map->value_size == BF_MAP_VALUE_SIZE_UNKNOWN) {
     351            0 :         return bf_err_r(
     352              :             -EINVAL,
     353              :             "can't create a map with BF_MAP_VALUE_SIZE_UNKNOWN value size");
     354              :     }
     355              : 
     356            3 :     if (map->n_elems == BF_MAP_N_ELEMS_UNKNOWN) {
     357            1 :         return bf_err_r(
     358              :             -EINVAL,
     359              :             "can't create a map with BF_MAP_N_ELEMS_UNKNOWN number of elements");
     360              :     }
     361              : 
     362            2 :     attr.map_type = _bf_map_bpf_type_to_kernel_type(map->bpf_type);
     363            2 :     attr.key_size = map->key_size;
     364            2 :     attr.value_size = map->value_size;
     365            2 :     attr.max_entries = map->n_elems;
     366            2 :     attr.map_flags = flags;
     367              : 
     368            2 :     if ((token_fd = bf_ctx_token()) != -1) {
     369            0 :         attr.map_token_fd = token_fd;
     370            0 :         attr.map_flags |= BPF_F_TOKEN_FD;
     371              :     }
     372              : 
     373              :     /** The BTF data is not mandatory to use the map, but a good addition.
     374              :      * Hence, bpfilter will try to make the BTF data available, but will
     375              :      * ignore if that fails. @ref _bf_map_make_btf is used to isolate the
     376              :      * BTF data generation: if it fails we ignore the issue, but if it
     377              :      * succeeds we update the @c bpf_attr structure with the BTF details.
     378              :      * There is some boilerplate for @ref bf_btf structure, it could be
     379              :      * simpler, but the current implementation ensure the BTF data is properly
     380              :      * freed on error, without preventing the BPF map to be created. */
     381            2 :     btf = _bf_map_make_btf(map);
     382            2 :     if (btf) {
     383            0 :         attr.btf_fd = btf->fd;
     384            0 :         attr.btf_key_type_id = btf->key_type_id;
     385            0 :         attr.btf_value_type_id = btf->value_type_id;
     386              :     }
     387              : 
     388            2 :     (void)snprintf(attr.map_name, BPF_OBJ_NAME_LEN, "%s", map->name);
     389              : 
     390            2 :     r = bf_bpf(BPF_MAP_CREATE, &attr);
     391            2 :     if (r < 0)
     392            1 :         return bf_err_r(r, "failed to create BPF map '%s'", map->name);
     393              : 
     394            1 :     map->fd = r;
     395              : 
     396            1 :     return 0;
     397              : }
     398              : 
     399            1 : void bf_map_destroy(struct bf_map *map)
     400              : {
     401            1 :     bf_assert(map);
     402              : 
     403            0 :     closep(&map->fd);
     404            0 : }
     405              : 
     406            0 : int bf_map_pin(const struct bf_map *map, int dir_fd)
     407              : {
     408              :     int r;
     409              : 
     410            0 :     bf_assert(map);
     411              : 
     412            0 :     r = bf_bpf_obj_pin(map->name, map->fd, dir_fd);
     413            0 :     if (r < 0)
     414            0 :         return bf_err_r(r, "failed to pin BPF map '%s'", map->name);
     415              : 
     416              :     return 0;
     417              : }
     418              : 
     419            0 : void bf_map_unpin(const struct bf_map *map, int dir_fd)
     420              : {
     421              :     int r;
     422              : 
     423            0 :     bf_assert(map);
     424              : 
     425            0 :     r = unlinkat(dir_fd, map->name, 0);
     426            0 :     if (r < 0 && errno != ENOENT) {
     427              :         // Do not warn on ENOENT, we want the file to be gone!
     428            0 :         bf_warn_r(
     429              :             errno,
     430              :             "failed to unlink BPF map '%s', assuming the map is not pinned",
     431              :             map->name);
     432              :     }
     433            0 : }
     434              : 
     435            0 : int bf_map_set_key_size(struct bf_map *map, size_t key_size)
     436              : {
     437            0 :     bf_assert(key_size != 0);
     438              : 
     439            0 :     if (map->fd != -1) {
     440            0 :         return bf_err_r(
     441              :             -EPERM,
     442              :             "can't change the size of the map key once it has been created");
     443              :     }
     444              : 
     445            0 :     map->key_size = key_size;
     446              : 
     447            0 :     return 0;
     448              : }
     449              : 
     450            0 : int bf_map_set_value_size(struct bf_map *map, size_t value_size)
     451              : {
     452            0 :     bf_assert(value_size != 0);
     453              : 
     454            0 :     if (map->fd != -1) {
     455            0 :         return bf_err_r(
     456              :             -EPERM,
     457              :             "can't change the size of the map value once it has been created");
     458              :     }
     459              : 
     460            0 :     map->value_size = value_size;
     461              : 
     462            0 :     return 0;
     463              : }
     464              : 
     465            2 : int bf_map_set_n_elems(struct bf_map *map, size_t n_elems)
     466              : {
     467            2 :     bf_assert(n_elems != 0);
     468              : 
     469            2 :     if (map->fd != -1) {
     470            1 :         return bf_err_r(
     471              :             -EPERM,
     472              :             "can't change the number of elements in a map once the map has been created");
     473              :     }
     474              : 
     475            1 :     map->n_elems = n_elems;
     476              : 
     477            1 :     return 0;
     478              : }
     479              : 
     480            3 : int bf_map_set_elem(const struct bf_map *map, void *key, void *value)
     481              : {
     482            3 :     union bpf_attr attr = {};
     483              : 
     484            6 :     bf_assert(map && key && value);
     485              : 
     486            0 :     attr.map_fd = map->fd;
     487            0 :     attr.key = (unsigned long long)key;
     488            0 :     attr.value = (unsigned long long)value;
     489            0 :     attr.flags = BPF_ANY;
     490              : 
     491            0 :     return bf_bpf(BPF_MAP_UPDATE_ELEM, &attr);
     492              : }
     493              : 
     494              : static const char *_bf_map_bpf_type_strs[] = {
     495              :     [BF_MAP_BPF_TYPE_ARRAY] = "BF_MAP_BPF_TYPE_ARRAY",
     496              :     [BF_MAP_BPF_TYPE_HASH] = "BF_MAP_BPF_TYPE_HASH",
     497              : };
     498              : 
     499              : static_assert(ARRAY_SIZE(_bf_map_bpf_type_strs) == _BF_MAP_BPF_TYPE_MAX,
     500              :               "missing entries in _bf_map_bpf_type_strs array");
     501              : 
     502            4 : const char *bf_map_bpf_type_to_str(enum bf_map_bpf_type bpf_type)
     503              : {
     504            4 :     bf_assert(0 <= bpf_type && bpf_type < _BF_MAP_BPF_TYPE_MAX);
     505              : 
     506            2 :     return _bf_map_bpf_type_strs[bpf_type];
     507              : }
     508              : 
     509            5 : int bf_map_bpf_type_from_str(const char *str, enum bf_map_bpf_type *bpf_type)
     510              : {
     511            5 :     bf_assert(str);
     512            4 :     bf_assert(bpf_type);
     513              : 
     514            6 :     for (size_t i = 0; i < _BF_MAP_BPF_TYPE_MAX; ++i) {
     515            5 :         if (bf_streq(_bf_map_bpf_type_strs[i], str)) {
     516            2 :             *bpf_type = i;
     517            2 :             return 0;
     518              :         }
     519              :     }
     520              : 
     521              :     return -EINVAL;
     522              : }
        

Generated by: LCOV version 2.0-1