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 19 : 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 13 : _free_bf_map_ struct bf_map *_map = NULL;
30 :
31 19 : bf_assert(map && name);
32 17 : bf_assert(name[0] != '\0');
33 19 : bf_assert(key_size > 0 && value_size > 0 && n_elems > 0);
34 :
35 13 : _map = malloc(sizeof(*_map));
36 13 : if (!_map)
37 : return -ENOMEM;
38 :
39 13 : _map->type = type;
40 13 : _map->fd = -1;
41 13 : _map->bpf_type = bpf_type;
42 13 : _map->key_size = key_size;
43 13 : _map->value_size = value_size;
44 13 : _map->n_elems = n_elems;
45 :
46 13 : bf_strncpy(_map->name, BPF_OBJ_NAME_LEN, name);
47 :
48 13 : *map = TAKE_PTR(_map);
49 :
50 13 : 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 : _free_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 31 : void bf_map_free(struct bf_map **map)
106 : {
107 31 : bf_assert(map);
108 :
109 30 : if (!*map)
110 : return;
111 :
112 14 : 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 : _free_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 0 : 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 0 : bf_assert(0 <= type && type < _BF_MAP_TYPE_MAX);
169 :
170 0 : 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 : [BF_MAP_BPF_TYPE_LPM_TRIE] = BPF_MAP_TYPE_LPM_TRIE,
205 : };
206 :
207 4 : bf_assert(0 <= bpf_type && bpf_type < _BF_MAP_BPF_TYPE_MAX);
208 :
209 2 : return _kernel_types[bpf_type];
210 : }
211 :
212 : #define _free_bf_btf_ __attribute__((__cleanup__(_bf_btf_free)))
213 :
214 : struct bf_btf
215 : {
216 : struct btf *btf;
217 : uint32_t key_type_id;
218 : uint32_t value_type_id;
219 : int fd;
220 : };
221 :
222 : static void _bf_btf_free(struct bf_btf **btf);
223 :
224 6 : static int _bf_btf_new(struct bf_btf **btf)
225 : {
226 5 : _free_bf_btf_ struct bf_btf *_btf = NULL;
227 :
228 6 : bf_assert(btf);
229 :
230 5 : _btf = malloc(sizeof(struct bf_btf));
231 5 : if (!_btf)
232 : return -ENOMEM;
233 :
234 5 : _btf->fd = -1;
235 :
236 5 : _btf->btf = btf__new_empty();
237 5 : if (!_btf->btf)
238 0 : return -errno;
239 :
240 5 : *btf = TAKE_PTR(_btf);
241 :
242 5 : return 0;
243 : }
244 :
245 16 : static void _bf_btf_free(struct bf_btf **btf)
246 : {
247 16 : bf_assert(btf);
248 :
249 15 : if (!*btf)
250 : return;
251 :
252 5 : btf__free((*btf)->btf);
253 5 : closep(&(*btf)->fd);
254 : freep((void *)btf);
255 : }
256 :
257 0 : static int _bf_btf_load(struct bf_btf *btf)
258 : {
259 0 : union bpf_attr attr = {};
260 : int token_fd;
261 : const void *raw;
262 : int r;
263 :
264 0 : bf_assert(btf);
265 :
266 0 : raw = btf__raw_data(btf->btf, &attr.btf_size);
267 0 : if (!raw)
268 0 : return bf_err_r(errno, "failed to request BTF raw data");
269 :
270 0 : attr.btf = bf_ptr_to_u64(raw);
271 :
272 0 : token_fd = bf_ctx_token();
273 0 : if (token_fd != -1) {
274 0 : attr.btf_token_fd = token_fd;
275 0 : attr.btf_flags |= BPF_F_TOKEN_FD;
276 : }
277 :
278 0 : r = bf_bpf(BPF_BTF_LOAD, &attr);
279 0 : if (r < 0)
280 0 : return bf_err_r(r, "failed to load BTF data");
281 :
282 0 : btf->fd = r;
283 :
284 0 : return 0;
285 : }
286 :
287 : /**
288 : * Create the BTF data for the map.
289 : *
290 : * @param map Map to create the BTF data for. @c map.type will define the
291 : * exact content of the BTF object. Can't be NULL.
292 : * @return A @ref bf_btf structure on success, or NULL on error. The
293 : * @ref bf_btf structure is owned by the caller.
294 : */
295 2 : static struct bf_btf *_bf_map_make_btf(const struct bf_map *map)
296 : {
297 2 : _free_bf_btf_ struct bf_btf *btf = NULL;
298 : struct btf *kbtf;
299 : int r;
300 :
301 2 : bf_assert(map);
302 :
303 2 : r = _bf_btf_new(&btf);
304 2 : if (r < 0)
305 : return NULL;
306 :
307 2 : kbtf = btf->btf;
308 :
309 2 : switch (map->type) {
310 0 : case BF_MAP_TYPE_COUNTERS:
311 0 : btf__add_int(kbtf, "u64", 8, 0);
312 0 : btf->key_type_id = btf__add_int(kbtf, "u32", 4, 0);
313 0 : btf->value_type_id = btf__add_struct(kbtf, "bf_counters", 16);
314 0 : btf__add_field(kbtf, "packets", 1, 0, 0);
315 0 : btf__add_field(kbtf, "bytes", 1, 64, 0);
316 : break;
317 : case BF_MAP_TYPE_PRINTER:
318 : case BF_MAP_TYPE_SET:
319 : // No BTF data available for this map types
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 : _free_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 : // NO_PREALLOC is *required* for LPM_TRIE map
369 2 : if (map->bpf_type == BF_MAP_BPF_TYPE_LPM_TRIE)
370 0 : attr.map_flags |= BPF_F_NO_PREALLOC;
371 :
372 2 : if ((token_fd = bf_ctx_token()) != -1) {
373 0 : attr.map_token_fd = token_fd;
374 0 : attr.map_flags |= BPF_F_TOKEN_FD;
375 : }
376 :
377 : /** The BTF data is not mandatory to use the map, but a good addition.
378 : * Hence, bpfilter will try to make the BTF data available, but will
379 : * ignore if that fails. @ref _bf_map_make_btf is used to isolate the
380 : * BTF data generation: if it fails we ignore the issue, but if it
381 : * succeeds we update the @c bpf_attr structure with the BTF details.
382 : * There is some boilerplate for @ref bf_btf structure, it could be
383 : * simpler, but the current implementation ensure the BTF data is properly
384 : * freed on error, without preventing the BPF map to be created. */
385 2 : btf = _bf_map_make_btf(map);
386 2 : if (btf) {
387 0 : attr.btf_fd = btf->fd;
388 0 : attr.btf_key_type_id = btf->key_type_id;
389 0 : attr.btf_value_type_id = btf->value_type_id;
390 : }
391 :
392 2 : (void)snprintf(attr.map_name, BPF_OBJ_NAME_LEN, "%s", map->name);
393 :
394 2 : r = bf_bpf(BPF_MAP_CREATE, &attr);
395 2 : if (r < 0)
396 1 : return bf_err_r(r, "failed to create BPF map '%s'", map->name);
397 :
398 1 : map->fd = r;
399 :
400 1 : return 0;
401 : }
402 :
403 1 : void bf_map_destroy(struct bf_map *map)
404 : {
405 1 : bf_assert(map);
406 :
407 0 : closep(&map->fd);
408 0 : }
409 :
410 0 : int bf_map_pin(const struct bf_map *map, int dir_fd)
411 : {
412 : int r;
413 :
414 0 : bf_assert(map);
415 :
416 0 : r = bf_bpf_obj_pin(map->name, map->fd, dir_fd);
417 0 : if (r < 0)
418 0 : return bf_err_r(r, "failed to pin BPF map '%s'", map->name);
419 :
420 : return 0;
421 : }
422 :
423 0 : void bf_map_unpin(const struct bf_map *map, int dir_fd)
424 : {
425 : int r;
426 :
427 0 : bf_assert(map);
428 :
429 0 : r = unlinkat(dir_fd, map->name, 0);
430 0 : if (r < 0 && errno != ENOENT) {
431 : // Do not warn on ENOENT, we want the file to be gone!
432 0 : bf_warn_r(
433 : errno,
434 : "failed to unlink BPF map '%s', assuming the map is not pinned",
435 : map->name);
436 : }
437 0 : }
438 :
439 0 : int bf_map_set_key_size(struct bf_map *map, size_t key_size)
440 : {
441 0 : bf_assert(key_size != 0);
442 :
443 0 : if (map->fd != -1) {
444 0 : return bf_err_r(
445 : -EPERM,
446 : "can't change the size of the map key once it has been created");
447 : }
448 :
449 0 : map->key_size = key_size;
450 :
451 0 : return 0;
452 : }
453 :
454 0 : int bf_map_set_value_size(struct bf_map *map, size_t value_size)
455 : {
456 0 : bf_assert(value_size != 0);
457 :
458 0 : if (map->fd != -1) {
459 0 : return bf_err_r(
460 : -EPERM,
461 : "can't change the size of the map value once it has been created");
462 : }
463 :
464 0 : map->value_size = value_size;
465 :
466 0 : return 0;
467 : }
468 :
469 2 : int bf_map_set_n_elems(struct bf_map *map, size_t n_elems)
470 : {
471 2 : bf_assert(n_elems != 0);
472 :
473 2 : if (map->fd != -1) {
474 1 : return bf_err_r(
475 : -EPERM,
476 : "can't change the number of elements in a map once the map has been created");
477 : }
478 :
479 1 : map->n_elems = n_elems;
480 :
481 1 : return 0;
482 : }
483 :
484 3 : int bf_map_set_elem(const struct bf_map *map, void *key, void *value)
485 : {
486 3 : union bpf_attr attr = {};
487 :
488 6 : bf_assert(map && key && value);
489 :
490 0 : attr.map_fd = map->fd;
491 0 : attr.key = (unsigned long long)key;
492 0 : attr.value = (unsigned long long)value;
493 0 : attr.flags = BPF_ANY;
494 :
495 0 : return bf_bpf(BPF_MAP_UPDATE_ELEM, &attr);
496 : }
497 :
498 : static const char *_bf_map_bpf_type_strs[] = {
499 : [BF_MAP_BPF_TYPE_ARRAY] = "BF_MAP_BPF_TYPE_ARRAY",
500 : [BF_MAP_BPF_TYPE_HASH] = "BF_MAP_BPF_TYPE_HASH",
501 : [BF_MAP_BPF_TYPE_LPM_TRIE] = "BF_MAP_BPF_TYPE_LPM_TRIE",
502 : };
503 :
504 : static_assert(ARRAY_SIZE(_bf_map_bpf_type_strs) == _BF_MAP_BPF_TYPE_MAX,
505 : "missing entries in _bf_map_bpf_type_strs array");
506 :
507 5 : const char *bf_map_bpf_type_to_str(enum bf_map_bpf_type bpf_type)
508 : {
509 5 : bf_assert(0 <= bpf_type && bpf_type < _BF_MAP_BPF_TYPE_MAX);
510 :
511 3 : return _bf_map_bpf_type_strs[bpf_type];
512 : }
513 :
514 6 : int bf_map_bpf_type_from_str(const char *str, enum bf_map_bpf_type *bpf_type)
515 : {
516 6 : bf_assert(str);
517 5 : bf_assert(bpf_type);
518 :
519 10 : for (size_t i = 0; i < _BF_MAP_BPF_TYPE_MAX; ++i) {
520 9 : if (bf_streq(_bf_map_bpf_type_strs[i], str)) {
521 3 : *bpf_type = i;
522 3 : return 0;
523 : }
524 : }
525 :
526 : return -EINVAL;
527 : }
|