LCOV - code coverage report
Current view: top level - libbpfilter/include/bpfilter - pack.h (source / functions) Coverage Total Hit
Test: lcov.out Lines: 79.2 % 24 19
Test Date: 2025-09-14 19:43:39 Functions: - 0 0

            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              : #pragma once
       7              : 
       8              : #include <stdbool.h>
       9              : #include <stddef.h>
      10              : #include <stdint.h>
      11              : 
      12              : #include <bpfilter/logger.h>
      13              : 
      14              : /**
      15              :  * @file pack.h
      16              :  *
      17              :  * Serialization is used to send/receive bpfilter objects to and from the
      18              :  * daemon. While bpfilter originally used a custom logic to convert its objects
      19              :  * into binary data, it was inefficient and didn't support different versions of
      20              :  * the same object (when fields are added or removed). Instead, the new packing
      21              :  * logic provides an API to (de)serialize bpfilter objects using
      22              :  * [MessagePack](https://msgpack.org).
      23              :  *
      24              :  * The packing mechanism doesn't implement the MessagePack protocol directly,
      25              :  * but relies on the [MPack](https://ludocode.github.io/mpack/) implementation
      26              :  * instead. MPack provides the following advantages:
      27              :  * - Fast (de)serialization: 4x faster than the previous custom serialization
      28              :  *   logic, and 10x faster than Jansson.
      29              :  * - Named fields: each serialized field of an object is named, allowing the
      30              :  *   objects to evolve and introduce or remove new fields while maintaining
      31              :  *   backward compatibility.
      32              :  * - Can be used in place: existing objects format can be used as-is, without
      33              :  *   extra field dedicated to the serializer or schema file (hello Protobuf
      34              :  *   and Thrift).
      35              :  *
      36              :  * Two different packing objects are provided:
      37              :  * - `bf_wpack_t`: the writer object, to serialize data. To keep the
      38              :  *   the serialization as simple as possible, no error will be returned when
      39              :  *   a field is serialized. Instead, users must use `bf_wpack_is_valid` to
      40              :  *   validate the serialized data before it is exported. This behaviour is
      41              :  *   different for the rest of the core module, but is justified for usability
      42              :  *   reasons.
      43              :  * - `bf_rpack_t`: node-based API to read serialized data. Each serialized field
      44              :  *   can be represented as a node. The object deserialization functions should
      45              :  *   expect to receive a node which represents them.
      46              :  *
      47              :  * ## Serializing
      48              :  * `bf_wpack_t` always have a root node containing key-value pairs. Keys should
      49              :  * be strings, and values can be any primal type, array, or another object.
      50              :  * `bf_wpack_kv_TYPE` functions should be used to write both the key and the
      51              :  * value in an object, `bf_wpack_TYPE` functions should be used to write single
      52              :  * objects (without a key) into arrays.
      53              :  * To nest objects and arrays, use `bf_wpack_open_object` and
      54              :  * `bf_wpack_open_array`, they both allow the caller to provide a key: if the
      55              :  * object (or array) is inserted into an object you should provide a key, if the
      56              :  * object (or array) is inserted into an array you should not provide a key. In
      57              :  * any case, call `bf_wpack_close_object` or `bf_wpack_close_array` to complete
      58              :  * the nested object/array.
      59              :  * Before using the serialized data, ensure it is valid with `bf_wpack_is_valid`,
      60              :  * as not error will be returned during the pack creation.
      61              :  *
      62              :  * ## Deserializing
      63              :  * `bf_rpack_t` is the deserialization object, although we do not read directly
      64              :  * from it. Instead, one should get the root object with `bf_rpack_root`, then
      65              :  * query keys from it.
      66              :  * All the functions used to deserialize data from the pack are available in two
      67              :  * formats:
      68              :  * - `bf_rpack_TYPE`: read a typed value from a node.
      69              :  * - `bf_rpack_kv_TYPE`: read the value identified by a key in an object node.
      70              :  * To prevent any tempering or corruption, every function able to read data from
      71              :  * a node will return a value: 0 on success, or a negative integer on failure.
      72              :  *
      73              :  * @note To ensure compatibility of the binary data over time, the following
      74              :  * constraints must be followed:
      75              :  * - Every enumeration value is serialized as integer: it should support every
      76              :  *   possible enumeration value used by bpfilter, including negative values.
      77              :  * - `size_t` values are serialied as `uint64_t`: for similar reasons to he
      78              :  *   enumerations.
      79              :  *
      80              :  * @todo `bf_wpack_kv_TYPE` functions should only write into objects.
      81              :  * @todo `bf_wpack_TYPE` functions should only write into arrays.
      82              :  */
      83              : 
      84              : struct bf_wpack;
      85              : typedef struct bf_wpack bf_wpack_t;
      86              : 
      87              : struct bf_list;
      88              : typedef struct bf_list bf_list;
      89              : 
      90              : /// @brief Cleanup attribute for dynamically allocated `bf_wpack_t` objects.
      91              : #define _free_bf_wpack_ __attribute__((cleanup(bf_wpack_free)))
      92              : 
      93              : /**
      94              :  * @brief Allocate and initialize a new `bf_wpack_t` object.
      95              :  *
      96              :  * @param pack `bf_wpack_t` object to allocate an initialize. Can't be NULL.
      97              :  * @return 0 on sucess, or a negative error value on failure.
      98              :  */
      99              : int bf_wpack_new(bf_wpack_t **pack);
     100              : 
     101              : /**
     102              :  * @brief Deinitialize and deallocate a `bf_wpack_t` object.
     103              :  *
     104              :  * @param pack `bf_wpack_t` object to deallocate and deinitialize. Can't be
     105              :  *        NULL. If `*pack` is NULL, this function has no effect.
     106              :  */
     107              : void bf_wpack_free(bf_wpack_t **pack);
     108              : 
     109              : /**
     110              :  * @brief Check if the serialized data is valid.
     111              :  *
     112              :  * @param pack `bf_wpack_t` object to check. Can't be NULL.
     113              :  * @return True if the serialized data is valid, false otherwise.
     114              :  */
     115              : bool bf_wpack_is_valid(bf_wpack_t *pack);
     116              : 
     117              : /**
     118              :  * @brief Get the serialized data.
     119              :  *
     120              :  * Ensure the buffer is flushed and all the remaining data has been serialized.
     121              :  * On success, `*data` points to a buffer containing `data_len` bytes. The
     122              :  * caller do not own the data.
     123              :  *
     124              :  * @return 0 on success, or a negative error value on failure.
     125              :  */
     126              : int bf_wpack_get_data(bf_wpack_t *pack, const void **data, size_t *data_len);
     127              : 
     128              : void bf_wpack_nil(bf_wpack_t *pack);
     129              : void bf_wpack_int(bf_wpack_t *pack, int value);
     130              : void bf_wpack_uint(bf_wpack_t *pack, unsigned int value);
     131              : void bf_wpack_u8(bf_wpack_t *pack, uint8_t value);
     132              : void bf_wpack_u16(bf_wpack_t *pack, uint16_t value);
     133              : void bf_wpack_u32(bf_wpack_t *pack, uint32_t value);
     134              : void bf_wpack_u64(bf_wpack_t *pack, uint64_t value);
     135              : void bf_wpack_bool(bf_wpack_t *pack, bool value);
     136              : void bf_wpack_str(bf_wpack_t *pack, const char *value);
     137              : void bf_wpack_bin(bf_wpack_t *pack, const void *value, size_t len);
     138              : void bf_wpack_list(bf_wpack_t *pack, const bf_list *value);
     139              : #define bf_wpack_enum(pack, value) bf_wpack_int((pack), (value))
     140              : 
     141              : static inline void bf_wpack_kv_nil(bf_wpack_t *pack, const char *key)
     142              : {
     143            2 :     bf_wpack_str(pack, key);
     144            2 :     bf_wpack_nil(pack);
     145            2 : }
     146              : 
     147              : static inline void bf_wpack_kv_int(bf_wpack_t *pack, const char *key, int value)
     148              : {
     149           51 :     bf_wpack_str(pack, key);
     150           51 :     bf_wpack_int(pack, value);
     151            0 : }
     152              : 
     153              : static inline void bf_wpack_kv_uint(bf_wpack_t *pack, const char *key,
     154              :                                     unsigned int value)
     155              : {
     156            0 :     bf_wpack_str(pack, key);
     157            0 :     bf_wpack_uint(pack, value);
     158            0 : }
     159              : 
     160              : static inline void bf_wpack_kv_u8(bf_wpack_t *pack, const char *key,
     161              :                                   uint8_t value)
     162              : {
     163            5 :     bf_wpack_str(pack, key);
     164            5 :     bf_wpack_u8(pack, value);
     165              : }
     166              : 
     167              : static inline void bf_wpack_kv_u16(bf_wpack_t *pack, const char *key,
     168              :                                    uint16_t value)
     169              : {
     170              :     bf_wpack_str(pack, key);
     171              :     bf_wpack_u16(pack, value);
     172              : }
     173              : 
     174              : static inline void bf_wpack_kv_u32(bf_wpack_t *pack, const char *key,
     175              :                                    uint32_t value)
     176              : {
     177            5 :     bf_wpack_str(pack, key);
     178            5 :     bf_wpack_u32(pack, value);
     179              : }
     180              : 
     181              : static inline void bf_wpack_kv_u64(bf_wpack_t *pack, const char *key,
     182              :                                    uint64_t value)
     183              : {
     184           19 :     bf_wpack_str(pack, key);
     185           19 :     bf_wpack_u64(pack, value);
     186              : }
     187              : 
     188              : static inline void bf_wpack_kv_bool(bf_wpack_t *pack, const char *key,
     189              :                                     bool value)
     190              : {
     191            5 :     bf_wpack_str(pack, key);
     192            5 :     bf_wpack_bool(pack, value);
     193              : }
     194              : 
     195              : static inline void bf_wpack_kv_str(bf_wpack_t *pack, const char *key,
     196              :                                    const char *str)
     197              : {
     198           11 :     bf_wpack_str(pack, key);
     199           11 :     bf_wpack_str(pack, str);
     200            0 : }
     201              : 
     202              : static inline void bf_wpack_kv_bin(bf_wpack_t *pack, const char *key,
     203              :                                    const void *data, size_t data_len)
     204              : {
     205           23 :     bf_wpack_str(pack, key);
     206           23 :     bf_wpack_bin(pack, data, data_len);
     207              : }
     208              : 
     209              : static inline void bf_wpack_kv_enum(bf_wpack_t *pack, const char *key,
     210              :                                     int value)
     211              : {
     212            7 :     bf_wpack_str(pack, key);
     213            7 :     bf_wpack_enum(pack, value);
     214              : }
     215              : 
     216              : void bf_wpack_kv_list(bf_wpack_t *pack, const char *key, const bf_list *value);
     217              : 
     218              : /**
     219              :  * @brief Open a new object in the pack.
     220              :  *
     221              :  * Once this function returns, any call to `bf_wpack_TYPE` or `bf_wpack_kv_TYPE`
     222              :  * will insert data into the object.
     223              :  *
     224              :  * @param pack `bf_wpack_t` object to open the object in. Can't be NULL.
     225              :  * @param key Key to use for the object. If NULL, no key is inserted.
     226              :  */
     227              : void bf_wpack_open_object(bf_wpack_t *pack, const char *key);
     228              : 
     229              : /**
     230              :  * @brief Close the current object.
     231              :  *
     232              :  * Once the object is closed, any call to `bf_wpack_TYPE` or `bf_wpack_kv_TYPE`
     233              :  * will insert data into the parent object or array.
     234              :  *
     235              :  * @param pack `bf_wpack_t` object to close the object in. Can't be NULL.
     236              :  */
     237              : void bf_wpack_close_object(bf_wpack_t *pack);
     238              : 
     239              : /**
     240              :  * @brief Open a new array in the pack.
     241              :  *
     242              :  * Once this function returns, any call to `bf_wpack_TYPE` or `bf_wpack_kv_TYPE`
     243              :  * will insert data into the array.
     244              :  *
     245              :  * @param pack `bf_wpack_t` object to open the array in. Can't be NULL.
     246              :  * @param key Key to use for the array. If NULL, no key is inserted.
     247              :  */
     248              : void bf_wpack_open_array(bf_wpack_t *pack, const char *key);
     249              : 
     250              : /**
     251              :  * @brief Close the current array.
     252              :  *
     253              :  * Once the array is closed, any call to `bf_wpack_TYPE` or `bf_wpack_kv_TYPE`
     254              :  * will insert data into the parent object or array.
     255              :  *
     256              :  * @param pack `bf_wpack_t` object to close the array in. Can't be NULL.
     257              :  */
     258              : void bf_wpack_close_array(bf_wpack_t *pack);
     259              : 
     260              : struct bf_rpack;
     261              : typedef struct bf_rpack bf_rpack_t;
     262              : 
     263              : /**
     264              :  * @brief Opaque structure to pass nodes by value.
     265              :  */
     266              : typedef union
     267              : {
     268              :     char _opaque[16];
     269              :     void *_align;
     270              : } bf_rpack_node_t;
     271              : 
     272              : /// @brief Cleanup attribute for dynamically allocated `bf_rpack_t` objects.
     273              : #define _free_bf_rpack_ __attribute__((cleanup(bf_rpack_free)))
     274              : 
     275              : /**
     276              :  * @brief Loop over all the nodes in an array node.
     277              :  *
     278              :  * The counter `i` is accessible within the scope.
     279              :  *
     280              :  * @warning This loop must ensure we don't query the node for invalid data:
     281              :  * we should not request a node from the array if it's empty, of if the index
     282              :  * is out of the array. Otherwise, it would put the `bf_rpack_t` object in
     283              :  * failure mode.
     284              :  *
     285              :  * @param node Array node. If the node is not an array, the loop will be
     286              :  *        ignored.
     287              :  * @param value_node A node in the array, updated on each iteration.
     288              :  */
     289              : #define bf_rpack_array_foreach(node, value_node)                               \
     290              :     for (size_t i = ((bf_rpack_array_count(node) ?                             \
     291              :                           ((value_node) = bf_rpack_array_value_at(node, 0)) :  \
     292              :                           (bf_rpack_node_t) {}),                               \
     293              :                      0);                                                       \
     294              :          i < bf_rpack_array_count(node);                                       \
     295              :          ++i, (value_node) = (i < bf_rpack_array_count(node) ?                 \
     296              :                                   bf_rpack_array_value_at(node, i) :           \
     297              :                                   (bf_rpack_node_t) {}))
     298              : 
     299              : /**
     300              :  * @brief Read an named enumerator value from a node.
     301              :  *
     302              :  * This macro will ensure the enumerator value is properly casted from the
     303              :  * corresponding integer stored in the pack.
     304              :  *
     305              :  * @param node Node to read from.
     306              :  * @param key Key of the node to read. Can't be NULL.
     307              :  * @param value Pointer to the enumerator value to read into.
     308              :  * @return 0 on success, or a negative error value on failure.
     309              :  */
     310              : #define bf_rpack_kv_enum(node, key, value)                                     \
     311              :     ({                                                                         \
     312              :         int __value;                                                           \
     313              :         int __r = bf_rpack_kv_int(node, key, &__value);                        \
     314              :         if (!__r)                                                              \
     315              :             *(value) = __value;                                                \
     316              :         __r;                                                                   \
     317              :     })
     318              : 
     319              : /**
     320              :  * @brief Read an enumerator value from a node.
     321              :  *
     322              :  * Similar to `bf_rpack_kv_enum` but reads a node directly.
     323              :  *
     324              :  * @param node Node to read from.
     325              :  * @param value Pointer to the enumerator value to read into.
     326              :  * @return 0 on success, or a negative error value on failure.
     327              :  */
     328              : #define bf_rpack_enum(node, value)                                             \
     329              :     ({                                                                         \
     330              :         int __value;                                                           \
     331              :         int __r = bf_rpack_int(node, &__value);                                \
     332              :         if (!__r)                                                              \
     333              :             *(value) = __value;                                                \
     334              :         __r;                                                                   \
     335              :     })
     336              : 
     337              : /**
     338              :  * @brief Log a missing key error and return a negative error value.
     339              :  *
     340              :  * @param v The error value to return.
     341              :  * @param key The missing key, as a string.
     342              :  */
     343              : #define bf_rpack_key_err(v, key) bf_err_r(v, "failed to read %s from pack", key)
     344              : 
     345              : /**
     346              :  * @brief Allocate and initialize a new `bf_rpack_t` object.
     347              :  *
     348              :  * @param pack `bf_rpack_t` object to allocate and initialize. Can't be NULL.
     349              :  * @param data Serialized data. To prevent large copies, the deserialization
     350              :  *        object won't take ownership of the data. Can't be NULL.
     351              :  * @param data_len Length of `data`.
     352              :  * @return 0 on success, or a negative error value on failure.
     353              :  */
     354              : int bf_rpack_new(bf_rpack_t **pack, const void *data, size_t data_len);
     355              : 
     356              : /**
     357              :  * @brief Deinitialize and deallocate a `bf_rpack_t` object.
     358              :  *
     359              :  * @param pack `bf_rpack_t` object to deallocate and deinitialize. Can't be
     360              :  *        NULL. If `*pack` is NULL, this function has no effect.
     361              :  */
     362              : void bf_rpack_free(bf_rpack_t **pack);
     363              : 
     364              : /**
     365              :  * @brief Get the root node of `pack`.
     366              :  *
     367              :  * @param pack `bf_rpack_t` object to get the root node from. Can't be NULL.
     368              :  * @return Root node for `pack`.
     369              :  */
     370              : bf_rpack_node_t bf_rpack_root(const bf_rpack_t *pack);
     371              : 
     372              : /**
     373              :  * @brief Returns true if the node is nil, false otherwise.
     374              :  *
     375              :  * Nil nodes are used to specific an absent value (e.g. optional `bf_hookopts`).
     376              :  *
     377              :  * @param node Node to check for nil.
     378              :  * @return True if `node` is nil, false otherwise.
     379              :  */
     380              : bool bf_rpack_is_nil(bf_rpack_node_t node);
     381              : 
     382              : /**
     383              :  * @brief Returns true if the node is an array, false otherwise.
     384              :  *
     385              :  * @param node Node to check for array type.
     386              :  * @return True if `node` is an array, false otherwise.
     387              :  */
     388              : bool bf_rpack_is_array(bf_rpack_node_t node);
     389              : 
     390              : /**
     391              :  * @brief Returns the number of elements in an array node.
     392              :  *
     393              :  * To ensure the returned value is relevant, the caller should ensure `node` is
     394              :  * an array.
     395              :  *
     396              :  * @param node Array node to get the number of elements of.
     397              :  * @return The number of elements in `node`, or 0 if `node` is not an array.
     398              :  */
     399              : size_t bf_rpack_array_count(bf_rpack_node_t node);
     400              : 
     401              : /**
     402              :  * @brief Get an array element by index.
     403              :  *
     404              :  * The caller is responsible for ensuring `node` is an array and `index` is a
     405              :  * valid index in `node`, otherwise the returned node will be invalid.
     406              :  *
     407              :  * @param node Array node to get elements from.
     408              :  * @param index Index of the node to get.
     409              :  * @return The node of the element as `index` in `node`. */
     410              : bf_rpack_node_t bf_rpack_array_value_at(bf_rpack_node_t node, size_t index);
     411              : 
     412              : /**
     413              :  * @brief Check if an object node contains a given key.
     414              :  *
     415              :  * The caller is responsible for ensuring `node` is a valid object node. If
     416              :  * `node` is invalid or the wrong type, false is returned.
     417              :  *
     418              :  * @param node Object node to check.
     419              :  * @param key Key to check in `node`. Can't be NULL.
     420              :  * @return True if `key` is a valid key in `node`, false otherwise. On error,
     421              :  *         false is returned.
     422              :  */
     423              : bool bf_rpack_kv_contains(bf_rpack_node_t node, const char *key);
     424              : 
     425              : int bf_rpack_int(bf_rpack_node_t node, int *value);
     426              : int bf_rpack_uint(bf_rpack_node_t node, unsigned int *value);
     427              : int bf_rpack_u8(bf_rpack_node_t node, uint8_t *value);
     428              : int bf_rpack_u16(bf_rpack_node_t node, uint16_t *value);
     429              : int bf_rpack_u32(bf_rpack_node_t node, uint32_t *value);
     430              : int bf_rpack_u64(bf_rpack_node_t node, uint64_t *value);
     431              : int bf_rpack_str(bf_rpack_node_t node, char **value);
     432              : int bf_rpack_bool(bf_rpack_node_t node, bool *value);
     433              : int bf_rpack_bin(bf_rpack_node_t node, const void **data, size_t *data_len);
     434              : 
     435              : int bf_rpack_kv_node(bf_rpack_node_t node, const char *key,
     436              :                      bf_rpack_node_t *child);
     437              : int bf_rpack_kv_int(bf_rpack_node_t node, const char *key, int *value);
     438              : int bf_rpack_kv_uint(bf_rpack_node_t node, const char *key,
     439              :                      unsigned int *value);
     440              : int bf_rpack_kv_u8(bf_rpack_node_t node, const char *key, uint8_t *value);
     441              : int bf_rpack_kv_u16(bf_rpack_node_t node, const char *key, uint16_t *value);
     442              : int bf_rpack_kv_u32(bf_rpack_node_t node, const char *key, uint32_t *value);
     443              : int bf_rpack_kv_u64(bf_rpack_node_t node, const char *key, uint64_t *value);
     444              : int bf_rpack_kv_str(bf_rpack_node_t node, const char *key, char **value);
     445              : int bf_rpack_kv_bool(bf_rpack_node_t node, const char *key, bool *value);
     446              : int bf_rpack_kv_bin(bf_rpack_node_t node, const char *key, const void **data,
     447              :                     size_t *data_len);
     448              : int bf_rpack_kv_obj(bf_rpack_node_t node, const char *key,
     449              :                     bf_rpack_node_t *child);
     450              : int bf_rpack_kv_array(bf_rpack_node_t node, const char *key,
     451              :                       bf_rpack_node_t *child);
        

Generated by: LCOV version 2.0-1