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