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