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