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 "cgen/printer.h"
7 :
8 : #include <errno.h>
9 : #include <stdlib.h>
10 : #include <string.h>
11 :
12 : #include <bpfilter/dump.h>
13 : #include <bpfilter/helper.h>
14 : #include <bpfilter/list.h>
15 : #include <bpfilter/logger.h>
16 : #include <bpfilter/pack.h>
17 :
18 : /**
19 : * @struct bf_printer_msg
20 : *
21 : * Represents a message to be printed by a generated BPF program.
22 : */
23 : struct bf_printer_msg
24 : {
25 : /// Offset of the message, in the concatenated messages string.
26 : size_t offset;
27 : /// Length of the message, including the nul termination character.
28 : size_t len;
29 : /// Message.
30 : const char *str;
31 : };
32 :
33 : /**
34 : * @struct bf_printer
35 : *
36 : * The printer context. It stores all the messages to be printed by any of the
37 : * generated BPF program, and the runtime data required to maintain the
38 : * log messages BPF map.
39 : */
40 : struct bf_printer
41 : {
42 : /// List of messages.
43 : bf_list msgs;
44 : };
45 :
46 : #define _free_bf_printer_msg_ __attribute__((__cleanup__(_bf_printer_msg_free)))
47 :
48 : static void _bf_printer_msg_free(struct bf_printer_msg **msg);
49 :
50 : /**
51 : * Allocate and initialise a new printer message.
52 : *
53 : * @param msg On success, points to the newly allocated and initialised
54 : * printer message. Can't be NULL.
55 : * @return 0 on success, or negative errno value on error.
56 : */
57 20 : static int _bf_printer_msg_new(struct bf_printer_msg **msg)
58 : {
59 19 : _free_bf_printer_msg_ struct bf_printer_msg *_msg = NULL;
60 :
61 20 : bf_assert(msg);
62 :
63 19 : _msg = calloc(1, sizeof(*_msg));
64 19 : if (!_msg)
65 : return -ENOMEM;
66 :
67 18 : *msg = TAKE_PTR(_msg);
68 :
69 18 : return 0;
70 : }
71 :
72 : /**
73 : * @brief Allocate and initialize a new printer message from serialized data.
74 : *
75 : * @param msg Printer message object to allocate and initialize from the
76 : * serialized data. The caller will own the object. On failure, `*msg`
77 : * is unchanged. Can't be NULL.
78 : * @param node Node containing the serialized message.
79 : * @return 0 on success, or a negative errno value on failure.
80 : */
81 8 : static int _bf_printer_msg_new_from_pack(struct bf_printer_msg **msg,
82 : bf_rpack_node_t node)
83 : {
84 8 : _free_bf_printer_msg_ struct bf_printer_msg *_msg = NULL;
85 : int r;
86 :
87 8 : bf_assert(msg);
88 :
89 8 : r = _bf_printer_msg_new(&_msg);
90 8 : if (r)
91 0 : return bf_err_r(r, "failed to create bf_printer_msg from pack");
92 :
93 8 : r = bf_rpack_kv_u64(node, "offset", &_msg->offset);
94 8 : if (r)
95 0 : return bf_rpack_key_err(r, "bf_printer_msg.offset");
96 :
97 8 : r = bf_rpack_kv_u64(node, "len", &_msg->len);
98 8 : if (r)
99 0 : return bf_rpack_key_err(r, "bf_printer_msg.len");
100 :
101 8 : r = bf_rpack_kv_str(node, "str", (char **)&_msg->str);
102 8 : if (r)
103 0 : return bf_rpack_key_err(r, "bf_printer_msg.str");
104 :
105 8 : *msg = TAKE_PTR(_msg);
106 :
107 8 : return 0;
108 : }
109 :
110 : /**
111 : * Deinitialise and deallocate a printer message.
112 : *
113 : * @param msg Printer message. Can't be NULL.
114 : */
115 64 : static void _bf_printer_msg_free(struct bf_printer_msg **msg)
116 : {
117 64 : bf_assert(msg);
118 :
119 64 : if (!*msg)
120 : return;
121 :
122 : // Compiler will complain if str is const
123 18 : free((char *)(*msg)->str);
124 :
125 18 : free(*msg);
126 18 : *msg = NULL;
127 : }
128 :
129 : /**
130 : * @brief Serialize a printer message.
131 : *
132 : * @param msg Printer message to serialize. Can't be NULL.
133 : * @param pack `bf_wpack_t` object to serialize the printer message into.
134 : * Can't be NULL.
135 : * @return 0 on success, or a negative error value on failure.
136 : */
137 8 : static int _bf_printer_msg_pack(const struct bf_printer_msg *msg,
138 : bf_wpack_t *pack)
139 : {
140 8 : bf_assert(msg);
141 8 : bf_assert(pack);
142 :
143 8 : bf_wpack_kv_u64(pack, "offset", msg->offset);
144 8 : bf_wpack_kv_u64(pack, "len", msg->len);
145 8 : bf_wpack_kv_str(pack, "str", msg->str);
146 :
147 8 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
148 : }
149 :
150 0 : static void _bf_printer_msg_dump(const struct bf_printer_msg *msg,
151 : prefix_t *prefix)
152 : {
153 0 : bf_assert(msg);
154 0 : bf_assert(prefix);
155 :
156 0 : DUMP(prefix, "struct bf_printer_msg at %p", msg);
157 :
158 0 : bf_dump_prefix_push(prefix);
159 0 : DUMP(prefix, "offset: %lu", msg->offset);
160 0 : DUMP(prefix, "len: %lu", msg->len);
161 0 : DUMP(bf_dump_prefix_last(prefix), "str: '%s'", msg->str);
162 0 : bf_dump_prefix_pop(prefix);
163 0 : }
164 :
165 0 : size_t bf_printer_msg_offset(const struct bf_printer_msg *msg)
166 : {
167 0 : bf_assert(msg);
168 0 : return msg->offset;
169 : }
170 :
171 0 : size_t bf_printer_msg_len(const struct bf_printer_msg *msg)
172 : {
173 0 : bf_assert(msg);
174 0 : return msg->len;
175 : }
176 :
177 9 : int bf_printer_new(struct bf_printer **printer)
178 : {
179 8 : _free_bf_printer_ struct bf_printer *_printer = NULL;
180 :
181 9 : bf_assert(printer);
182 :
183 8 : _printer = malloc(sizeof(*_printer));
184 8 : if (!_printer)
185 : return -ENOMEM;
186 :
187 7 : _printer->msgs =
188 : bf_list_default(_bf_printer_msg_free, _bf_printer_msg_pack);
189 :
190 7 : *printer = TAKE_PTR(_printer);
191 :
192 7 : return 0;
193 : }
194 :
195 1 : int bf_printer_new_from_pack(struct bf_printer **printer, bf_rpack_node_t node)
196 : {
197 1 : _free_bf_printer_ struct bf_printer *_printer = NULL;
198 : bf_rpack_node_t child, m_node;
199 : int r;
200 :
201 1 : bf_assert(printer);
202 :
203 1 : r = bf_printer_new(&_printer);
204 1 : if (r)
205 0 : return bf_err_r(r, "failed to create a bf_printer from pack");
206 :
207 1 : r = bf_rpack_kv_array(node, "msgs", &child);
208 1 : if (r)
209 0 : return bf_rpack_key_err(r, "bf_printer.msgs");
210 18 : bf_rpack_array_foreach (child, m_node) {
211 16 : _free_bf_printer_msg_ struct bf_printer_msg *msg = NULL;
212 :
213 8 : r = bf_list_emplace(&_printer->msgs, _bf_printer_msg_new_from_pack, msg,
214 : m_node);
215 : if (r) {
216 0 : return bf_err_r(
217 : r, "failed to unpack bf_printer_msg into bf_printer.msgs");
218 : }
219 : }
220 :
221 1 : *printer = TAKE_PTR(_printer);
222 :
223 1 : return 0;
224 : }
225 :
226 18 : void bf_printer_free(struct bf_printer **printer)
227 : {
228 18 : bf_assert(printer);
229 :
230 18 : if (!*printer)
231 : return;
232 :
233 7 : bf_list_clean(&(*printer)->msgs);
234 :
235 7 : free(*printer);
236 7 : *printer = NULL;
237 : }
238 :
239 3 : int bf_printer_pack(const struct bf_printer *printer, bf_wpack_t *pack)
240 : {
241 3 : bf_assert(printer);
242 2 : bf_assert(pack);
243 :
244 1 : bf_wpack_kv_list(pack, "msgs", &printer->msgs);
245 :
246 1 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
247 : }
248 :
249 0 : void bf_printer_dump(const struct bf_printer *printer, prefix_t *prefix)
250 : {
251 0 : bf_assert(printer);
252 0 : bf_assert(prefix);
253 :
254 0 : DUMP(prefix, "struct bf_printer at %p", printer);
255 :
256 0 : bf_dump_prefix_push(prefix);
257 0 : DUMP(prefix, "msgs: bf_list<bf_printer_msg>[%lu]",
258 : bf_list_size(&printer->msgs));
259 0 : bf_dump_prefix_push(prefix);
260 0 : bf_list_foreach (&printer->msgs, msg_node) {
261 0 : struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
262 :
263 0 : if (bf_list_is_tail(&printer->msgs, msg_node))
264 0 : bf_dump_prefix_last(prefix);
265 :
266 0 : _bf_printer_msg_dump(msg, prefix);
267 : }
268 0 : bf_dump_prefix_pop(prefix);
269 :
270 0 : bf_dump_prefix_pop(prefix);
271 0 : }
272 :
273 : /**
274 : * Get the total size of the concatenated messages.
275 : *
276 : * @param printer Printer context. Can't be NULL.
277 : * @return Total size of the concatenated messages.
278 : */
279 8 : static size_t _bf_printer_total_size(const struct bf_printer *printer)
280 : {
281 : bf_list_node *last_msg_node;
282 : struct bf_printer_msg *last_msg;
283 :
284 8 : bf_assert(printer);
285 :
286 8 : if (!(last_msg_node = bf_list_get_tail(&printer->msgs)))
287 : return 0;
288 :
289 7 : last_msg = bf_list_node_get_data(last_msg_node);
290 :
291 7 : return last_msg->offset + last_msg->len;
292 : }
293 :
294 10 : const struct bf_printer_msg *bf_printer_add_msg(struct bf_printer *printer,
295 : const char *str)
296 : {
297 10 : _free_bf_printer_msg_ struct bf_printer_msg *msg = NULL;
298 : int r;
299 :
300 10 : bf_assert(printer);
301 10 : bf_assert(str);
302 :
303 : // Check if an identical message is already stored in the context.
304 78 : bf_list_foreach (&printer->msgs, msg_node) {
305 31 : struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
306 :
307 31 : if (bf_streq(msg->str, str))
308 : return msg;
309 : }
310 :
311 : // Otherwise, create a new message.
312 8 : r = _bf_printer_msg_new(&msg);
313 8 : if (r) {
314 0 : bf_err("failed to create a new bf_printer_msg object");
315 0 : return NULL;
316 : }
317 :
318 8 : msg->len = strlen(str) + 1;
319 : // Next expected offset is equal to the current total size of the
320 : // concatenated string
321 8 : msg->offset = _bf_printer_total_size(printer);
322 8 : msg->str = strdup(str);
323 8 : if (!msg->str)
324 : return NULL;
325 :
326 8 : r = bf_list_add_tail(&printer->msgs, msg);
327 8 : if (r) {
328 0 : bf_err("failed to add a new printer message to the printer context");
329 0 : return NULL;
330 : }
331 :
332 8 : return TAKE_PTR(msg);
333 : }
334 :
335 0 : int bf_printer_assemble(const struct bf_printer *printer, void **str,
336 : size_t *str_len)
337 : {
338 : _cleanup_free_ char *_str = NULL;
339 : size_t _str_len;
340 :
341 0 : bf_assert(printer);
342 0 : bf_assert(str);
343 0 : bf_assert(str_len);
344 :
345 0 : _str_len = _bf_printer_total_size(printer);
346 :
347 : // If the printer doesn't contain any message, the string should only
348 : // contain \0.
349 0 : if (_str_len == 0) {
350 0 : _str = malloc(1);
351 0 : if (!_str)
352 : return -ENOMEM;
353 :
354 0 : *_str = '\0';
355 : _str_len = 1;
356 : } else {
357 0 : _str_len = _bf_printer_total_size(printer);
358 0 : _str = malloc(_str_len);
359 0 : if (!_str)
360 : return -ENOMEM;
361 :
362 0 : bf_list_foreach (&printer->msgs, msg_node) {
363 0 : struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
364 0 : memcpy(_str + msg->offset, msg->str, msg->len);
365 : }
366 : }
367 :
368 0 : *str = TAKE_PTR(_str);
369 0 : *str_len = _str_len;
370 :
371 0 : return 0;
372 : }
|