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