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-30 16:37:25 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              :  * @todo Create dedicated functions to pack and unpack a `size_t` and `ssize_t`.
      83              :  * @todo Functions that write to the pack object should be more verbose: even
      84              :  * if they don't return an error code, they should log a warning when a packing
      85              :  * operation fails.
      86              :  */
      87              : 
      88              : struct bf_wpack;
      89              : typedef struct bf_wpack bf_wpack_t;
      90              : 
      91              : struct bf_list;
      92              : typedef struct bf_list bf_list;
      93              : 
      94              : /// @brief Cleanup attribute for dynamically allocated `bf_wpack_t` objects.
      95              : #define _free_bf_wpack_ __attribute__((cleanup(bf_wpack_free)))
      96              : 
      97              : /**
      98              :  * @brief Allocate and initialize a new `bf_wpack_t` object.
      99              :  *
     100              :  * @param pack `bf_wpack_t` object to allocate and initialize. Can't be NULL.
     101              :  * @return 0 on sucess, or a negative error value on failure.
     102              :  */
     103              : int bf_wpack_new(bf_wpack_t **pack);
     104              : 
     105              : /**
     106              :  * @brief Deinitialize and deallocate a `bf_wpack_t` object.
     107              :  *
     108              :  * @param pack `bf_wpack_t` object to deallocate and deinitialize. Can't be
     109              :  *        NULL. If `*pack` is NULL, this function has no effect.
     110              :  */
     111              : void bf_wpack_free(bf_wpack_t **pack);
     112              : 
     113              : /**
     114              :  * @brief Check if the serialized data is valid.
     115              :  *
     116              :  * @param pack `bf_wpack_t` object to check. Can't be NULL.
     117              :  * @return True if the serialized data is valid, false otherwise.
     118              :  */
     119              : bool bf_wpack_is_valid(bf_wpack_t *pack);
     120              : 
     121              : /**
     122              :  * @brief Get the serialized data.
     123              :  *
     124              :  * Ensure the buffer is flushed and all the remaining data has been serialized.
     125              :  * On success, `*data` points to a buffer containing `data_len` bytes. The
     126              :  * caller do not own the data.
     127              :  *
     128              :  * @return 0 on success, or a negative error value on failure.
     129              :  */
     130              : int bf_wpack_get_data(bf_wpack_t *pack, const void **data, size_t *data_len);
     131              : 
     132              : void bf_wpack_nil(bf_wpack_t *pack);
     133              : void bf_wpack_int(bf_wpack_t *pack, int value);
     134              : void bf_wpack_uint(bf_wpack_t *pack, unsigned int value);
     135              : void bf_wpack_u8(bf_wpack_t *pack, uint8_t value);
     136              : void bf_wpack_u16(bf_wpack_t *pack, uint16_t value);
     137              : void bf_wpack_u32(bf_wpack_t *pack, uint32_t value);
     138              : void bf_wpack_u64(bf_wpack_t *pack, uint64_t value);
     139              : void bf_wpack_bool(bf_wpack_t *pack, bool value);
     140              : void bf_wpack_str(bf_wpack_t *pack, const char *value);
     141              : void bf_wpack_bin(bf_wpack_t *pack, const void *value, size_t len);
     142              : void bf_wpack_list(bf_wpack_t *pack, const bf_list *value);
     143              : #define bf_wpack_enum(pack, value) bf_wpack_int((pack), (value))
     144              : 
     145              : static inline void bf_wpack_kv_nil(bf_wpack_t *pack, const char *key)
     146              : {
     147            2 :     bf_wpack_str(pack, key);
     148            2 :     bf_wpack_nil(pack);
     149            2 : }
     150              : 
     151              : static inline void bf_wpack_kv_int(bf_wpack_t *pack, const char *key, int value)
     152              : {
     153           51 :     bf_wpack_str(pack, key);
     154           51 :     bf_wpack_int(pack, value);
     155            0 : }
     156              : 
     157              : static inline void bf_wpack_kv_uint(bf_wpack_t *pack, const char *key,
     158              :                                     unsigned int value)
     159              : {
     160            0 :     bf_wpack_str(pack, key);
     161            0 :     bf_wpack_uint(pack, value);
     162            0 : }
     163              : 
     164              : static inline void bf_wpack_kv_u8(bf_wpack_t *pack, const char *key,
     165              :                                   uint8_t value)
     166              : {
     167            5 :     bf_wpack_str(pack, key);
     168            5 :     bf_wpack_u8(pack, value);
     169              : }
     170              : 
     171              : static inline void bf_wpack_kv_u16(bf_wpack_t *pack, const char *key,
     172              :                                    uint16_t value)
     173              : {
     174              :     bf_wpack_str(pack, key);
     175              :     bf_wpack_u16(pack, value);
     176              : }
     177              : 
     178              : static inline void bf_wpack_kv_u32(bf_wpack_t *pack, const char *key,
     179              :                                    uint32_t value)
     180              : {
     181            5 :     bf_wpack_str(pack, key);
     182            5 :     bf_wpack_u32(pack, value);
     183              : }
     184              : 
     185              : static inline void bf_wpack_kv_u64(bf_wpack_t *pack, const char *key,
     186              :                                    uint64_t value)
     187              : {
     188           24 :     bf_wpack_str(pack, key);
     189           24 :     bf_wpack_u64(pack, value);
     190              : }
     191              : 
     192              : static inline void bf_wpack_kv_bool(bf_wpack_t *pack, const char *key,
     193              :                                     bool value)
     194              : {
     195            5 :     bf_wpack_str(pack, key);
     196            5 :     bf_wpack_bool(pack, value);
     197              : }
     198              : 
     199              : static inline void bf_wpack_kv_str(bf_wpack_t *pack, const char *key,
     200              :                                    const char *str)
     201              : {
     202           11 :     bf_wpack_str(pack, key);
     203           11 :     bf_wpack_str(pack, str);
     204            0 : }
     205              : 
     206              : static inline void bf_wpack_kv_bin(bf_wpack_t *pack, const char *key,
     207              :                                    const void *data, size_t data_len)
     208              : {
     209           23 :     bf_wpack_str(pack, key);
     210           23 :     bf_wpack_bin(pack, data, data_len);
     211              : }
     212              : 
     213              : static inline void bf_wpack_kv_enum(bf_wpack_t *pack, const char *key,
     214              :                                     int value)
     215              : {
     216            7 :     bf_wpack_str(pack, key);
     217            7 :     bf_wpack_enum(pack, value);
     218              : }
     219              : 
     220              : void bf_wpack_kv_list(bf_wpack_t *pack, const char *key, const bf_list *value);
     221              : 
     222              : /**
     223              :  * @brief Open a new object in the pack.
     224              :  *
     225              :  * Once this function returns, any call to `bf_wpack_TYPE` or `bf_wpack_kv_TYPE`
     226              :  * will insert data into the object.
     227              :  *
     228              :  * @param pack `bf_wpack_t` object to open the object in. Can't be NULL.
     229              :  * @param key Key to use for the object. If NULL, no key is inserted.
     230              :  */
     231              : void bf_wpack_open_object(bf_wpack_t *pack, const char *key);
     232              : 
     233              : /**
     234              :  * @brief Close the current object.
     235              :  *
     236              :  * Once the object is closed, any call to `bf_wpack_TYPE` or `bf_wpack_kv_TYPE`
     237              :  * will insert data into the parent object or array.
     238              :  *
     239              :  * @param pack `bf_wpack_t` object to close the object in. Can't be NULL.
     240              :  */
     241              : void bf_wpack_close_object(bf_wpack_t *pack);
     242              : 
     243              : /**
     244              :  * @brief Open a new array in the pack.
     245              :  *
     246              :  * Once this function returns, any call to `bf_wpack_TYPE` or `bf_wpack_kv_TYPE`
     247              :  * will insert data into the array.
     248              :  *
     249              :  * @param pack `bf_wpack_t` object to open the array in. Can't be NULL.
     250              :  * @param key Key to use for the array. If NULL, no key is inserted.
     251              :  */
     252              : void bf_wpack_open_array(bf_wpack_t *pack, const char *key);
     253              : 
     254              : /**
     255              :  * @brief Close the current array.
     256              :  *
     257              :  * Once the array is closed, any call to `bf_wpack_TYPE` or `bf_wpack_kv_TYPE`
     258              :  * will insert data into the parent object or array.
     259              :  *
     260              :  * @param pack `bf_wpack_t` object to close the array in. Can't be NULL.
     261              :  */
     262              : void bf_wpack_close_array(bf_wpack_t *pack);
     263              : 
     264              : struct bf_rpack;
     265              : typedef struct bf_rpack bf_rpack_t;
     266              : 
     267              : /**
     268              :  * @brief Opaque structure to pass nodes by value.
     269              :  */
     270              : typedef union
     271              : {
     272              :     char _opaque[16];
     273              :     void *_align;
     274              : } bf_rpack_node_t;
     275              : 
     276              : /// @brief Cleanup attribute for dynamically allocated `bf_rpack_t` objects.
     277              : #define _free_bf_rpack_ __attribute__((cleanup(bf_rpack_free)))
     278              : 
     279              : /**
     280              :  * @brief Loop over all the nodes in an array node.
     281              :  *
     282              :  * The counter `i` is accessible within the scope.
     283              :  *
     284              :  * @warning This loop must ensure we don't query the node for invalid data:
     285              :  * we should not request a node from the array if it's empty, of if the index
     286              :  * is out of the array. Otherwise, it would put the `bf_rpack_t` object in
     287              :  * failure mode.
     288              :  *
     289              :  * @param node Array node. If the node is not an array, the loop will be
     290              :  *        ignored.
     291              :  * @param value_node A node in the array, updated on each iteration.
     292              :  */
     293              : #define bf_rpack_array_foreach(node, value_node)                               \
     294              :     for (size_t i = ((bf_rpack_array_count(node) ?                             \
     295              :                           ((value_node) = bf_rpack_array_value_at(node, 0)) :  \
     296              :                           (bf_rpack_node_t) {}),                               \
     297              :                      0);                                                       \
     298              :          i < bf_rpack_array_count(node);                                       \
     299              :          ++i, (value_node) = (i < bf_rpack_array_count(node) ?                 \
     300              :                                   bf_rpack_array_value_at(node, i) :           \
     301              :                                   (bf_rpack_node_t) {}))
     302              : 
     303              : /**
     304              :  * @brief Read an named enumerator value from a node.
     305              :  *
     306              :  * This macro will ensure the enumerator value is properly casted from the
     307              :  * corresponding integer stored in the pack.
     308              :  *
     309              :  * @param node Node to read from.
     310              :  * @param key Key of the node to read. Can't be NULL.
     311              :  * @param value Pointer to the enumerator value to read into.
     312              :  * @return 0 on success, or a negative error value on failure.
     313              :  */
     314              : #define bf_rpack_kv_enum(node, key, value)                                     \
     315              :     ({                                                                         \
     316              :         int __value;                                                           \
     317              :         int __r = bf_rpack_kv_int(node, key, &__value);                        \
     318              :         if (!__r)                                                              \
     319              :             *(value) = __value;                                                \
     320              :         __r;                                                                   \
     321              :     })
     322              : 
     323              : /**
     324              :  * @brief Read an enumerator value from a node.
     325              :  *
     326              :  * Similar to `bf_rpack_kv_enum` but reads a node directly.
     327              :  *
     328              :  * @param node Node to read from.
     329              :  * @param value Pointer to the enumerator value to read into.
     330              :  * @return 0 on success, or a negative error value on failure.
     331              :  */
     332              : #define bf_rpack_enum(node, value)                                             \
     333              :     ({                                                                         \
     334              :         int __value;                                                           \
     335              :         int __r = bf_rpack_int(node, &__value);                                \
     336              :         if (!__r)                                                              \
     337              :             *(value) = __value;                                                \
     338              :         __r;                                                                   \
     339              :     })
     340              : 
     341              : /**
     342              :  * @brief Log a missing key error and return a negative error value.
     343              :  *
     344              :  * @param v The error value to return.
     345              :  * @param key The missing key, as a string.
     346              :  */
     347              : #define bf_rpack_key_err(v, key) bf_err_r(v, "failed to read %s from pack", key)
     348              : 
     349              : /**
     350              :  * @brief Allocate and initialize a new `bf_rpack_t` object.
     351              :  *
     352              :  * @param pack `bf_rpack_t` object to allocate and initialize. Can't be NULL.
     353              :  * @param data Serialized data. To prevent large copies, the deserialization
     354              :  *        object won't take ownership of the data. Can't be NULL.
     355              :  * @param data_len Length of `data`.
     356              :  * @return 0 on success, or a negative error value on failure.
     357              :  */
     358              : int bf_rpack_new(bf_rpack_t **pack, const void *data, size_t data_len);
     359              : 
     360              : /**
     361              :  * @brief Deinitialize and deallocate a `bf_rpack_t` object.
     362              :  *
     363              :  * @param pack `bf_rpack_t` object to deallocate and deinitialize. Can't be
     364              :  *        NULL. If `*pack` is NULL, this function has no effect.
     365              :  */
     366              : void bf_rpack_free(bf_rpack_t **pack);
     367              : 
     368              : /**
     369              :  * @brief Get the root node of `pack`.
     370              :  *
     371              :  * @param pack `bf_rpack_t` object to get the root node from. Can't be NULL.
     372              :  * @return Root node for `pack`.
     373              :  */
     374              : bf_rpack_node_t bf_rpack_root(const bf_rpack_t *pack);
     375              : 
     376              : /**
     377              :  * @brief Returns true if the node is nil, false otherwise.
     378              :  *
     379              :  * Nil nodes are used to specific an absent value (e.g. optional `bf_hookopts`).
     380              :  *
     381              :  * @param node Node to check for nil.
     382              :  * @return True if `node` is nil, false otherwise.
     383              :  */
     384              : bool bf_rpack_is_nil(bf_rpack_node_t node);
     385              : 
     386              : /**
     387              :  * @brief Returns true if the node is an array, false otherwise.
     388              :  *
     389              :  * @param node Node to check for array type.
     390              :  * @return True if `node` is an array, false otherwise.
     391              :  */
     392              : bool bf_rpack_is_array(bf_rpack_node_t node);
     393              : 
     394              : /**
     395              :  * @brief Returns the number of elements in an array node.
     396              :  *
     397              :  * To ensure the returned value is relevant, the caller should ensure `node` is
     398              :  * an array.
     399              :  *
     400              :  * @param node Array node to get the number of elements of.
     401              :  * @return The number of elements in `node`, or 0 if `node` is not an array.
     402              :  */
     403              : size_t bf_rpack_array_count(bf_rpack_node_t node);
     404              : 
     405              : /**
     406              :  * @brief Get an array element by index.
     407              :  *
     408              :  * The caller is responsible for ensuring `node` is an array and `index` is a
     409              :  * valid index in `node`, otherwise the returned node will be invalid.
     410              :  *
     411              :  * @param node Array node to get elements from.
     412              :  * @param index Index of the node to get.
     413              :  * @return The node of the element as `index` in `node`. */
     414              : bf_rpack_node_t bf_rpack_array_value_at(bf_rpack_node_t node, size_t index);
     415              : 
     416              : /**
     417              :  * @brief Check if an object node contains a given key.
     418              :  *
     419              :  * The caller is responsible for ensuring `node` is a valid object node. If
     420              :  * `node` is invalid or the wrong type, false is returned.
     421              :  *
     422              :  * @param node Object node to check.
     423              :  * @param key Key to check in `node`. Can't be NULL.
     424              :  * @return True if `key` is a valid key in `node`, false otherwise. On error,
     425              :  *         false is returned.
     426              :  */
     427              : bool bf_rpack_kv_contains(bf_rpack_node_t node, const char *key);
     428              : 
     429              : int bf_rpack_int(bf_rpack_node_t node, int *value);
     430              : int bf_rpack_uint(bf_rpack_node_t node, unsigned int *value);
     431              : int bf_rpack_u8(bf_rpack_node_t node, uint8_t *value);
     432              : int bf_rpack_u16(bf_rpack_node_t node, uint16_t *value);
     433              : int bf_rpack_u32(bf_rpack_node_t node, uint32_t *value);
     434              : int bf_rpack_u64(bf_rpack_node_t node, uint64_t *value);
     435              : int bf_rpack_str(bf_rpack_node_t node, char **value);
     436              : int bf_rpack_bool(bf_rpack_node_t node, bool *value);
     437              : int bf_rpack_bin(bf_rpack_node_t node, const void **data, size_t *data_len);
     438              : 
     439              : int bf_rpack_kv_node(bf_rpack_node_t node, const char *key,
     440              :                      bf_rpack_node_t *child);
     441              : int bf_rpack_kv_int(bf_rpack_node_t node, const char *key, int *value);
     442              : int bf_rpack_kv_uint(bf_rpack_node_t node, const char *key,
     443              :                      unsigned int *value);
     444              : int bf_rpack_kv_u8(bf_rpack_node_t node, const char *key, uint8_t *value);
     445              : int bf_rpack_kv_u16(bf_rpack_node_t node, const char *key, uint16_t *value);
     446              : int bf_rpack_kv_u32(bf_rpack_node_t node, const char *key, uint32_t *value);
     447              : int bf_rpack_kv_u64(bf_rpack_node_t node, const char *key, uint64_t *value);
     448              : int bf_rpack_kv_str(bf_rpack_node_t node, const char *key, char **value);
     449              : int bf_rpack_kv_bool(bf_rpack_node_t node, const char *key, bool *value);
     450              : int bf_rpack_kv_bin(bf_rpack_node_t node, const char *key, const void **data,
     451              :                     size_t *data_len);
     452              : int bf_rpack_kv_obj(bf_rpack_node_t node, const char *key,
     453              :                     bf_rpack_node_t *child);
     454              : int bf_rpack_kv_array(bf_rpack_node_t node, const char *key,
     455              :                       bf_rpack_node_t *child);
        

Generated by: LCOV version 2.0-1