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/ctx.h>
22 : : #include <bpfilter/dump.h>
23 : : #include <bpfilter/helper.h>
24 : : #include <bpfilter/logger.h>
25 : :
26 : : #define _free_bf_btf_ __attribute__((__cleanup__(_bf_btf_free)))
27 : :
28 : : static void _bf_btf_free(struct bf_btf **btf);
29 : :
30 : 4274 : static int _bf_btf_new(struct bf_btf **btf)
31 : : {
32 : 4274 : _free_bf_btf_ struct bf_btf *_btf = NULL;
33 : :
34 : : assert(btf);
35 : :
36 : 4274 : _btf = malloc(sizeof(struct bf_btf));
37 [ + - ]: 4274 : if (!_btf)
38 : : return -ENOMEM;
39 : :
40 : 4274 : _btf->fd = -1;
41 : :
42 : 4274 : _btf->btf = btf__new_empty();
43 [ - + ]: 4274 : if (!_btf->btf)
44 : 0 : return -errno;
45 : :
46 : 4274 : *btf = TAKE_PTR(_btf);
47 : :
48 : 4274 : return 0;
49 : : }
50 : :
51 : 12822 : static void _bf_btf_free(struct bf_btf **btf)
52 : : {
53 : : assert(btf);
54 : :
55 [ + + ]: 12822 : if (!*btf)
56 : : return;
57 : :
58 : 4274 : btf__free((*btf)->btf);
59 : 4274 : closep(&(*btf)->fd);
60 : : freep((void *)btf);
61 : : }
62 : :
63 : 1307 : static int _bf_btf_load(struct bf_btf *btf)
64 : : {
65 : 1307 : union bpf_attr attr = {};
66 : : const void *raw;
67 : : int r;
68 : :
69 : : assert(btf);
70 : :
71 : 1307 : raw = btf__raw_data(btf->btf, &attr.btf_size);
72 [ - + ]: 1307 : if (!raw)
73 [ # # ]: 0 : return bf_err_r(errno, "failed to request BTF raw data");
74 : :
75 : 1307 : r = bf_bpf_btf_load(raw, attr.btf_size, bf_ctx_token());
76 [ + - ]: 1307 : if (r < 0)
77 : : return r;
78 : :
79 : 1307 : btf->fd = r;
80 : :
81 : 1307 : return 0;
82 : : }
83 : :
84 : : /**
85 : : * @brief Create the BTF data for the map.
86 : : *
87 : : * @param map Map to create the BTF data for. @c map.type will define the
88 : : * exact content of the BTF object. Can't be NULL.
89 : : * @return A @ref bf_btf structure on success, or NULL on error. The
90 : : * @ref bf_btf structure is owned by the caller.
91 : : */
92 : 4274 : static struct bf_btf *_bf_map_make_btf(const struct bf_map *map)
93 : : {
94 : 4274 : _free_bf_btf_ struct bf_btf *btf = NULL;
95 : : struct btf *kbtf;
96 : : int r;
97 : :
98 : : assert(map);
99 : :
100 : 4274 : r = _bf_btf_new(&btf);
101 [ - + ]: 4274 : if (r < 0)
102 : : return NULL;
103 : :
104 : 4274 : kbtf = btf->btf;
105 : :
106 [ + + - ]: 4274 : switch (map->type) {
107 : 1307 : case BF_MAP_TYPE_COUNTERS:
108 : 1307 : btf__add_int(kbtf, "u64", 8, 0);
109 : 1307 : btf->key_type_id = btf__add_int(kbtf, "u32", 4, 0);
110 : 1307 : btf->value_type_id = btf__add_struct(kbtf, "bf_counters", 16);
111 : 1307 : btf__add_field(kbtf, "count", 1, 0, 0);
112 : 1307 : btf__add_field(kbtf, "size", 1, 64, 0);
113 : : break;
114 : : case BF_MAP_TYPE_PRINTER:
115 : : case BF_MAP_TYPE_SET:
116 : : case BF_MAP_TYPE_LOG:
117 : : case BF_MAP_TYPE_CTX:
118 : : // No BTF data available for these map types
119 : : return NULL;
120 : 0 : default:
121 [ # # ]: 0 : bf_err_r(-ENOTSUP, "bf_map type %d is not supported", map->type);
122 : : return NULL;
123 : : }
124 : :
125 : 1307 : r = _bf_btf_load(btf);
126 [ - + ]: 1307 : if (r) {
127 [ # # ]: 0 : bf_warn_r(r, "failed to load BTF data for %s, ignoring", map->name);
128 : 0 : return NULL;
129 : : }
130 : :
131 : 1307 : return TAKE_PTR(btf);
132 : : }
133 : :
134 : 4274 : static int _bf_map_new(struct bf_map **map, const char *name,
135 : : enum bf_map_type type, enum bf_bpf_map_type bpf_type,
136 : : size_t key_size, size_t value_size, size_t n_elems)
137 : : {
138 : 4274 : _free_bf_map_ struct bf_map *_map = NULL;
139 : 4274 : _free_bf_btf_ struct bf_btf *btf = NULL;
140 : 4274 : _cleanup_close_ int fd = -1;
141 : :
142 : : assert(map);
143 : : assert(name);
144 : :
145 [ - + ]: 4274 : if (name[0] == '\0')
146 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map: name can't be empty");
147 : :
148 [ - + ]: 4274 : if (type != BF_MAP_TYPE_LOG && key_size == 0)
149 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map %s: key size can't be 0", name);
150 [ - + ]: 4274 : if (type == BF_MAP_TYPE_LOG && key_size != 0)
151 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map %s: key size must be 0", name);
152 : :
153 [ - + ]: 4274 : if (type != BF_MAP_TYPE_LOG && value_size == 0)
154 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map %s: value size can't be 0", name);
155 [ - + ]: 4274 : if (type == BF_MAP_TYPE_LOG && value_size != 0)
156 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map %s: value size must be 0", name);
157 : :
158 [ - + ]: 4274 : if (n_elems == 0) {
159 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map %s: number of elements can't be 0",
160 : : name);
161 : : }
162 : :
163 : 4274 : _map = malloc(sizeof(*_map));
164 [ + - ]: 4274 : if (!_map)
165 : : return -ENOMEM;
166 : :
167 : 4274 : _map->type = type;
168 : 4274 : _map->bpf_type = bpf_type;
169 : 4274 : _map->key_size = key_size;
170 : 4274 : _map->value_size = value_size;
171 : 4274 : _map->n_elems = n_elems;
172 : 4274 : _map->fd = -1;
173 : :
174 : 4274 : bf_strncpy(_map->name, BPF_OBJ_NAME_LEN, name);
175 : :
176 : 4274 : btf = _bf_map_make_btf(_map);
177 : :
178 : 4274 : fd =
179 : 4274 : bf_bpf_map_create(_map->name, _map->bpf_type, _map->key_size,
180 : 4274 : _map->value_size, _map->n_elems, btf, bf_ctx_token());
181 [ - + ]: 4274 : if (fd < 0)
182 [ # # ]: 0 : return bf_err_r(fd, "bf_map %s: failed to create map", name);
183 : :
184 : 4274 : _map->fd = TAKE_FD(fd);
185 : 4274 : *map = TAKE_PTR(_map);
186 : :
187 : 4274 : return 0;
188 : : }
189 : :
190 : 3973 : int bf_map_new(struct bf_map **map, const char *name, enum bf_map_type type,
191 : : size_t key_size, size_t value_size, size_t n_elems)
192 : : {
193 : : static enum bf_bpf_map_type _map_type_to_bpf[_BF_MAP_TYPE_MAX] = {
194 : : [BF_MAP_TYPE_COUNTERS] = BF_BPF_MAP_TYPE_ARRAY,
195 : : [BF_MAP_TYPE_PRINTER] = BF_BPF_MAP_TYPE_ARRAY,
196 : : [BF_MAP_TYPE_LOG] = BF_BPF_MAP_TYPE_RINGBUF,
197 : : [BF_MAP_TYPE_CTX] = BF_BPF_MAP_TYPE_ARRAY,
198 : : };
199 : :
200 : : assert(map);
201 : : assert(name);
202 : :
203 [ - + ]: 3973 : if (type == BF_MAP_TYPE_SET) {
204 [ # # ]: 0 : return bf_err_r(
205 : : -EINVAL,
206 : : "use bf_map_new_from_set() to create a bf_map from a bf_set");
207 : : }
208 : :
209 : 3973 : return _bf_map_new(map, name, type, _map_type_to_bpf[type], key_size,
210 : : value_size, n_elems);
211 : : }
212 : :
213 : 301 : int bf_map_new_from_set(struct bf_map **map, const char *name,
214 : : const struct bf_set *set, size_t n_elems,
215 : : size_t value_size)
216 : : {
217 : : assert(map);
218 : : assert(name);
219 : : assert(set);
220 : :
221 : 301 : return _bf_map_new(map, name, BF_MAP_TYPE_SET,
222 : 301 : set->use_trie ? BF_BPF_MAP_TYPE_LPM_TRIE :
223 : : BF_BPF_MAP_TYPE_HASH,
224 [ + + ]: 301 : set->elem_size, value_size, n_elems);
225 : : }
226 : :
227 : 11680 : int bf_map_new_from_pack(struct bf_map **map, int dir_fd, bf_rpack_node_t node)
228 : : {
229 : 11680 : _free_bf_map_ struct bf_map *_map = NULL;
230 : 11680 : _cleanup_free_ char *name = NULL;
231 : : int r;
232 : :
233 : : assert(map);
234 : :
235 [ - + ]: 11680 : if (dir_fd < 0)
236 [ # # ]: 0 : return bf_err_r(-EBADFD, "dir_fd is invalid");
237 : :
238 : 11680 : _map = malloc(sizeof(*_map));
239 [ + - ]: 11680 : if (!_map)
240 : : return -ENOMEM;
241 : :
242 : 11680 : r = bf_rpack_kv_str(node, "name", &name);
243 [ - + ]: 11680 : if (r)
244 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.name");
245 [ - + ]: 11680 : if (strlen(name) == 0)
246 [ # # ]: 0 : return bf_err_r(-EINVAL, "map name can't be empty");
247 : 11680 : bf_strncpy(_map->name, BPF_OBJ_NAME_LEN, name);
248 : :
249 [ - + - + : 11680 : r = bf_rpack_kv_enum(node, "type", &_map->type, 0, _BF_MAP_TYPE_MAX);
- - ]
250 : : if (r)
251 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.type");
252 : :
253 [ - + - + : 11680 : r = bf_rpack_kv_enum(node, "bpf_type", &_map->bpf_type, 0,
- - ]
254 : : __MAX_BPF_MAP_TYPE);
255 : : if (r)
256 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.bpf_type");
257 : :
258 : 11680 : r = bf_rpack_kv_u64(node, "key_size", &_map->key_size);
259 [ - + ]: 11680 : if (r)
260 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.key_size");
261 : :
262 : 11680 : r = bf_rpack_kv_u64(node, "value_size", &_map->value_size);
263 [ - + ]: 11680 : if (r)
264 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.value_size");
265 : :
266 : 11680 : r = bf_rpack_kv_u64(node, "n_elems", &_map->n_elems);
267 [ - + ]: 11680 : if (r)
268 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.n_elems");
269 [ - + ]: 11680 : if (_map->n_elems == 0)
270 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map should not have 0 elements");
271 : :
272 : 11680 : r = bf_bpf_obj_get(_map->name, dir_fd, &_map->fd);
273 [ - + ]: 11680 : if (r < 0)
274 [ # # ]: 0 : return bf_err_r(r, "failed to open pinned BPF map '%s'", _map->name);
275 : :
276 : 11680 : *map = TAKE_PTR(_map);
277 : :
278 : 11680 : return 0;
279 : : }
280 : :
281 : 43597 : void bf_map_free(struct bf_map **map)
282 : : {
283 : : assert(map);
284 : :
285 [ + + ]: 43597 : if (!*map)
286 : : return;
287 : :
288 : 15954 : closep(&(*map)->fd);
289 : : freep((void *)map);
290 : : }
291 : :
292 : 2998 : int bf_map_pack(const struct bf_map *map, bf_wpack_t *pack)
293 : : {
294 : : assert(map);
295 : : assert(pack);
296 : :
297 : 2998 : bf_wpack_kv_str(pack, "name", map->name);
298 : 2998 : bf_wpack_kv_enum(pack, "type", map->type);
299 : 2998 : bf_wpack_kv_enum(pack, "bpf_type", map->bpf_type);
300 : 2998 : bf_wpack_kv_u64(pack, "key_size", map->key_size);
301 : 2998 : bf_wpack_kv_u64(pack, "value_size", map->value_size);
302 : 2998 : bf_wpack_kv_u64(pack, "n_elems", map->n_elems);
303 : :
304 [ - + ]: 2998 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
305 : : }
306 : :
307 : : static const char *_bf_map_type_to_str(enum bf_map_type type)
308 : : {
309 : : static const char *type_strs[] = {
310 : : [BF_MAP_TYPE_COUNTERS] = "BF_MAP_TYPE_COUNTERS",
311 : : [BF_MAP_TYPE_PRINTER] = "BF_MAP_TYPE_PRINTER",
312 : : [BF_MAP_TYPE_LOG] = "BF_MAP_TYPE_LOG",
313 : : [BF_MAP_TYPE_SET] = "BF_MAP_TYPE_SET",
314 : : [BF_MAP_TYPE_CTX] = "BF_MAP_TYPE_CTX",
315 : : };
316 : :
317 : : static_assert_enum_mapping(type_strs, _BF_MAP_TYPE_MAX);
318 : : assert(0 <= type && type < _BF_MAP_TYPE_MAX);
319 : :
320 : 0 : return type_strs[type];
321 : : }
322 : :
323 : : static const char *_bf_bpf_type_to_str(enum bf_bpf_map_type type)
324 : : {
325 : : static const char *type_strs[] = {
326 : : [BF_BPF_MAP_TYPE_HASH] = "BF_BPF_MAP_TYPE_HASH",
327 : : [BF_BPF_MAP_TYPE_ARRAY] = "BF_BPF_MAP_TYPE_ARRAY",
328 : : [BF_BPF_MAP_TYPE_LPM_TRIE] = "BF_BPF_MAP_TYPE_LPM_TRIE",
329 : : [BF_BPF_MAP_TYPE_RINGBUF] = "BF_BPF_MAP_TYPE_RINGBUF",
330 : : };
331 : :
332 : 0 : return type_strs[type] ? type_strs[type] : "<no mapping>";
333 : : }
334 : :
335 : 35 : void bf_map_dump(const struct bf_map *map, prefix_t *prefix)
336 : : {
337 : : assert(map);
338 : : assert(prefix);
339 : :
340 [ - + ]: 35 : DUMP(prefix, "struct bf_map at %p", map);
341 : :
342 : 35 : bf_dump_prefix_push(prefix);
343 [ - + ]: 35 : DUMP(prefix, "type: %s", _bf_map_type_to_str(map->type));
344 [ - + ]: 35 : DUMP(prefix, "name: %s", map->name);
345 [ - + - - ]: 35 : DUMP(prefix, "bpf_type: %s", _bf_bpf_type_to_str(map->bpf_type));
346 [ - + ]: 35 : DUMP(prefix, "key_size: %lu", map->key_size);
347 [ - + ]: 35 : DUMP(prefix, "value_size: %lu", map->value_size);
348 [ - + ]: 35 : DUMP(prefix, "n_elems: %lu", map->n_elems);
349 : :
350 : 35 : bf_dump_prefix_last(prefix);
351 [ - + ]: 35 : DUMP(prefix, "fd: %d", map->fd);
352 : :
353 : 35 : bf_dump_prefix_pop(prefix);
354 : 35 : }
355 : :
356 : 2933 : int bf_map_pin(const struct bf_map *map, int dir_fd)
357 : : {
358 : : int r;
359 : :
360 : : assert(map);
361 : :
362 : 2933 : r = bf_bpf_obj_pin(map->name, map->fd, dir_fd);
363 [ - + ]: 2933 : if (r < 0)
364 [ # # ]: 0 : return bf_err_r(r, "failed to pin BPF map '%s'", map->name);
365 : :
366 : : return 0;
367 : : }
368 : :
369 : 2796 : void bf_map_unpin(const struct bf_map *map, int dir_fd)
370 : : {
371 : : int r;
372 : :
373 : : assert(map);
374 : :
375 : 2796 : r = unlinkat(dir_fd, map->name, 0);
376 [ + + - + ]: 2796 : if (r < 0 && errno != ENOENT) {
377 : : // Do not warn on ENOENT, we want the file to be gone!
378 [ # # ]: 0 : bf_warn_r(
379 : : errno,
380 : : "failed to unlink BPF map '%s', assuming the map is not pinned",
381 : : map->name);
382 : : }
383 : 2796 : }
384 : :
385 : 3312 : int bf_map_set_elem(const struct bf_map *map, void *key, void *value)
386 : : {
387 : : assert(map);
388 : : assert(key);
389 : : assert(value);
390 : :
391 : 3312 : return bf_bpf_map_update_elem(map->fd, key, value, 0);
392 : : }
|