e_bpf.c revision 4bd624467bc6f8f6e8b4c676f3dd8ae7593fbe70
1/*
2 * e_bpf.c	BPF exec proxy
3 *
4 *		This program is free software; you can distribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:	Daniel Borkmann <daniel@iogearbox.net>
10 */
11
12#include <stdio.h>
13#include <unistd.h>
14
15#include "utils.h"
16
17#include "tc_util.h"
18#include "tc_bpf.h"
19
20#include "bpf_elf.h"
21#include "bpf_scm.h"
22
23#define BPF_DEFAULT_CMD	"/bin/sh"
24
25static char *argv_default[] = { BPF_DEFAULT_CMD, NULL };
26
27static void explain(void)
28{
29	fprintf(stderr, "Usage: ... bpf [ import UDS_FILE ] [ run CMD ]\n\n");
30	fprintf(stderr, "Where UDS_FILE provides the name of a unix domain socket file\n");
31	fprintf(stderr, "to import eBPF maps and the optional CMD denotes the command\n");
32	fprintf(stderr, "to be executed (default: \'%s\').\n", BPF_DEFAULT_CMD);
33}
34
35static int bpf_num_env_entries(void)
36{
37	char **envp;
38	int num;
39
40	for (num = 0, envp = environ; *envp != NULL; envp++)
41		num++;
42	return num;
43}
44
45static int parse_bpf(struct exec_util *eu, int argc, char **argv)
46{
47	char **argv_run = argv_default, **envp_run, *tmp;
48	int ret, i, env_old, env_num, env_map;
49	const char *bpf_uds_name = NULL;
50	int fds[BPF_SCM_MAX_FDS];
51	struct bpf_map_aux aux;
52
53	if (argc == 0)
54		return 0;
55
56	while (argc > 0) {
57		if (matches(*argv, "run") == 0) {
58			NEXT_ARG();
59			argv_run = argv;
60			break;
61		} else if (matches(*argv, "import") == 0 ||
62			   matches(*argv, "imp") == 0) {
63			NEXT_ARG();
64			bpf_uds_name = *argv;
65		} else {
66			explain();
67			return -1;
68		}
69
70		argc--;
71		argv++;
72	}
73
74	if (!bpf_uds_name) {
75		fprintf(stderr, "bpf: No import parameter provided!\n");
76		explain();
77		return -1;
78	}
79
80	if (argv_run != argv_default && argc == 0) {
81		fprintf(stderr, "bpf: No run command provided!\n");
82		explain();
83		return -1;
84	}
85
86	memset(fds, 0, sizeof(fds));
87	memset(&aux, 0, sizeof(aux));
88
89	ret = bpf_recv_map_fds(bpf_uds_name, fds, &aux, ARRAY_SIZE(fds));
90	if (ret < 0) {
91		fprintf(stderr, "bpf: Could not receive fds!\n");
92		return -1;
93	}
94
95	if (aux.num_ent == 0) {
96		envp_run = environ;
97		goto out;
98	}
99
100	env_old = bpf_num_env_entries();
101	env_num = env_old + aux.num_ent + 2;
102	env_map = env_old + 1;
103
104	envp_run = malloc(sizeof(*envp_run) * env_num);
105	if (!envp_run) {
106		fprintf(stderr, "bpf: No memory left to allocate env!\n");
107		goto err;
108	}
109
110	for (i = 0; i < env_old; i++)
111		envp_run[i] = environ[i];
112
113	ret = asprintf(&tmp, "BPF_NUM_MAPS=%u", aux.num_ent);
114	if (ret < 0)
115		goto err_free;
116
117	envp_run[env_old] = tmp;
118
119	for (i = env_map; i < env_num - 1; i++) {
120		ret = asprintf(&tmp, "BPF_MAP%u=%u",
121			       aux.ent[i - env_map].id,
122			       fds[i - env_map]);
123		if (ret < 0)
124			goto err_free_env;
125
126		envp_run[i] = tmp;
127	}
128
129	envp_run[env_num - 1] = NULL;
130out:
131	return execvpe(argv_run[0], argv_run, envp_run);
132
133err_free_env:
134	for (--i; i >= env_old; i--)
135		free(envp_run[i]);
136err_free:
137	free(envp_run);
138err:
139	for (i = 0; i < aux.num_ent; i++)
140		close(fds[i]);
141	return -1;
142}
143
144struct exec_util bpf_exec_util = {
145	.id = "bpf",
146	.parse_eopt = parse_bpf,
147};
148