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