summaryrefslogtreecommitdiffstats
path: root/tc/e_bpf.c
blob: 517ee5b35734d8a417a83eb104edd363973cfe4a (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
/*
 * e_bpf.c	BPF exec proxy
 *
 *		This program is free software; you can distribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 *
 * Authors:	Daniel Borkmann <daniel@iogearbox.net>
 */

#include <stdio.h>
#include <unistd.h>

#include "utils.h"

#include "tc_util.h"

#include "bpf_util.h"
#include "bpf_elf.h"
#include "bpf_scm.h"

#define BPF_DEFAULT_CMD	"/bin/sh"

static char *argv_default[] = { BPF_DEFAULT_CMD, NULL };

static void explain(void)
{
	fprintf(stderr,
		"Usage: ... bpf [ import UDS_FILE ] [ run CMD ]\n"
		"       ... bpf [ debug ]\n"
		"       ... bpf [ graft MAP_FILE ] [ key KEY ]\n"
		"          `... [ object-file OBJ_FILE ] [ type TYPE ] [ section NAME ] [ verbose ]\n"
		"          `... [ object-pinned PROG_FILE ]\n"
		"\n"
		"Where UDS_FILE provides the name of a unix domain socket file\n"
		"to import eBPF maps and the optional CMD denotes the command\n"
		"to be executed (default: \'%s\').\n"
		"Where MAP_FILE points to a pinned map, OBJ_FILE to an object file\n"
		"and PROG_FILE to a pinned program. TYPE can be {cls, act}, where\n"
		"\'cls\' is default. KEY is optional and can be inferred from the\n"
		"section name, otherwise it needs to be provided.\n",
		BPF_DEFAULT_CMD);
}

static int bpf_num_env_entries(void)
{
	char **envp;
	int num;

	for (num = 0, envp = environ; *envp != NULL; envp++)
		num++;
	return num;
}

static int parse_bpf(struct exec_util *eu, int argc, char **argv)
{
	char **argv_run = argv_default, **envp_run, *tmp;
	int ret, i, env_old, env_num, env_map;
	const char *bpf_uds_name = NULL;
	int fds[BPF_SCM_MAX_FDS] = {};
	struct bpf_map_aux aux = {};

	if (argc == 0)
		return 0;

	while (argc > 0) {
		if (matches(*argv, "run") == 0) {
			NEXT_ARG();
			argv_run = argv;
			break;
		} else if (matches(*argv, "import") == 0) {
			NEXT_ARG();
			bpf_uds_name = *argv;
		} else if (matches(*argv, "debug") == 0 ||
			   matches(*argv, "dbg") == 0) {
			if (bpf_trace_pipe())
				fprintf(stderr,
					"No trace pipe, tracefs not mounted?\n");
			return -1;
		} else if (matches(*argv, "graft") == 0) {
			const char *bpf_map_path;
			bool has_key = false;
			uint32_t key;

			NEXT_ARG();
			bpf_map_path = *argv;
			NEXT_ARG();
			if (matches(*argv, "key") == 0) {
				NEXT_ARG();
				if (get_unsigned(&key, *argv, 0)) {
					fprintf(stderr, "Illegal \"key\"\n");
					return -1;
				}
				has_key = true;
				NEXT_ARG();
			}
			return bpf_graft_map(bpf_map_path, has_key ?
					     &key : NULL, argc, argv);
		} else {
			explain();
			return -1;
		}

		NEXT_ARG_FWD();
	}

	if (!bpf_uds_name) {
		fprintf(stderr, "bpf: No import parameter provided!\n");
		explain();
		return -1;
	}

	if (argv_run != argv_default && argc == 0) {
		fprintf(stderr, "bpf: No run command provided!\n");
		explain();
		return -1;
	}

	ret = bpf_recv_map_fds(bpf_uds_name, fds, &aux, ARRAY_SIZE(fds));
	if (ret < 0) {
		fprintf(stderr, "bpf: Could not receive fds!\n");
		return -1;
	}

	if (aux.num_ent == 0) {
		envp_run = environ;
		goto out;
	}

	env_old = bpf_num_env_entries();
	env_num = env_old + aux.num_ent + 2;
	env_map = env_old + 1;

	envp_run = malloc(sizeof(*envp_run) * env_num);
	if (!envp_run) {
		fprintf(stderr, "bpf: No memory left to allocate env!\n");
		goto err;
	}

	for (i = 0; i < env_old; i++)
		envp_run[i] = environ[i];

	ret = asprintf(&tmp, "BPF_NUM_MAPS=%u", aux.num_ent);
	if (ret < 0)
		goto err_free;

	envp_run[env_old] = tmp;

	for (i = env_map; i < env_num - 1; i++) {
		ret = asprintf(&tmp, "BPF_MAP%u=%u",
			       aux.ent[i - env_map].id,
			       fds[i - env_map]);
		if (ret < 0)
			goto err_free_env;

		envp_run[i] = tmp;
	}

	envp_run[env_num - 1] = NULL;
out:
	ret = execvpe(argv_run[0], argv_run, envp_run);
	free(envp_run);
	return ret;

err_free_env:
	for (--i; i >= env_old; i--)
		free(envp_run[i]);
err_free:
	free(envp_run);
err:
	for (i = 0; i < aux.num_ent; i++)
		close(fds[i]);
	return -1;
}

struct exec_util bpf_exec_util = {
	.id		= "bpf",
	.parse_eopt	= parse_bpf,
};