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 : 3798 : static int _bf_btf_new(struct bf_btf **btf)
31 : : {
32 : 3798 : _free_bf_btf_ struct bf_btf *_btf = NULL;
33 : :
34 : : assert(btf);
35 : :
36 : 3798 : _btf = malloc(sizeof(struct bf_btf));
37 [ + - ]: 3798 : if (!_btf)
38 : : return -ENOMEM;
39 : :
40 : 3798 : _btf->fd = -1;
41 : :
42 : 3798 : _btf->btf = btf__new_empty();
43 [ - + ]: 3798 : if (!_btf->btf)
44 : 0 : return -errno;
45 : :
46 : 3798 : *btf = TAKE_PTR(_btf);
47 : :
48 : 3798 : return 0;
49 : : }
50 : :
51 : 11394 : static void _bf_btf_free(struct bf_btf **btf)
52 : : {
53 : : assert(btf);
54 : :
55 [ + + ]: 11394 : if (!*btf)
56 : : return;
57 : :
58 : 3798 : btf__free((*btf)->btf);
59 : 3798 : closep(&(*btf)->fd);
60 : : freep((void *)btf);
61 : : }
62 : :
63 : 1156 : static int _bf_btf_load(struct bf_btf *btf)
64 : : {
65 : 1156 : union bpf_attr attr = {};
66 : : const void *raw;
67 : : int r;
68 : :
69 : : assert(btf);
70 : :
71 : 1156 : raw = btf__raw_data(btf->btf, &attr.btf_size);
72 [ - + ]: 1156 : if (!raw)
73 [ # # ]: 0 : return bf_err_r(errno, "failed to request BTF raw data");
74 : :
75 : 1156 : r = bf_bpf_btf_load(raw, attr.btf_size, bf_ctx_token());
76 [ + - ]: 1156 : if (r < 0)
77 : : return r;
78 : :
79 : 1156 : btf->fd = r;
80 : :
81 : 1156 : 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 : 3798 : static struct bf_btf *_bf_map_make_btf(const struct bf_map *map)
93 : : {
94 : 3798 : _free_bf_btf_ struct bf_btf *btf = NULL;
95 : : struct btf *kbtf;
96 : : int r;
97 : :
98 : : assert(map);
99 : :
100 : 3798 : r = _bf_btf_new(&btf);
101 [ - + ]: 3798 : if (r < 0)
102 : : return NULL;
103 : :
104 : 3798 : kbtf = btf->btf;
105 : :
106 [ + + - ]: 3798 : switch (map->type) {
107 : 1156 : case BF_MAP_TYPE_COUNTERS:
108 : 1156 : btf__add_int(kbtf, "u64", 8, 0);
109 : 1156 : btf->key_type_id = btf__add_int(kbtf, "u32", 4, 0);
110 : 1156 : btf->value_type_id = btf__add_struct(kbtf, "bf_counters", 16);
111 : 1156 : btf__add_field(kbtf, "count", 1, 0, 0);
112 : 1156 : 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 : 1156 : r = _bf_btf_load(btf);
126 [ - + ]: 1156 : 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 : 1156 : return TAKE_PTR(btf);
132 : : }
133 : :
134 : 3798 : 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 : 3798 : _free_bf_map_ struct bf_map *_map = NULL;
139 : 3798 : _free_bf_btf_ struct bf_btf *btf = NULL;
140 : 3798 : _cleanup_close_ int fd = -1;
141 : :
142 : : assert(map);
143 : : assert(name);
144 : :
145 [ - + ]: 3798 : if (name[0] == '\0')
146 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map: name can't be empty");
147 : :
148 [ - + ]: 3798 : 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 [ - + ]: 3798 : 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 [ - + ]: 3798 : 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 [ - + ]: 3798 : 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 [ - + ]: 3798 : 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 : 3798 : _map = malloc(sizeof(*_map));
164 [ + - ]: 3798 : if (!_map)
165 : : return -ENOMEM;
166 : :
167 : 3798 : _map->type = type;
168 : 3798 : _map->bpf_type = bpf_type;
169 : 3798 : _map->key_size = key_size;
170 : 3798 : _map->value_size = value_size;
171 : 3798 : _map->n_elems = n_elems;
172 : 3798 : _map->fd = -1;
173 : :
174 : 3798 : bf_strncpy(_map->name, BPF_OBJ_NAME_LEN, name);
175 : :
176 : 3798 : btf = _bf_map_make_btf(_map);
177 : :
178 : 3798 : fd =
179 : 3798 : bf_bpf_map_create(_map->name, _map->bpf_type, _map->key_size,
180 : 3798 : _map->value_size, _map->n_elems, btf, bf_ctx_token());
181 [ - + ]: 3798 : if (fd < 0)
182 [ # # ]: 0 : return bf_err_r(fd, "bf_map %s: failed to create map", name);
183 : :
184 : 3798 : _map->fd = TAKE_FD(fd);
185 : 3798 : *map = TAKE_PTR(_map);
186 : :
187 : 3798 : return 0;
188 : : }
189 : :
190 : 3516 : 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 [ - + ]: 3516 : 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 : 3516 : return _bf_map_new(map, name, type, _map_type_to_bpf[type], key_size,
210 : : value_size, n_elems);
211 : : }
212 : :
213 [ + + ]: 282 : int bf_map_new_from_set(struct bf_map **map, const char *name,
214 : : const struct bf_set *set)
215 : : {
216 : : assert(map);
217 : : assert(name);
218 : : assert(set);
219 : :
220 : 282 : return _bf_map_new(map, name, BF_MAP_TYPE_SET,
221 : 282 : set->use_trie ? BF_BPF_MAP_TYPE_LPM_TRIE :
222 : : BF_BPF_MAP_TYPE_HASH,
223 [ + + ]: 282 : set->elem_size, 1, bf_list_size(&set->elems));
224 : : }
225 : :
226 : 1106 : int bf_map_new_from_pack(struct bf_map **map, int dir_fd, bf_rpack_node_t node)
227 : : {
228 : 1106 : _free_bf_map_ struct bf_map *_map = NULL;
229 : 1106 : _cleanup_free_ char *name = NULL;
230 : : int r;
231 : :
232 : : assert(map);
233 : :
234 [ - + ]: 1106 : if (dir_fd < 0)
235 [ # # ]: 0 : return bf_err_r(-EBADFD, "dir_fd is invalid");
236 : :
237 : 1106 : _map = malloc(sizeof(*_map));
238 [ + - ]: 1106 : if (!_map)
239 : : return -ENOMEM;
240 : :
241 : 1106 : r = bf_rpack_kv_str(node, "name", &name);
242 [ - + ]: 1106 : if (r)
243 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.name");
244 [ - + ]: 1106 : if (strlen(name) == 0)
245 [ # # ]: 0 : return bf_err_r(-EINVAL, "map name can't be empty");
246 : 1106 : bf_strncpy(_map->name, BPF_OBJ_NAME_LEN, name);
247 : :
248 [ - + - + : 1106 : r = bf_rpack_kv_enum(node, "type", &_map->type, 0, _BF_MAP_TYPE_MAX);
- - ]
249 : : if (r)
250 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.type");
251 : :
252 [ - + - + : 1106 : r = bf_rpack_kv_enum(node, "bpf_type", &_map->bpf_type, 0,
- - ]
253 : : __MAX_BPF_MAP_TYPE);
254 : : if (r)
255 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.bpf_type");
256 : :
257 : 1106 : r = bf_rpack_kv_u64(node, "key_size", &_map->key_size);
258 [ - + ]: 1106 : if (r)
259 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.key_size");
260 : :
261 : 1106 : r = bf_rpack_kv_u64(node, "value_size", &_map->value_size);
262 [ - + ]: 1106 : if (r)
263 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.value_size");
264 : :
265 : 1106 : r = bf_rpack_kv_u64(node, "n_elems", &_map->n_elems);
266 [ - + ]: 1106 : if (r)
267 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_map.n_elems");
268 [ - + ]: 1106 : if (_map->n_elems == 0)
269 [ # # ]: 0 : return bf_err_r(-EINVAL, "bf_map should not have 0 elements");
270 : :
271 : 1106 : r = bf_bpf_obj_get(_map->name, dir_fd, &_map->fd);
272 [ - + ]: 1106 : if (r < 0)
273 [ # # ]: 0 : return bf_err_r(r, "failed to open pinned BPF map '%s'", _map->name);
274 : :
275 : 1106 : *map = TAKE_PTR(_map);
276 : :
277 : 1106 : return 0;
278 : : }
279 : :
280 : 15270 : void bf_map_free(struct bf_map **map)
281 : : {
282 : : assert(map);
283 : :
284 [ + + ]: 15270 : if (!*map)
285 : : return;
286 : :
287 : 4904 : closep(&(*map)->fd);
288 : : freep((void *)map);
289 : : }
290 : :
291 : 2677 : int bf_map_pack(const struct bf_map *map, bf_wpack_t *pack)
292 : : {
293 : : assert(map);
294 : : assert(pack);
295 : :
296 : 2677 : bf_wpack_kv_str(pack, "name", map->name);
297 : 2677 : bf_wpack_kv_enum(pack, "type", map->type);
298 : 2677 : bf_wpack_kv_enum(pack, "bpf_type", map->bpf_type);
299 : 2677 : bf_wpack_kv_u64(pack, "key_size", map->key_size);
300 : 2677 : bf_wpack_kv_u64(pack, "value_size", map->value_size);
301 : 2677 : bf_wpack_kv_u64(pack, "n_elems", map->n_elems);
302 : :
303 [ - + ]: 2677 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
304 : : }
305 : :
306 : : static const char *_bf_map_type_to_str(enum bf_map_type type)
307 : : {
308 : : static const char *type_strs[] = {
309 : : [BF_MAP_TYPE_COUNTERS] = "BF_MAP_TYPE_COUNTERS",
310 : : [BF_MAP_TYPE_PRINTER] = "BF_MAP_TYPE_PRINTER",
311 : : [BF_MAP_TYPE_LOG] = "BF_MAP_TYPE_LOG",
312 : : [BF_MAP_TYPE_SET] = "BF_MAP_TYPE_SET",
313 : : [BF_MAP_TYPE_CTX] = "BF_MAP_TYPE_CTX",
314 : : };
315 : :
316 : : static_assert_enum_mapping(type_strs, _BF_MAP_TYPE_MAX);
317 : : assert(0 <= type && type < _BF_MAP_TYPE_MAX);
318 : :
319 : 0 : return type_strs[type];
320 : : }
321 : :
322 : : static const char *_bf_bpf_type_to_str(enum bf_bpf_map_type type)
323 : : {
324 : : static const char *type_strs[] = {
325 : : [BF_BPF_MAP_TYPE_HASH] = "BF_BPF_MAP_TYPE_HASH",
326 : : [BF_BPF_MAP_TYPE_ARRAY] = "BF_BPF_MAP_TYPE_ARRAY",
327 : : [BF_BPF_MAP_TYPE_LPM_TRIE] = "BF_BPF_MAP_TYPE_LPM_TRIE",
328 : : [BF_BPF_MAP_TYPE_RINGBUF] = "BF_BPF_MAP_TYPE_RINGBUF",
329 : : };
330 : :
331 : 0 : return type_strs[type] ? type_strs[type] : "<no mapping>";
332 : : }
333 : :
334 : 35 : void bf_map_dump(const struct bf_map *map, prefix_t *prefix)
335 : : {
336 : : assert(map);
337 : : assert(prefix);
338 : :
339 [ - + ]: 35 : DUMP(prefix, "struct bf_map at %p", map);
340 : :
341 : 35 : bf_dump_prefix_push(prefix);
342 [ - + ]: 35 : DUMP(prefix, "type: %s", _bf_map_type_to_str(map->type));
343 [ - + ]: 35 : DUMP(prefix, "name: %s", map->name);
344 [ - + - - ]: 35 : DUMP(prefix, "bpf_type: %s", _bf_bpf_type_to_str(map->bpf_type));
345 [ - + ]: 35 : DUMP(prefix, "key_size: %lu", map->key_size);
346 [ - + ]: 35 : DUMP(prefix, "value_size: %lu", map->value_size);
347 [ - + ]: 35 : DUMP(prefix, "n_elems: %lu", map->n_elems);
348 : :
349 : 35 : bf_dump_prefix_last(prefix);
350 [ - + ]: 35 : DUMP(prefix, "fd: %d", map->fd);
351 : :
352 : 35 : bf_dump_prefix_pop(prefix);
353 : 35 : }
354 : :
355 : 2612 : int bf_map_pin(const struct bf_map *map, int dir_fd)
356 : : {
357 : : int r;
358 : :
359 : : assert(map);
360 : :
361 : 2612 : r = bf_bpf_obj_pin(map->name, map->fd, dir_fd);
362 [ - + ]: 2612 : if (r < 0)
363 [ # # ]: 0 : return bf_err_r(r, "failed to pin BPF map '%s'", map->name);
364 : :
365 : : return 0;
366 : : }
367 : :
368 : 2478 : void bf_map_unpin(const struct bf_map *map, int dir_fd)
369 : : {
370 : : int r;
371 : :
372 : : assert(map);
373 : :
374 : 2478 : r = unlinkat(dir_fd, map->name, 0);
375 [ - + - - ]: 2478 : if (r < 0 && errno != ENOENT) {
376 : : // Do not warn on ENOENT, we want the file to be gone!
377 [ # # ]: 0 : bf_warn_r(
378 : : errno,
379 : : "failed to unlink BPF map '%s', assuming the map is not pinned",
380 : : map->name);
381 : : }
382 : 2478 : }
383 : :
384 : 2342 : int bf_map_set_elem(const struct bf_map *map, void *key, void *value)
385 : : {
386 : : assert(map);
387 : : assert(key);
388 : : assert(value);
389 : :
390 : 2342 : return bf_bpf_map_update_elem(map->fd, key, value, 0);
391 : : }
|