summaryrefslogtreecommitdiffstats
path: root/lib/bpf_libbpf.c
blob: e1c211a1b3b722b64c307d87bf3205dba34555cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * bpf_libbpf.c		BPF code relay on libbpf
 * Authors:		Hangbin Liu <haliu@redhat.com>
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>

#include <libelf.h>
#include <gelf.h>

#include <bpf/libbpf.h>
#include <bpf/bpf.h>

#include "bpf_util.h"

static int __attribute__((format(printf, 2, 0)))
verbose_print(enum libbpf_print_level level, const char *format, va_list args)
{
	return vfprintf(stderr, format, args);
}

static int __attribute__((format(printf, 2, 0)))
silent_print(enum libbpf_print_level level, const char *format, va_list args)
{
	if (level > LIBBPF_WARN)
		return 0;

	/* Skip warning from bpf_object__init_user_maps() for legacy maps */
	if (strstr(format, "has unrecognized, non-zero options"))
		return 0;

	return vfprintf(stderr, format, args);
}

static const char *get_bpf_program__section_name(const struct bpf_program *prog)
{
#ifdef HAVE_LIBBPF_SECTION_NAME
	return bpf_program__section_name(prog);
#else
	return bpf_program__title(prog, false);
#endif
}

static int create_map(const char *name, struct bpf_elf_map *map,
		      __u32 ifindex, int inner_fd)
{
	union bpf_attr attr = {};

	attr.map_type = map->type;
	strlcpy(attr.map_name, name, sizeof(attr.map_name));
	attr.map_flags = map->flags;
	attr.key_size = map->size_key;
	attr.value_size = map->size_value;
	attr.max_entries = map->max_elem;
	attr.map_ifindex = ifindex;
	attr.inner_map_fd = inner_fd;

	return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
}

static int create_map_in_map(struct bpf_object *obj, struct bpf_map *map,
			     struct bpf_elf_map *elf_map, int inner_fd,
			     bool *reuse_pin_map)
{
	char pathname[PATH_MAX];
	const char *map_name;
	bool pin_map = false;
	int map_fd, ret = 0;

	map_name = bpf_map__name(map);

	if (iproute2_is_pin_map(map_name, pathname)) {
		pin_map = true;

		/* Check if there already has a pinned map */
		map_fd = bpf_obj_get(pathname);
		if (map_fd > 0) {
			if (reuse_pin_map)
				*reuse_pin_map = true;
			close(map_fd);
			return bpf_map__set_pin_path(map, pathname);
		}
	}

	map_fd = create_map(map_name, elf_map, bpf_map__ifindex(map), inner_fd);
	if (map_fd < 0) {
		fprintf(stderr, "create map %s failed\n", map_name);
		return map_fd;
	}

	ret = bpf_map__reuse_fd(map, map_fd);
	if (ret < 0) {
		fprintf(stderr, "map %s reuse fd failed\n", map_name);
		goto err_out;
	}

	if (pin_map) {
		ret = bpf_map__set_pin_path(map, pathname);
		if (ret < 0)
			goto err_out;
	}

	return 0;
err_out:
	close(map_fd);
	return ret;
}

static int
handle_legacy_map_in_map(struct bpf_object *obj, struct bpf_map *inner_map,
			 const char *inner_map_name)
{
	int inner_fd, outer_fd, inner_idx, ret = 0;
	struct bpf_elf_map imap, omap;
	struct bpf_map *outer_map;
	/* What's the size limit of map name? */
	char outer_map_name[128];
	bool reuse_pin_map = false;

	/* Deal with map-in-map */
	if (iproute2_is_map_in_map(inner_map_name, &imap, &omap, outer_map_name)) {
		ret = create_map_in_map(obj, inner_map, &imap, -1, NULL);
		if (ret < 0)
			return ret;

		inner_fd = bpf_map__fd(inner_map);
		outer_map = bpf_object__find_map_by_name(obj, outer_map_name);
		ret = create_map_in_map(obj, outer_map, &omap, inner_fd, &reuse_pin_map);
		if (ret < 0)
			return ret;

		if (!reuse_pin_map) {
			inner_idx = imap.inner_idx;
			outer_fd = bpf_map__fd(outer_map);
			ret = bpf_map_update_elem(outer_fd, &inner_idx, &inner_fd, 0);
			if (ret < 0)
				fprintf(stderr, "Cannot update inner_idx into outer_map\n");
		}
	}

	return ret;
}

static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj,
				  struct bpf_map **pmap)
{
	unsigned int map_id, key_id;
	const char *sec_name;
	struct bpf_map *map;
	char map_name[128];
	int ret;

	/* Handle iproute2 tail call */
	sec_name = get_bpf_program__section_name(prog);
	ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
	if (ret != 2)
		return -1;

	ret = iproute2_find_map_name_by_id(map_id, map_name);
	if (ret < 0) {
		fprintf(stderr, "unable to find map id %u for tail call\n", map_id);
		return ret;
	}

	map = bpf_object__find_map_by_name(obj, map_name);
	if (!map)
		return -1;

	if (pmap)
		*pmap = map;

	return 0;
}

static int update_legacy_tail_call_maps(struct bpf_object *obj)
{
	int prog_fd, map_fd, ret = 0;
	unsigned int map_id, key_id;
	struct bpf_program *prog;
	const char *sec_name;
	struct bpf_map *map;

	bpf_object__for_each_program(prog, obj) {
		/* load_bpf_object has already verified find_legacy_tail_calls
		 * succeeds when it should
		 */
		if (find_legacy_tail_calls(prog, obj, &map) < 0)
			continue;

		prog_fd = bpf_program__fd(prog);
		if (prog_fd < 0)
			continue;

		sec_name = get_bpf_program__section_name(prog);
		ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
		if (ret != 2)
			continue;

		map_fd = bpf_map__fd(map);
		ret = bpf_map_update_elem(map_fd, &key_id, &prog_fd, 0);
		if (ret < 0) {
			fprintf(stderr, "Cannot update map key for tail call!\n");
			return ret;
		}
	}

	return 0;
}

static int handle_legacy_maps(struct bpf_object *obj)
{
	char pathname[PATH_MAX];
	struct bpf_map *map;
	const char *map_name;
	int map_fd, ret = 0;

	bpf_object__for_each_map(map, obj) {
		map_name = bpf_map__name(map);

		ret = handle_legacy_map_in_map(obj, map, map_name);
		if (ret)
			return ret;

		/* If it is a iproute2 legacy pin maps, just set pin path
		 * and let bpf_object__load() to deal with the map creation.
		 * We need to ignore map-in-maps which have pinned maps manually
		 */
		map_fd = bpf_map__fd(map);
		if (map_fd < 0 && iproute2_is_pin_map(map_name, pathname)) {
			ret = bpf_map__set_pin_path(map, pathname);
			if (ret) {
				fprintf(stderr, "map '%s': couldn't set pin path.\n", map_name);
				break;
			}
		}

	}

	return ret;
}

static bool bpf_map_is_offload_neutral(const struct bpf_map *map)
{
	return bpf_map__type(map) == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
}

static bool find_prog_to_attach(struct bpf_program *prog,
				struct bpf_program *exist_prog,
				const char *section, const char *prog_name)
{
	if (exist_prog)
		return false;

	/* We have default section name 'prog'. So do not check
	 * section name if there already has program name.
	 */
	if (prog_name)
		return !strcmp(bpf_program__name(prog), prog_name);
	else
		return !strcmp(get_bpf_program__section_name(prog), section);
}

static int load_bpf_object(struct bpf_cfg_in *cfg)
{
	struct bpf_program *p, *prog = NULL;
	struct bpf_object *obj;
	char root_path[PATH_MAX];
	struct bpf_map *map;
	int prog_fd, ret = 0;

	ret = iproute2_get_root_path(root_path, PATH_MAX);
	if (ret)
		return ret;

	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
			.relaxed_maps = true,
			.pin_root_path = root_path,
	);

	obj = bpf_object__open_file(cfg->object, &open_opts);
	if (libbpf_get_error(obj)) {
		fprintf(stderr, "ERROR: opening BPF object file failed\n");
		return -ENOENT;
	}

	bpf_object__for_each_program(p, obj) {
		bool prog_to_attach = find_prog_to_attach(p, prog,
							  cfg->section,
							  cfg->prog_name);

		/* Only load the programs that will either be subsequently
		 * attached or inserted into a tail call map */
		if (find_legacy_tail_calls(p, obj, NULL) < 0 &&
		    !prog_to_attach) {
			ret = bpf_program__set_autoload(p, false);
			if (ret)
				return -EINVAL;
			continue;
		}

		bpf_program__set_type(p, cfg->type);
		bpf_program__set_ifindex(p, cfg->ifindex);

		if (prog_to_attach)
			prog = p;
	}

	bpf_object__for_each_map(map, obj) {
		if (!bpf_map_is_offload_neutral(map))
			bpf_map__set_ifindex(map, cfg->ifindex);
	}

	if (!prog) {
		if (cfg->prog_name)
			fprintf(stderr, "object file doesn't contain prog %s\n", cfg->prog_name);
		else
			fprintf(stderr, "object file doesn't contain sec %s\n", cfg->section);
		return -ENOENT;
	}

	/* Handle iproute2 legacy pin maps and map-in-maps */
	ret = handle_legacy_maps(obj);
	if (ret)
		goto unload_obj;

	ret = bpf_object__load(obj);
	if (ret)
		goto unload_obj;

	ret = update_legacy_tail_call_maps(obj);
	if (ret)
		goto unload_obj;

	prog_fd = fcntl(bpf_program__fd(prog), F_DUPFD_CLOEXEC, 1);
	if (prog_fd < 0)
		ret = -errno;
	else
		cfg->prog_fd = prog_fd;

unload_obj:
	/* Close obj as we don't need it */
	bpf_object__close(obj);
	return ret;
}

/* Load ebpf and return prog fd */
int iproute2_load_libbpf(struct bpf_cfg_in *cfg)
{
	int ret = 0;

	if (cfg->verbose)
		libbpf_set_print(verbose_print);
	else
		libbpf_set_print(silent_print);

	ret = iproute2_bpf_elf_ctx_init(cfg);
	if (ret < 0) {
		fprintf(stderr, "Cannot initialize ELF context!\n");
		return ret;
	}

	ret = iproute2_bpf_fetch_ancillary();
	if (ret < 0) {
		fprintf(stderr, "Error fetching ELF ancillary data!\n");
		return ret;
	}

	ret = load_bpf_object(cfg);
	if (ret)
		return ret;

	return cfg->prog_fd;
}