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