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