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 : 266 : 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 : : bf_assert(map && name);
32 : :
33 : 266 : _free_bf_map_ struct bf_map *_map = NULL;
34 : :
35 [ - + ]: 266 : if (name[0] == '\0')
36 [ # # ]: 0 : return bf_err_r(-EINVAL, "map name can't be empty");
37 : :
38 [ - + ]: 266 : if (n_elems == 0)
39 [ # # ]: 0 : return bf_err_r(-EINVAL, "map can't be created without elements");
40 : :
41 : 266 : _map = malloc(sizeof(*_map));
42 [ + - ]: 266 : if (!_map)
43 : : return -ENOMEM;
44 : :
45 : 266 : _map->type = type;
46 : 266 : _map->bpf_type = bpf_type;
47 : 266 : _map->key_size = key_size;
48 : 266 : _map->value_size = value_size;
49 : 266 : _map->n_elems = n_elems;
50 : 266 : _map->fd = -1;
51 : :
52 : 266 : bf_strncpy(_map->name, BPF_OBJ_NAME_LEN, name);
53 : :
54 : 266 : *map = TAKE_PTR(_map);
55 : :
56 : 266 : return 0;
57 : : }
58 : :
59 : 186 : 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 [ - + ]: 186 : 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 : 186 : return _bf_map_new(map, name, type, _map_type_to_bpf[type], key_size,
75 : : value_size, n_elems);
76 : : }
77 : :
78 [ + + ]: 80 : int bf_map_new_from_set(struct bf_map **map, const char *name,
79 : : const struct bf_set *set)
80 : : {
81 : 80 : return _bf_map_new(map, name, BF_MAP_TYPE_SET,
82 : 80 : set->use_trie ? BF_BPF_MAP_TYPE_LPM_TRIE :
83 : : BF_BPF_MAP_TYPE_HASH,
84 [ + + ]: 80 : set->elem_size, 1, bf_list_size(&set->elems));
85 : : }
86 : :
87 : 6 : int bf_map_new_from_pack(struct bf_map **map, int dir_fd, bf_rpack_node_t node)
88 : : {
89 : 6 : _free_bf_map_ struct bf_map *_map = NULL;
90 : 6 : _cleanup_free_ char *name = NULL;
91 : : int r;
92 : :
93 : : bf_assert(map);
94 : :
95 : 6 : _map = malloc(sizeof(*_map));
96 [ + - ]: 6 : if (!_map)
97 : : return -ENOMEM;
98 : :
99 : 6 : r = bf_rpack_kv_str(node, "name", &name);
100 [ - + ]: 6 : if (r)
101 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.name");
102 [ - + ]: 6 : if (strlen(name) == 0)
103 [ # # ]: 0 : return bf_err_r(-EINVAL, "map name can't be empty");
104 : 6 : bf_strncpy(_map->name, BPF_OBJ_NAME_LEN, name);
105 : :
106 [ - + ]: 6 : 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 [ - + ]: 6 : 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 : 6 : r = bf_rpack_kv_u64(node, "key_size", &_map->key_size);
115 [ - + ]: 6 : if (r)
116 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.key_size");
117 : :
118 : 6 : r = bf_rpack_kv_u64(node, "value_size", &_map->value_size);
119 [ - + ]: 6 : if (r)
120 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.value_size");
121 : :
122 : 6 : r = bf_rpack_kv_u64(node, "n_elems", &_map->n_elems);
123 [ - + ]: 6 : if (r)
124 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.n_elems");
125 [ - + ]: 6 : if (_map->n_elems == 0)
126 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map should not have 0 elements");
127 : :
128 [ + - ]: 6 : if (dir_fd != -1) {
129 : 6 : r = bf_bpf_obj_get(_map->name, dir_fd, &_map->fd);
130 [ - + ]: 6 : 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 : 6 : *map = TAKE_PTR(_map);
139 : :
140 : 6 : return 0;
141 : : }
142 : :
143 : 624 : void bf_map_free(struct bf_map **map)
144 : : {
145 : : bf_assert(map);
146 : :
147 [ + + ]: 624 : if (!*map)
148 : : return;
149 : :
150 : 272 : closep(&(*map)->fd);
151 : : freep((void *)map);
152 : : }
153 : :
154 : 932 : int bf_map_pack(const struct bf_map *map, bf_wpack_t *pack)
155 : : {
156 : : bf_assert(map);
157 : : bf_assert(pack);
158 : :
159 : 932 : bf_wpack_kv_str(pack, "name", map->name);
160 : 932 : bf_wpack_kv_enum(pack, "type", map->type);
161 : 932 : bf_wpack_kv_enum(pack, "bpf_type", map->bpf_type);
162 : 932 : bf_wpack_kv_u64(pack, "key_size", map->key_size);
163 : 932 : bf_wpack_kv_u64(pack, "value_size", map->value_size);
164 : 932 : bf_wpack_kv_u64(pack, "n_elems", map->n_elems);
165 : :
166 [ - + ]: 932 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
167 : : }
168 : :
169 : : 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 : : bf_assert(0 <= type && type < _BF_MAP_TYPE_MAX);
181 : :
182 : 6 : return type_strs[type];
183 : : }
184 : :
185 : : 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 : 6 : return type_strs[type] ? type_strs[type] : "<no mapping>";
195 : : }
196 : :
197 : 6 : void bf_map_dump(const struct bf_map *map, prefix_t *prefix)
198 : : {
199 : : bf_assert(map);
200 : : bf_assert(prefix);
201 : :
202 [ + - ]: 6 : DUMP(prefix, "struct bf_map at %p", map);
203 : :
204 : 6 : bf_dump_prefix_push(prefix);
205 [ + - ]: 6 : DUMP(prefix, "type: %s", _bf_map_type_to_str(map->type));
206 [ + - ]: 6 : DUMP(prefix, "name: %s", map->name);
207 [ + - + - ]: 12 : DUMP(prefix, "bpf_type: %s", _bf_bpf_type_to_str(map->bpf_type));
208 [ + - ]: 6 : DUMP(prefix, "key_size: %lu", map->key_size);
209 [ + - ]: 6 : DUMP(prefix, "value_size: %lu", map->value_size);
210 : :
211 [ - + ]: 6 : if (map->n_elems == BF_MAP_N_ELEMS_UNKNOWN) {
212 [ # # ]: 0 : DUMP(prefix, "n_elems: unknown");
213 : : } else {
214 [ + - ]: 6 : DUMP(prefix, "n_elems: %lu", map->n_elems);
215 : : }
216 : :
217 : 6 : bf_dump_prefix_last(prefix);
218 [ + - ]: 6 : DUMP(prefix, "fd: %d", map->fd);
219 : :
220 : 6 : bf_dump_prefix_pop(prefix);
221 : 6 : }
222 : :
223 : : #define _free_bf_btf_ __attribute__((__cleanup__(_bf_btf_free)))
224 : :
225 : : static void _bf_btf_free(struct bf_btf **btf);
226 : :
227 : 260 : static int _bf_btf_new(struct bf_btf **btf)
228 : : {
229 : 260 : _free_bf_btf_ struct bf_btf *_btf = NULL;
230 : :
231 : : bf_assert(btf);
232 : :
233 : 260 : _btf = malloc(sizeof(struct bf_btf));
234 [ + - ]: 260 : if (!_btf)
235 : : return -ENOMEM;
236 : :
237 : 260 : _btf->fd = -1;
238 : :
239 : 260 : _btf->btf = btf__new_empty();
240 [ - + ]: 260 : if (!_btf->btf)
241 : 0 : return -errno;
242 : :
243 : 260 : *btf = TAKE_PTR(_btf);
244 : :
245 : 260 : return 0;
246 : : }
247 : :
248 : 780 : static void _bf_btf_free(struct bf_btf **btf)
249 : : {
250 : : bf_assert(btf);
251 : :
252 [ + + ]: 780 : if (!*btf)
253 : : return;
254 : :
255 : 260 : btf__free((*btf)->btf);
256 : 260 : closep(&(*btf)->fd);
257 : : freep((void *)btf);
258 : : }
259 : :
260 : 60 : static int _bf_btf_load(struct bf_btf *btf)
261 : : {
262 : 60 : union bpf_attr attr = {};
263 : : const void *raw;
264 : : int r;
265 : :
266 : : bf_assert(btf);
267 : :
268 : 60 : raw = btf__raw_data(btf->btf, &attr.btf_size);
269 [ - + ]: 60 : if (!raw)
270 [ # # ]: 0 : return bf_err_r(errno, "failed to request BTF raw data");
271 : :
272 : 60 : r = bf_bpf_btf_load(raw, bf_ctx_token());
273 [ - + ]: 60 : 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 : 260 : static struct bf_btf *_bf_map_make_btf(const struct bf_map *map)
290 : : {
291 : 260 : _free_bf_btf_ struct bf_btf *btf = NULL;
292 : : struct btf *kbtf;
293 : : int r;
294 : :
295 : : bf_assert(map);
296 : :
297 : 260 : r = _bf_btf_new(&btf);
298 [ - + ]: 260 : if (r < 0)
299 : : return NULL;
300 : :
301 : 260 : kbtf = btf->btf;
302 : :
303 [ + + - ]: 260 : switch (map->type) {
304 : 60 : case BF_MAP_TYPE_COUNTERS:
305 : 60 : btf__add_int(kbtf, "u64", 8, 0);
306 : 60 : btf->key_type_id = btf__add_int(kbtf, "u32", 4, 0);
307 : 60 : btf->value_type_id = btf__add_struct(kbtf, "bf_counters", 16);
308 : 60 : btf__add_field(kbtf, "packets", 1, 0, 0);
309 : 60 : 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 : 60 : r = _bf_btf_load(btf);
322 [ + - ]: 60 : if (r) {
323 [ + - ]: 60 : bf_warn_r(r, "failed to load BTF data for %s, ignoring", map->name);
324 : 60 : return NULL;
325 : : }
326 : :
327 : 0 : return TAKE_PTR(btf);
328 : : }
329 : :
330 : 260 : int bf_map_create(struct bf_map *map)
331 : : {
332 : 260 : _free_bf_btf_ struct bf_btf *btf = NULL;
333 : : int r;
334 : :
335 : : bf_assert(map);
336 : :
337 [ - + ]: 260 : 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 [ - + ]: 260 : 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 [ - + ]: 260 : if (map->n_elems == BF_MAP_N_ELEMS_UNKNOWN) {
350 [ # # ]: 0 : 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 : 520 : r = bf_bpf_map_create(map->name, map->bpf_type, map->key_size,
364 : 260 : map->value_size, map->n_elems, _bf_map_make_btf(map),
365 : : bf_ctx_token());
366 [ - + ]: 260 : if (r < 0)
367 [ # # ]: 0 : return bf_err_r(r, "failed to create BPF map '%s'", map->name);
368 : :
369 : 260 : map->fd = r;
370 : :
371 : 260 : return 0;
372 : : }
373 : :
374 : 108 : void bf_map_destroy(struct bf_map *map)
375 : : {
376 : : bf_assert(map);
377 : :
378 : 108 : closep(&map->fd);
379 : 108 : }
380 : :
381 : 254 : int bf_map_pin(const struct bf_map *map, int dir_fd)
382 : : {
383 : : int r;
384 : :
385 : : bf_assert(map);
386 : :
387 : 254 : r = bf_bpf_obj_pin(map->name, map->fd, dir_fd);
388 [ - + ]: 254 : 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 : 117 : void bf_map_unpin(const struct bf_map *map, int dir_fd)
395 : : {
396 : : int r;
397 : :
398 : : bf_assert(map);
399 : :
400 : 117 : r = unlinkat(dir_fd, map->name, 0);
401 [ - + - - ]: 117 : 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 : 117 : }
409 : :
410 : 0 : int bf_map_set_key_size(struct bf_map *map, size_t key_size)
411 : : {
412 : : 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 : 60 : int bf_map_set_value_size(struct bf_map *map, size_t value_size)
426 : : {
427 : : bf_assert(value_size != 0);
428 : :
429 [ - + ]: 60 : 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 : 60 : map->value_size = value_size;
436 : :
437 : 60 : return 0;
438 : : }
439 : :
440 : 60 : int bf_map_set_n_elems(struct bf_map *map, size_t n_elems)
441 : : {
442 : : bf_assert(n_elems != 0);
443 : :
444 [ - + ]: 60 : if (map->fd != -1) {
445 [ # # ]: 0 : 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 : 60 : map->n_elems = n_elems;
451 : :
452 : 60 : return 0;
453 : : }
454 : :
455 : 60 : int bf_map_set_elem(const struct bf_map *map, void *key, void *value)
456 : : {
457 : : bf_assert(map && key && value);
458 : :
459 : 60 : return bf_bpf_map_update_elem(map->fd, key, value, 0);
460 : : }
|