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 
19 #include "bpf_util.h"
20 #include "bpf_elf.h"
21 #include "bpf_scm.h"
22 
23 #define BPF_DEFAULT_CMD	"/bin/sh"
24 
25 static char *argv_default[] = { BPF_DEFAULT_CMD, NULL };
26 
explain(void)27 static void explain(void)
28 {
29 	fprintf(stderr, "Usage: ... bpf [ import UDS_FILE ] [ run CMD ]\n");
30 	fprintf(stderr, "       ... bpf [ debug ]\n");
31 	fprintf(stderr, "       ... bpf [ graft MAP_FILE ] [ key KEY ]\n");
32 	fprintf(stderr, "          `... [ object-file OBJ_FILE ] [ type TYPE ] [ section NAME ] [ verbose ]\n");
33 	fprintf(stderr, "          `... [ object-pinned PROG_FILE ]\n");
34 	fprintf(stderr, "\n");
35 	fprintf(stderr, "Where UDS_FILE provides the name of a unix domain socket file\n");
36 	fprintf(stderr, "to import eBPF maps and the optional CMD denotes the command\n");
37 	fprintf(stderr, "to be executed (default: \'%s\').\n", BPF_DEFAULT_CMD);
38 	fprintf(stderr, "Where MAP_FILE points to a pinned map, OBJ_FILE to an object file\n");
39 	fprintf(stderr, "and PROG_FILE to a pinned program. TYPE can be {cls, act}, where\n");
40 	fprintf(stderr, "\'cls\' is default. KEY is optional and can be inferred from the\n");
41 	fprintf(stderr, "section name, otherwise it needs to be provided.\n");
42 }
43 
bpf_num_env_entries(void)44 static int bpf_num_env_entries(void)
45 {
46 	char **envp;
47 	int num;
48 
49 	for (num = 0, envp = environ; *envp != NULL; envp++)
50 		num++;
51 	return num;
52 }
53 
parse_bpf(struct exec_util * eu,int argc,char ** argv)54 static int parse_bpf(struct exec_util *eu, int argc, char **argv)
55 {
56 	char **argv_run = argv_default, **envp_run, *tmp;
57 	int ret, i, env_old, env_num, env_map;
58 	const char *bpf_uds_name = NULL;
59 	int fds[BPF_SCM_MAX_FDS] = {};
60 	struct bpf_map_aux aux = {};
61 
62 	if (argc == 0)
63 		return 0;
64 
65 	while (argc > 0) {
66 		if (matches(*argv, "run") == 0) {
67 			NEXT_ARG();
68 			argv_run = argv;
69 			break;
70 		} else if (matches(*argv, "import") == 0) {
71 			NEXT_ARG();
72 			bpf_uds_name = *argv;
73 		} else if (matches(*argv, "debug") == 0 ||
74 			   matches(*argv, "dbg") == 0) {
75 			if (bpf_trace_pipe())
76 				fprintf(stderr,
77 					"No trace pipe, tracefs not mounted?\n");
78 			return -1;
79 		} else if (matches(*argv, "graft") == 0) {
80 			const char *bpf_map_path;
81 			bool has_key = false;
82 			uint32_t key;
83 
84 			NEXT_ARG();
85 			bpf_map_path = *argv;
86 			NEXT_ARG();
87 			if (matches(*argv, "key") == 0) {
88 				NEXT_ARG();
89 				if (get_unsigned(&key, *argv, 0)) {
90 					fprintf(stderr, "Illegal \"key\"\n");
91 					return -1;
92 				}
93 				has_key = true;
94 				NEXT_ARG();
95 			}
96 			return bpf_graft_map(bpf_map_path, has_key ?
97 					     &key : NULL, argc, argv);
98 		} else {
99 			explain();
100 			return -1;
101 		}
102 
103 		NEXT_ARG_FWD();
104 	}
105 
106 	if (!bpf_uds_name) {
107 		fprintf(stderr, "bpf: No import parameter provided!\n");
108 		explain();
109 		return -1;
110 	}
111 
112 	if (argv_run != argv_default && argc == 0) {
113 		fprintf(stderr, "bpf: No run command provided!\n");
114 		explain();
115 		return -1;
116 	}
117 
118 	ret = bpf_recv_map_fds(bpf_uds_name, fds, &aux, ARRAY_SIZE(fds));
119 	if (ret < 0) {
120 		fprintf(stderr, "bpf: Could not receive fds!\n");
121 		return -1;
122 	}
123 
124 	if (aux.num_ent == 0) {
125 		envp_run = environ;
126 		goto out;
127 	}
128 
129 	env_old = bpf_num_env_entries();
130 	env_num = env_old + aux.num_ent + 2;
131 	env_map = env_old + 1;
132 
133 	envp_run = malloc(sizeof(*envp_run) * env_num);
134 	if (!envp_run) {
135 		fprintf(stderr, "bpf: No memory left to allocate env!\n");
136 		goto err;
137 	}
138 
139 	for (i = 0; i < env_old; i++)
140 		envp_run[i] = environ[i];
141 
142 	ret = asprintf(&tmp, "BPF_NUM_MAPS=%u", aux.num_ent);
143 	if (ret < 0)
144 		goto err_free;
145 
146 	envp_run[env_old] = tmp;
147 
148 	for (i = env_map; i < env_num - 1; i++) {
149 		ret = asprintf(&tmp, "BPF_MAP%u=%u",
150 			       aux.ent[i - env_map].id,
151 			       fds[i - env_map]);
152 		if (ret < 0)
153 			goto err_free_env;
154 
155 		envp_run[i] = tmp;
156 	}
157 
158 	envp_run[env_num - 1] = NULL;
159 out:
160 	return execvpe(argv_run[0], argv_run, envp_run);
161 
162 err_free_env:
163 	for (--i; i >= env_old; i--)
164 		free(envp_run[i]);
165 err_free:
166 	free(envp_run);
167 err:
168 	for (i = 0; i < aux.num_ent; i++)
169 		close(fds[i]);
170 	return -1;
171 }
172 
173 struct exec_util bpf_exec_util = {
174 	.id		= "bpf",
175 	.parse_eopt	= parse_bpf,
176 };
177