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