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);
|