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