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