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 : _free_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 : _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 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 : _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 : };
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 _free_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 : _free_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 : _free_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 : case BF_MAP_TYPE_PRINTER:
317 : case BF_MAP_TYPE_SET:
318 : // No BTF data available for this map types
319 : return NULL;
320 0 : default:
321 0 : bf_err_r(-ENOTSUP, "bf_map type %d is not supported", map->type);
322 : return NULL;
323 : }
324 :
325 0 : r = _bf_btf_load(btf);
326 0 : if (r) {
327 0 : bf_err_r(r, "failed to load BTF data");
328 0 : return NULL;
329 : }
330 :
331 0 : return TAKE_PTR(btf);
332 : }
333 :
334 4 : int bf_map_create(struct bf_map *map, uint32_t flags)
335 : {
336 : int token_fd;
337 4 : union bpf_attr attr = {};
338 3 : _free_bf_btf_ struct bf_btf *btf = NULL;
339 : int r;
340 :
341 4 : bf_assert(map);
342 :
343 3 : if (map->key_size == BF_MAP_KEY_SIZE_UNKNOWN) {
344 0 : return bf_err_r(
345 : -EINVAL,
346 : "can't create a map with BF_MAP_KEY_SIZE_UNKNOWN key size");
347 : }
348 :
349 3 : if (map->value_size == BF_MAP_VALUE_SIZE_UNKNOWN) {
350 0 : return bf_err_r(
351 : -EINVAL,
352 : "can't create a map with BF_MAP_VALUE_SIZE_UNKNOWN value size");
353 : }
354 :
355 3 : if (map->n_elems == BF_MAP_N_ELEMS_UNKNOWN) {
356 1 : return bf_err_r(
357 : -EINVAL,
358 : "can't create a map with BF_MAP_N_ELEMS_UNKNOWN number of elements");
359 : }
360 :
361 2 : attr.map_type = _bf_map_bpf_type_to_kernel_type(map->bpf_type);
362 2 : attr.key_size = map->key_size;
363 2 : attr.value_size = map->value_size;
364 2 : attr.max_entries = map->n_elems;
365 2 : attr.map_flags = flags;
366 :
367 2 : if ((token_fd = bf_ctx_token()) != -1) {
368 0 : attr.map_token_fd = token_fd;
369 0 : attr.map_flags |= BPF_F_TOKEN_FD;
370 : }
371 :
372 : /** The BTF data is not mandatory to use the map, but a good addition.
373 : * Hence, bpfilter will try to make the BTF data available, but will
374 : * ignore if that fails. @ref _bf_map_make_btf is used to isolate the
375 : * BTF data generation: if it fails we ignore the issue, but if it
376 : * succeeds we update the @c bpf_attr structure with the BTF details.
377 : * There is some boilerplate for @ref bf_btf structure, it could be
378 : * simpler, but the current implementation ensure the BTF data is properly
379 : * freed on error, without preventing the BPF map to be created. */
380 2 : btf = _bf_map_make_btf(map);
381 2 : if (btf) {
382 0 : attr.btf_fd = btf->fd;
383 0 : attr.btf_key_type_id = btf->key_type_id;
384 0 : attr.btf_value_type_id = btf->value_type_id;
385 : }
386 :
387 2 : (void)snprintf(attr.map_name, BPF_OBJ_NAME_LEN, "%s", map->name);
388 :
389 2 : r = bf_bpf(BPF_MAP_CREATE, &attr);
390 2 : if (r < 0)
391 1 : return bf_err_r(r, "failed to create BPF map '%s'", map->name);
392 :
393 1 : map->fd = r;
394 :
395 1 : return 0;
396 : }
397 :
398 1 : void bf_map_destroy(struct bf_map *map)
399 : {
400 1 : bf_assert(map);
401 :
402 0 : closep(&map->fd);
403 0 : }
404 :
405 0 : int bf_map_pin(const struct bf_map *map, int dir_fd)
406 : {
407 : int r;
408 :
409 0 : bf_assert(map);
410 :
411 0 : r = bf_bpf_obj_pin(map->name, map->fd, dir_fd);
412 0 : if (r < 0)
413 0 : return bf_err_r(r, "failed to pin BPF map '%s'", map->name);
414 :
415 : return 0;
416 : }
417 :
418 0 : void bf_map_unpin(const struct bf_map *map, int dir_fd)
419 : {
420 : int r;
421 :
422 0 : bf_assert(map);
423 :
424 0 : r = unlinkat(dir_fd, map->name, 0);
425 0 : if (r < 0 && errno != ENOENT) {
426 : // Do not warn on ENOENT, we want the file to be gone!
427 0 : bf_warn_r(
428 : errno,
429 : "failed to unlink BPF map '%s', assuming the map is not pinned",
430 : map->name);
431 : }
432 0 : }
433 :
434 0 : int bf_map_set_key_size(struct bf_map *map, size_t key_size)
435 : {
436 0 : bf_assert(key_size != 0);
437 :
438 0 : if (map->fd != -1) {
439 0 : return bf_err_r(
440 : -EPERM,
441 : "can't change the size of the map key once it has been created");
442 : }
443 :
444 0 : map->key_size = key_size;
445 :
446 0 : return 0;
447 : }
448 :
449 0 : int bf_map_set_value_size(struct bf_map *map, size_t value_size)
450 : {
451 0 : bf_assert(value_size != 0);
452 :
453 0 : if (map->fd != -1) {
454 0 : return bf_err_r(
455 : -EPERM,
456 : "can't change the size of the map value once it has been created");
457 : }
458 :
459 0 : map->value_size = value_size;
460 :
461 0 : return 0;
462 : }
463 :
464 2 : int bf_map_set_n_elems(struct bf_map *map, size_t n_elems)
465 : {
466 2 : bf_assert(n_elems != 0);
467 :
468 2 : if (map->fd != -1) {
469 1 : return bf_err_r(
470 : -EPERM,
471 : "can't change the number of elements in a map once the map has been created");
472 : }
473 :
474 1 : map->n_elems = n_elems;
475 :
476 1 : return 0;
477 : }
478 :
479 3 : int bf_map_set_elem(const struct bf_map *map, void *key, void *value)
480 : {
481 3 : union bpf_attr attr = {};
482 :
483 6 : bf_assert(map && key && value);
484 :
485 0 : attr.map_fd = map->fd;
486 0 : attr.key = (unsigned long long)key;
487 0 : attr.value = (unsigned long long)value;
488 0 : attr.flags = BPF_ANY;
489 :
490 0 : return bf_bpf(BPF_MAP_UPDATE_ELEM, &attr);
491 : }
492 :
493 : static const char *_bf_map_bpf_type_strs[] = {
494 : [BF_MAP_BPF_TYPE_ARRAY] = "BF_MAP_BPF_TYPE_ARRAY",
495 : [BF_MAP_BPF_TYPE_HASH] = "BF_MAP_BPF_TYPE_HASH",
496 : };
497 :
498 : static_assert(ARRAY_SIZE(_bf_map_bpf_type_strs) == _BF_MAP_BPF_TYPE_MAX,
499 : "missing entries in _bf_map_bpf_type_strs array");
500 :
501 4 : const char *bf_map_bpf_type_to_str(enum bf_map_bpf_type bpf_type)
502 : {
503 4 : bf_assert(0 <= bpf_type && bpf_type < _BF_MAP_BPF_TYPE_MAX);
504 :
505 2 : return _bf_map_bpf_type_strs[bpf_type];
506 : }
507 :
508 5 : int bf_map_bpf_type_from_str(const char *str, enum bf_map_bpf_type *bpf_type)
509 : {
510 5 : bf_assert(str);
511 4 : bf_assert(bpf_type);
512 :
513 6 : for (size_t i = 0; i < _BF_MAP_BPF_TYPE_MAX; ++i) {
514 5 : if (bf_streq(_bf_map_bpf_type_strs[i], str)) {
515 2 : *bpf_type = i;
516 2 : return 0;
517 : }
518 : }
519 :
520 : return -EINVAL;
521 : }
|