blob: fefa294856d3b906b9750375e085284fd8baa405 [file] [log] [blame]
Brenden Blanco246b9422015-06-05 11:15:27 -07001/*
2 * Copyright (c) 2015 PLUMgrid, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Brenden Blancocd5cb412015-04-26 09:41:58 -070017#include <arpa/inet.h>
Brenden Blancoa94bd932015-04-26 00:56:42 -070018#include <errno.h>
Brenden Blancocd5cb412015-04-26 09:41:58 -070019#include <fcntl.h>
Brenden Blancocd5cb412015-04-26 09:41:58 -070020#include <linux/bpf.h>
21#include <linux/if_packet.h>
22#include <linux/pkt_cls.h>
23#include <linux/perf_event.h>
24#include <linux/rtnetlink.h>
25#include <linux/unistd.h>
26#include <linux/version.h>
Brenden Blancoa94bd932015-04-26 00:56:42 -070027#include <net/ethernet.h>
28#include <net/if.h>
Brenden Blancobb7200c2015-06-04 18:01:42 -070029#include <stdio.h>
Brenden Blancocd5cb412015-04-26 09:41:58 -070030#include <stdlib.h>
31#include <string.h>
32#include <sys/ioctl.h>
Brenden Blancobb7200c2015-06-04 18:01:42 -070033#include <unistd.h>
Brenden Blancocd5cb412015-04-26 09:41:58 -070034
Brenden Blancoa94bd932015-04-26 00:56:42 -070035#include "libbpf.h"
Brenden Blanco8207d102015-09-25 13:58:30 -070036#include "perf_reader.h"
Brenden Blancoa94bd932015-04-26 00:56:42 -070037
Brenden Blancof275d3d2015-07-06 23:41:23 -070038// TODO: remove these defines when linux-libc-dev exports them properly
39
40#ifndef __NR_bpf
41#define __NR_bpf 321
42#endif
43
44#ifndef SO_ATTACH_BPF
45#define SO_ATTACH_BPF 50
46#endif
47
48#ifndef PERF_EVENT_IOC_SET_BPF
49#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
50#endif
51
52#ifndef PERF_FLAG_FD_CLOEXEC
53#define PERF_FLAG_FD_CLOEXEC (1UL << 3)
54#endif
55
Brenden Blancoa94bd932015-04-26 00:56:42 -070056static __u64 ptr_to_u64(void *ptr)
57{
58 return (__u64) (unsigned long) ptr;
59}
60
61int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries)
62{
Brenden Blancofdc027c2015-09-03 11:49:54 -070063 union bpf_attr attr;
64 memset(&attr, 0, sizeof(attr));
65 attr.map_type = map_type;
66 attr.key_size = key_size;
67 attr.value_size = value_size;
68 attr.max_entries = max_entries;
Brenden Blancoa94bd932015-04-26 00:56:42 -070069
70 return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
71}
72
73int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags)
74{
Brenden Blancofdc027c2015-09-03 11:49:54 -070075 union bpf_attr attr;
76 memset(&attr, 0, sizeof(attr));
77 attr.map_fd = fd;
78 attr.key = ptr_to_u64(key);
79 attr.value = ptr_to_u64(value);
80 attr.flags = flags;
Brenden Blancoa94bd932015-04-26 00:56:42 -070081
82 return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
83}
84
85int bpf_lookup_elem(int fd, void *key, void *value)
86{
Brenden Blancofdc027c2015-09-03 11:49:54 -070087 union bpf_attr attr;
88 memset(&attr, 0, sizeof(attr));
89 attr.map_fd = fd;
90 attr.key = ptr_to_u64(key);
91 attr.value = ptr_to_u64(value);
Brenden Blancoa94bd932015-04-26 00:56:42 -070092
93 return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
94}
95
96int bpf_delete_elem(int fd, void *key)
97{
Brenden Blancofdc027c2015-09-03 11:49:54 -070098 union bpf_attr attr;
99 memset(&attr, 0, sizeof(attr));
100 attr.map_fd = fd;
101 attr.key = ptr_to_u64(key);
Brenden Blancoa94bd932015-04-26 00:56:42 -0700102
103 return syscall(__NR_bpf, BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
104}
105
106int bpf_get_next_key(int fd, void *key, void *next_key)
107{
Brenden Blancofdc027c2015-09-03 11:49:54 -0700108 union bpf_attr attr;
109 memset(&attr, 0, sizeof(attr));
110 attr.map_fd = fd;
111 attr.key = ptr_to_u64(key);
112 attr.next_key = ptr_to_u64(next_key);
Brenden Blancoa94bd932015-04-26 00:56:42 -0700113
114 return syscall(__NR_bpf, BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
115}
116
117#define ROUND_UP(x, n) (((x) + (n) - 1u) & ~((n) - 1u))
118
119char bpf_log_buf[LOG_BUF_SIZE];
120
121int bpf_prog_load(enum bpf_prog_type prog_type,
Brenden Blancocd5cb412015-04-26 09:41:58 -0700122 const struct bpf_insn *insns, int prog_len,
Brenden Blanco759029f2015-07-29 15:47:51 -0700123 const char *license, unsigned kern_version,
124 char *log_buf, unsigned log_buf_size)
Brenden Blancoa94bd932015-04-26 00:56:42 -0700125{
Brenden Blancofdc027c2015-09-03 11:49:54 -0700126 union bpf_attr attr;
127 memset(&attr, 0, sizeof(attr));
128 attr.prog_type = prog_type;
129 attr.insns = ptr_to_u64((void *) insns);
130 attr.insn_cnt = prog_len / sizeof(struct bpf_insn);
131 attr.license = ptr_to_u64((void *) license);
132 attr.log_buf = ptr_to_u64(log_buf);
133 attr.log_size = log_buf_size;
134 attr.log_level = log_buf ? 1 : 0;
Brenden Blancoa94bd932015-04-26 00:56:42 -0700135
Brenden Blanco7009b552015-05-26 11:48:17 -0700136 attr.kern_version = kern_version;
Brenden Blanco81a783a2015-08-24 23:42:42 -0700137 if (log_buf)
138 log_buf[0] = 0;
Brenden Blancoa94bd932015-04-26 00:56:42 -0700139
Brenden Blancocd5cb412015-04-26 09:41:58 -0700140 int ret = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
Brenden Blanco81a783a2015-08-24 23:42:42 -0700141 if (ret < 0 && !log_buf) {
142 // caller did not specify log_buf but failure should be printed,
143 // so call recursively and print the result to stderr
144 bpf_prog_load(prog_type, insns, prog_len, license, kern_version,
145 bpf_log_buf, LOG_BUF_SIZE);
Brenden Blancocd5cb412015-04-26 09:41:58 -0700146 fprintf(stderr, "bpf: %s\n%s\n", strerror(errno), bpf_log_buf);
147 }
148 return ret;
Brenden Blancoa94bd932015-04-26 00:56:42 -0700149}
150
151int bpf_open_raw_sock(const char *name)
152{
153 struct sockaddr_ll sll;
154 int sock;
155
156 sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL));
157 if (sock < 0) {
158 printf("cannot create raw socket\n");
159 return -1;
160 }
161
162 memset(&sll, 0, sizeof(sll));
163 sll.sll_family = AF_PACKET;
164 sll.sll_ifindex = if_nametoindex(name);
165 sll.sll_protocol = htons(ETH_P_ALL);
166 if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
167 printf("bind to %s: %s\n", name, strerror(errno));
168 close(sock);
169 return -1;
170 }
171
172 return sock;
173}
174
175int bpf_attach_socket(int sock, int prog) {
Brenden Blancoaf956732015-06-09 13:58:42 -0700176 return setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog, sizeof(prog));
Brenden Blancoa94bd932015-04-26 00:56:42 -0700177}
178
Brenden Blanco8207d102015-09-25 13:58:30 -0700179static int bpf_attach_tracing_event(int progfd, const char *event_path,
180 struct perf_reader *reader, int pid, int cpu, int group_fd) {
Brenden Blanco75982492015-11-06 10:43:05 -0800181 int efd = -1, pfd;
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800182 ssize_t bytes;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700183 char buf[256];
184 struct perf_event_attr attr = {};
185
186 snprintf(buf, sizeof(buf), "%s/id", event_path);
187 efd = open(buf, O_RDONLY, 0);
188 if (efd < 0) {
189 fprintf(stderr, "open(%s): %s\n", buf, strerror(errno));
Brenden Blanco75982492015-11-06 10:43:05 -0800190 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700191 }
192
193 bytes = read(efd, buf, sizeof(buf));
194 if (bytes <= 0 || bytes >= sizeof(buf)) {
195 fprintf(stderr, "read(%s): %s\n", buf, strerror(errno));
Brenden Blanco75982492015-11-06 10:43:05 -0800196 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700197 }
198 buf[bytes] = '\0';
199 attr.config = strtol(buf, NULL, 0);
200 attr.type = PERF_TYPE_TRACEPOINT;
Brenden Blanco8207d102015-09-25 13:58:30 -0700201 attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700202 attr.sample_period = 1;
203 attr.wakeup_events = 1;
204 pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, PERF_FLAG_FD_CLOEXEC);
205 if (pfd < 0) {
206 perror("perf_event_open");
Brenden Blanco75982492015-11-06 10:43:05 -0800207 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700208 }
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800209 perf_reader_set_fd(reader, pfd);
Brenden Blanco8207d102015-09-25 13:58:30 -0700210
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800211 if (perf_reader_mmap(reader, attr.type, attr.sample_type) < 0)
Brenden Blanco75982492015-11-06 10:43:05 -0800212 goto error;
Brenden Blanco8207d102015-09-25 13:58:30 -0700213
Brenden Blancocd5cb412015-04-26 09:41:58 -0700214 if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, progfd) < 0) {
215 perror("ioctl(PERF_EVENT_IOC_SET_BPF)");
Brenden Blanco75982492015-11-06 10:43:05 -0800216 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700217 }
218 if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
219 perror("ioctl(PERF_EVENT_IOC_ENABLE)");
Brenden Blanco75982492015-11-06 10:43:05 -0800220 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700221 }
222
Brenden Blanco75982492015-11-06 10:43:05 -0800223 return 0;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700224
Brenden Blanco75982492015-11-06 10:43:05 -0800225error:
Brenden Blancocd5cb412015-04-26 09:41:58 -0700226 if (efd >= 0)
227 close(efd);
Brenden Blancocd5cb412015-04-26 09:41:58 -0700228
Brenden Blanco75982492015-11-06 10:43:05 -0800229 return -1;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700230}
231
Brenden Blanco8207d102015-09-25 13:58:30 -0700232void * bpf_attach_kprobe(int progfd, const char *event,
233 const char *event_desc, pid_t pid,
234 int cpu, int group_fd, perf_reader_cb cb,
235 void *cb_cookie) {
Brenden Blanco75982492015-11-06 10:43:05 -0800236 int kfd = -1;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700237 char buf[256];
Brenden Blanco8207d102015-09-25 13:58:30 -0700238 struct perf_reader *reader = NULL;
239
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800240 reader = perf_reader_new(cb, NULL, cb_cookie);
Brenden Blanco8207d102015-09-25 13:58:30 -0700241 if (!reader)
Brenden Blanco75982492015-11-06 10:43:05 -0800242 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700243
244 kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0);
245 if (kfd < 0) {
246 perror("open(kprobe_events)");
Brenden Blanco75982492015-11-06 10:43:05 -0800247 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700248 }
249
250 if (write(kfd, event_desc, strlen(event_desc)) < 0) {
Brenden Blanco7e71aef2015-09-09 18:28:21 -0700251 fprintf(stderr, "write of \"%s\" into kprobe_events failed: %s\n", event_desc, strerror(errno));
252 if (errno == EINVAL)
253 fprintf(stderr, "check dmesg output for possible cause\n");
Brenden Blanco75982492015-11-06 10:43:05 -0800254 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700255 }
256
257 snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/kprobes/%s", event);
Brenden Blanco75982492015-11-06 10:43:05 -0800258 if (bpf_attach_tracing_event(progfd, buf, reader, pid, cpu, group_fd) < 0)
259 goto error;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700260
Brenden Blanco8207d102015-09-25 13:58:30 -0700261 return reader;
Brenden Blanco75982492015-11-06 10:43:05 -0800262
263error:
264 if (kfd >= 0)
265 close(kfd);
266 if (reader)
267 perf_reader_free(reader);
268
269 return NULL;
Brenden Blancocd5cb412015-04-26 09:41:58 -0700270}
271
Brenden Blanco839dd272015-06-11 12:35:55 -0700272int bpf_detach_kprobe(const char *event_desc) {
Brenden Blanco75982492015-11-06 10:43:05 -0800273 int kfd = -1;
Brenden Blanco839dd272015-06-11 12:35:55 -0700274
275 kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0);
276 if (kfd < 0) {
277 perror("open(kprobe_events)");
Brenden Blanco75982492015-11-06 10:43:05 -0800278 goto error;
Brenden Blanco839dd272015-06-11 12:35:55 -0700279 }
280
281 if (write(kfd, event_desc, strlen(event_desc)) < 0) {
282 perror("write(kprobe_events)");
Brenden Blanco75982492015-11-06 10:43:05 -0800283 goto error;
Brenden Blanco839dd272015-06-11 12:35:55 -0700284 }
Brenden Blanco839dd272015-06-11 12:35:55 -0700285
Brenden Blanco75982492015-11-06 10:43:05 -0800286 return 0;
287
288error:
Brenden Blanco839dd272015-06-11 12:35:55 -0700289 if (kfd >= 0)
290 close(kfd);
291
Brenden Blanco75982492015-11-06 10:43:05 -0800292 return -1;
Brenden Blanco839dd272015-06-11 12:35:55 -0700293}
294
Brenden Blanco75982492015-11-06 10:43:05 -0800295void * bpf_open_perf_buffer(perf_reader_raw_cb raw_cb, void *cb_cookie, int pid, int cpu) {
296 int pfd;
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800297 struct perf_event_attr attr = {};
Brenden Blanco75982492015-11-06 10:43:05 -0800298 struct perf_reader *reader = NULL;
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800299
Brenden Blanco75982492015-11-06 10:43:05 -0800300 reader = perf_reader_new(NULL, raw_cb, cb_cookie);
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800301 if (!reader)
Brenden Blanco75982492015-11-06 10:43:05 -0800302 goto error;
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800303
304 attr.config = PERF_COUNT_SW_BPF_OUTPUT;
305 attr.type = PERF_TYPE_SOFTWARE;
306 attr.sample_type = PERF_SAMPLE_RAW;
Brenden Blanco75982492015-11-06 10:43:05 -0800307 attr.sample_period = 1;
308 attr.wakeup_events = 1;
309 pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC);
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800310 if (pfd < 0) {
311 perror("perf_event_open");
Brenden Blanco75982492015-11-06 10:43:05 -0800312 goto error;
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800313 }
314 perf_reader_set_fd(reader, pfd);
315
316 if (perf_reader_mmap(reader, attr.type, attr.sample_type) < 0)
Brenden Blanco75982492015-11-06 10:43:05 -0800317 goto error;
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800318
319 if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
320 perror("ioctl(PERF_EVENT_IOC_ENABLE)");
Brenden Blanco75982492015-11-06 10:43:05 -0800321 goto error;
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800322 }
323
324 return reader;
Brenden Blanco75982492015-11-06 10:43:05 -0800325
326error:
327 if (reader)
328 perf_reader_free(reader);
329
330 return NULL;
Brenden Blancod0daf6a2015-11-05 23:31:22 -0800331}