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

Generated by: LCOV version 2.0-1