blob: a81790790a0f73f574c384f169a099d6cef0acd5 [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"
36
Brenden Blancof275d3d2015-07-06 23:41:23 -070037// TODO: remove these defines when linux-libc-dev exports them properly
38
39#ifndef __NR_bpf
40#define __NR_bpf 321
41#endif
42
43#ifndef SO_ATTACH_BPF
44#define SO_ATTACH_BPF 50
45#endif
46
47#ifndef PERF_EVENT_IOC_SET_BPF
48#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
49#endif
50
51#ifndef PERF_FLAG_FD_CLOEXEC
52#define PERF_FLAG_FD_CLOEXEC (1UL << 3)
53#endif
54
Brenden Blancoa94bd932015-04-26 00:56:42 -070055static __u64 ptr_to_u64(void *ptr)
56{
57 return (__u64) (unsigned long) ptr;
58}
59
60int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries)
61{
Brenden Blancofdc027c2015-09-03 11:49:54 -070062 union bpf_attr attr;
63 memset(&attr, 0, sizeof(attr));
64 attr.map_type = map_type;
65 attr.key_size = key_size;
66 attr.value_size = value_size;
67 attr.max_entries = max_entries;
Brenden Blancoa94bd932015-04-26 00:56:42 -070068
69 return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
70}
71
72int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags)
73{
Brenden Blancofdc027c2015-09-03 11:49:54 -070074 union bpf_attr attr;
75 memset(&attr, 0, sizeof(attr));
76 attr.map_fd = fd;
77 attr.key = ptr_to_u64(key);
78 attr.value = ptr_to_u64(value);
79 attr.flags = flags;
Brenden Blancoa94bd932015-04-26 00:56:42 -070080
81 return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
82}
83
84int bpf_lookup_elem(int fd, void *key, void *value)
85{
Brenden Blancofdc027c2015-09-03 11:49:54 -070086 union bpf_attr attr;
87 memset(&attr, 0, sizeof(attr));
88 attr.map_fd = fd;
89 attr.key = ptr_to_u64(key);
90 attr.value = ptr_to_u64(value);
Brenden Blancoa94bd932015-04-26 00:56:42 -070091
92 return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
93}
94
95int bpf_delete_elem(int fd, void *key)
96{
Brenden Blancofdc027c2015-09-03 11:49:54 -070097 union bpf_attr attr;
98 memset(&attr, 0, sizeof(attr));
99 attr.map_fd = fd;
100 attr.key = ptr_to_u64(key);
Brenden Blancoa94bd932015-04-26 00:56:42 -0700101
102 return syscall(__NR_bpf, BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
103}
104
105int bpf_get_next_key(int fd, void *key, void *next_key)
106{
Brenden Blancofdc027c2015-09-03 11:49:54 -0700107 union bpf_attr attr;
108 memset(&attr, 0, sizeof(attr));
109 attr.map_fd = fd;
110 attr.key = ptr_to_u64(key);
111 attr.next_key = ptr_to_u64(next_key);
Brenden Blancoa94bd932015-04-26 00:56:42 -0700112
113 return syscall(__NR_bpf, BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
114}
115
116#define ROUND_UP(x, n) (((x) + (n) - 1u) & ~((n) - 1u))
117
118char bpf_log_buf[LOG_BUF_SIZE];
119
120int bpf_prog_load(enum bpf_prog_type prog_type,
Brenden Blancocd5cb412015-04-26 09:41:58 -0700121 const struct bpf_insn *insns, int prog_len,
Brenden Blanco759029f2015-07-29 15:47:51 -0700122 const char *license, unsigned kern_version,
123 char *log_buf, unsigned log_buf_size)
Brenden Blancoa94bd932015-04-26 00:56:42 -0700124{
Brenden Blancofdc027c2015-09-03 11:49:54 -0700125 union bpf_attr attr;
126 memset(&attr, 0, sizeof(attr));
127 attr.prog_type = prog_type;
128 attr.insns = ptr_to_u64((void *) insns);
129 attr.insn_cnt = prog_len / sizeof(struct bpf_insn);
130 attr.license = ptr_to_u64((void *) license);
131 attr.log_buf = ptr_to_u64(log_buf);
132 attr.log_size = log_buf_size;
133 attr.log_level = log_buf ? 1 : 0;
Brenden Blancoa94bd932015-04-26 00:56:42 -0700134
Brenden Blanco7009b552015-05-26 11:48:17 -0700135 attr.kern_version = kern_version;
Brenden Blanco81a783a2015-08-24 23:42:42 -0700136 if (log_buf)
137 log_buf[0] = 0;
Brenden Blancoa94bd932015-04-26 00:56:42 -0700138
Brenden Blancocd5cb412015-04-26 09:41:58 -0700139 int ret = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
Brenden Blanco81a783a2015-08-24 23:42:42 -0700140 if (ret < 0 && !log_buf) {
141 // caller did not specify log_buf but failure should be printed,
142 // so call recursively and print the result to stderr
143 bpf_prog_load(prog_type, insns, prog_len, license, kern_version,
144 bpf_log_buf, LOG_BUF_SIZE);
Brenden Blancocd5cb412015-04-26 09:41:58 -0700145 fprintf(stderr, "bpf: %s\n%s\n", strerror(errno), bpf_log_buf);
146 }
147 return ret;
Brenden Blancoa94bd932015-04-26 00:56:42 -0700148}
149
150int bpf_open_raw_sock(const char *name)
151{
152 struct sockaddr_ll sll;
153 int sock;
154
155 sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL));
156 if (sock < 0) {
157 printf("cannot create raw socket\n");
158 return -1;
159 }
160
161 memset(&sll, 0, sizeof(sll));
162 sll.sll_family = AF_PACKET;
163 sll.sll_ifindex = if_nametoindex(name);
164 sll.sll_protocol = htons(ETH_P_ALL);
165 if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
166 printf("bind to %s: %s\n", name, strerror(errno));
167 close(sock);
168 return -1;
169 }
170
171 return sock;
172}
173
174int bpf_attach_socket(int sock, int prog) {
Brenden Blancoaf956732015-06-09 13:58:42 -0700175 return setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog, sizeof(prog));
Brenden Blancoa94bd932015-04-26 00:56:42 -0700176}
177
Brenden Blancocd5cb412015-04-26 09:41:58 -0700178static int bpf_attach_tracing_event(int progfd, const char *event_path, pid_t pid, int cpu, int group_fd)
179{
180 int efd = -1, rc = -1, pfd = -1;
181 ssize_t bytes = -1;
182 char buf[256];
183 struct perf_event_attr attr = {};
184
185 snprintf(buf, sizeof(buf), "%s/id", event_path);
186 efd = open(buf, O_RDONLY, 0);
187 if (efd < 0) {
188 fprintf(stderr, "open(%s): %s\n", buf, strerror(errno));
189 goto cleanup;
190 }
191
192 bytes = read(efd, buf, sizeof(buf));
193 if (bytes <= 0 || bytes >= sizeof(buf)) {
194 fprintf(stderr, "read(%s): %s\n", buf, strerror(errno));
195 goto cleanup;
196 }
197 buf[bytes] = '\0';
198 attr.config = strtol(buf, NULL, 0);
199 attr.type = PERF_TYPE_TRACEPOINT;
200 attr.sample_type = PERF_SAMPLE_RAW;
201 attr.sample_period = 1;
202 attr.wakeup_events = 1;
203 pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, PERF_FLAG_FD_CLOEXEC);
204 if (pfd < 0) {
205 perror("perf_event_open");
206 goto cleanup;
207 }
208 if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, progfd) < 0) {
209 perror("ioctl(PERF_EVENT_IOC_SET_BPF)");
210 goto cleanup;
211 }
212 if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
213 perror("ioctl(PERF_EVENT_IOC_ENABLE)");
214 goto cleanup;
215 }
216
217 rc = pfd;
218 pfd = -1;
219
220cleanup:
221 if (efd >= 0)
222 close(efd);
223 if (pfd >= 0)
224 close(pfd);
225
226 return rc;
227}
228
229int bpf_attach_kprobe(int progfd, const char *event,
230 const char *event_desc, pid_t pid,
231 int cpu, int group_fd) {
232 int rc = -1, kfd = -1;
233 char buf[256];
234
235 kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0);
236 if (kfd < 0) {
237 perror("open(kprobe_events)");
238 goto cleanup;
239 }
240
241 if (write(kfd, event_desc, strlen(event_desc)) < 0) {
242 perror("write(kprobe_events)");
243 goto cleanup;
244 }
245
246 snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/kprobes/%s", event);
247 rc = bpf_attach_tracing_event(progfd, buf, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/);
248
249cleanup:
250 if (kfd >= 0)
251 close(kfd);
252
253 return rc;
254}
255
Brenden Blanco839dd272015-06-11 12:35:55 -0700256int bpf_detach_kprobe(const char *event_desc) {
257 int rc = -1, kfd = -1;
258
259 kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0);
260 if (kfd < 0) {
261 perror("open(kprobe_events)");
262 goto cleanup;
263 }
264
265 if (write(kfd, event_desc, strlen(event_desc)) < 0) {
266 perror("write(kprobe_events)");
267 goto cleanup;
268 }
269 rc = 0;
270
271cleanup:
272 if (kfd >= 0)
273 close(kfd);
274
275 return rc;
276}
277