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