Home | History | Annotate | Download | only in bpf
      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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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