• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * eBPF user space agent part
3  *
4  * Simple, _self-contained_ user space agent for the eBPF kernel
5  * ebpf_prog.c program, which gets all map fds passed from tc via unix
6  * domain socket in one transaction and can thus keep referencing
7  * them from user space in order to read out (or possibly modify)
8  * map data. Here, just as a minimal example to display counters.
9  *
10  * The agent only uses the bpf(2) syscall API to read or possibly
11  * write to eBPF maps, it doesn't need to be aware of the low-level
12  * bytecode parts and/or ELF parsing bits.
13  *
14  * ! For more details, see header comment in bpf_prog.c !
15  *
16  * gcc bpf_agent.c -o bpf_agent -Wall -O2
17  *
18  * For example, a more complex user space agent could run on each
19  * host, reading and writing into eBPF maps used by tc classifier
20  * and actions. It would thus allow for implementing a distributed
21  * tc architecture, for example, which would push down central
22  * policies into eBPF maps, and thus altering run-time behaviour.
23  *
24  *   -- Happy eBPF hacking! ;)
25  */
26 
27 #define _GNU_SOURCE
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <stdint.h>
35 #include <assert.h>
36 
37 #include <sys/un.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/socket.h>
41 
42 /* Just some misc macros as min(), offsetof(), etc. */
43 #include "../../include/utils.h"
44 /* Common code from fd passing. */
45 #include "../../include/bpf_scm.h"
46 /* Common, shared definitions with ebpf_prog.c */
47 #include "bpf_shared.h"
48 /* Mini syscall wrapper */
49 #include "bpf_sys.h"
50 
bpf_dump_drops(int fd)51 static void bpf_dump_drops(int fd)
52 {
53 	int cpu, max;
54 
55 	max = sysconf(_SC_NPROCESSORS_ONLN);
56 
57 	printf(" `- number of drops:");
58 	for (cpu = 0; cpu < max; cpu++) {
59 		long drops;
60 
61 		assert(bpf_lookup_elem(fd, &cpu, &drops) == 0);
62 		printf("\tcpu%d: %5ld", cpu, drops);
63 	}
64 	printf("\n");
65 }
66 
bpf_dump_queue(int fd)67 static void bpf_dump_queue(int fd)
68 {
69 	/* Just for the same of the example. */
70 	int max_queue = 4, i;
71 
72 	printf("  | nic queues:");
73 	for (i = 0; i < max_queue; i++) {
74 		struct count_queue cq;
75 		int ret;
76 
77 		memset(&cq, 0, sizeof(cq));
78 		ret = bpf_lookup_elem(fd, &i, &cq);
79 		assert(ret == 0 || (ret < 0 && errno == ENOENT));
80 
81 		printf("\tq%d:[pkts: %ld, mis: %ld]",
82 		       i, cq.total, cq.mismatch);
83 	}
84 	printf("\n");
85 }
86 
bpf_dump_proto(int fd)87 static void bpf_dump_proto(int fd)
88 {
89 	uint8_t protos[] = { IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP };
90 	char *names[] = { "tcp", "udp", "icmp" };
91 	int i;
92 
93 	printf("  ` protos:");
94 	for (i = 0; i < ARRAY_SIZE(protos); i++) {
95 		struct count_tuple ct;
96 		int ret;
97 
98 		memset(&ct, 0, sizeof(ct));
99 		ret = bpf_lookup_elem(fd, &protos[i], &ct);
100 		assert(ret == 0 || (ret < 0 && errno == ENOENT));
101 
102 		printf("\t%s:[pkts: %ld, bytes: %ld]",
103 		       names[i], ct.packets, ct.bytes);
104 	}
105 	printf("\n");
106 }
107 
bpf_dump_map_data(int * tfd)108 static void bpf_dump_map_data(int *tfd)
109 {
110 	int i;
111 
112 	for (i = 0; i < 30; i++) {
113 		const int period = 5;
114 
115 		printf("data, period: %dsec\n", period);
116 
117 		bpf_dump_drops(tfd[BPF_MAP_ID_DROPS]);
118 		bpf_dump_queue(tfd[BPF_MAP_ID_QUEUE]);
119 		bpf_dump_proto(tfd[BPF_MAP_ID_PROTO]);
120 
121 		sleep(period);
122 	}
123 }
124 
bpf_info_loop(int * fds,struct bpf_map_aux * aux)125 static void bpf_info_loop(int *fds, struct bpf_map_aux *aux)
126 {
127 	int i, tfd[BPF_MAP_ID_MAX];
128 
129 	printf("ver: %d\nobj: %s\ndev: %lu\nino: %lu\nmaps: %u\n",
130 	       aux->uds_ver, aux->obj_name, aux->obj_st.st_dev,
131 	       aux->obj_st.st_ino, aux->num_ent);
132 
133 	for (i = 0; i < aux->num_ent; i++) {
134 		printf("map%d:\n", i);
135 		printf(" `- fd: %u\n", fds[i]);
136 		printf("  | serial: %u\n", aux->ent[i].id);
137 		printf("  | type: %u\n", aux->ent[i].type);
138 		printf("  | max elem: %u\n", aux->ent[i].max_elem);
139 		printf("  | size key: %u\n", aux->ent[i].size_key);
140 		printf("  ` size val: %u\n", aux->ent[i].size_value);
141 
142 		tfd[aux->ent[i].id] = fds[i];
143 	}
144 
145 	bpf_dump_map_data(tfd);
146 }
147 
bpf_map_get_from_env(int * tfd)148 static void bpf_map_get_from_env(int *tfd)
149 {
150 	char key[64], *val;
151 	int i;
152 
153 	for (i = 0; i < BPF_MAP_ID_MAX; i++) {
154 		memset(key, 0, sizeof(key));
155 		snprintf(key, sizeof(key), "BPF_MAP%d", i);
156 
157 		val = getenv(key);
158 		assert(val != NULL);
159 
160 		tfd[i] = atoi(val);
161 	}
162 }
163 
bpf_map_set_recv(int fd,int * fds,struct bpf_map_aux * aux,unsigned int entries)164 static int bpf_map_set_recv(int fd, int *fds,  struct bpf_map_aux *aux,
165 			    unsigned int entries)
166 {
167 	struct bpf_map_set_msg msg;
168 	int *cmsg_buf, min_fd, i;
169 	char *amsg_buf, *mmsg_buf;
170 
171 	cmsg_buf = bpf_map_set_init(&msg, NULL, 0);
172 	amsg_buf = (char *)msg.aux.ent;
173 	mmsg_buf = (char *)&msg.aux;
174 
175 	for (i = 0; i < entries; i += min_fd) {
176 		struct cmsghdr *cmsg;
177 		int ret;
178 
179 		min_fd = min(BPF_SCM_MAX_FDS * 1U, entries - i);
180 
181 		bpf_map_set_init_single(&msg, min_fd);
182 
183 		ret = recvmsg(fd, &msg.hdr, 0);
184 		if (ret <= 0)
185 			return ret ? : -1;
186 
187 		cmsg = CMSG_FIRSTHDR(&msg.hdr);
188 		if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS)
189 			return -EINVAL;
190 		if (msg.hdr.msg_flags & MSG_CTRUNC)
191 			return -EIO;
192 
193 		min_fd = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(fd);
194 		if (min_fd > entries || min_fd <= 0)
195 			return -1;
196 
197 		memcpy(&fds[i], cmsg_buf, sizeof(fds[0]) * min_fd);
198 		memcpy(&aux->ent[i], amsg_buf, sizeof(aux->ent[0]) * min_fd);
199 		memcpy(aux, mmsg_buf, offsetof(struct bpf_map_aux, ent));
200 
201 		if (i + min_fd == aux->num_ent)
202 			break;
203 	}
204 
205 	return 0;
206 }
207 
main(int argc,char ** argv)208 int main(int argc, char **argv)
209 {
210 	int fds[BPF_SCM_MAX_FDS];
211 	struct bpf_map_aux aux;
212 	struct sockaddr_un addr;
213 	int fd, ret, i;
214 
215 	/* When arguments are being passed, we take it as a path
216 	 * to a Unix domain socket, otherwise we grab the fds
217 	 * from the environment to demonstrate both possibilities.
218 	 */
219 	if (argc == 1) {
220 		int tfd[BPF_MAP_ID_MAX];
221 
222 		bpf_map_get_from_env(tfd);
223 		bpf_dump_map_data(tfd);
224 
225 		return 0;
226 	}
227 
228 	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
229 	if (fd < 0) {
230 		fprintf(stderr, "Cannot open socket: %s\n",
231 			strerror(errno));
232 		exit(1);
233 	}
234 
235 	memset(&addr, 0, sizeof(addr));
236 	addr.sun_family = AF_UNIX;
237 	strncpy(addr.sun_path, argv[argc - 1], sizeof(addr.sun_path));
238 
239 	ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
240 	if (ret < 0) {
241 		fprintf(stderr, "Cannot bind to socket: %s\n",
242 			strerror(errno));
243 		exit(1);
244 	}
245 
246 	memset(fds, 0, sizeof(fds));
247 	memset(&aux, 0, sizeof(aux));
248 
249 	ret = bpf_map_set_recv(fd, fds, &aux, BPF_SCM_MAX_FDS);
250 	if (ret >= 0)
251 		bpf_info_loop(fds, &aux);
252 
253 	for (i = 0; i < aux.num_ent; i++)
254 		close(fds[i]);
255 
256 	close(fd);
257 	return 0;
258 }
259