blob: 5e88dcfc47418aed5b5b594caa5538be441a212e [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{
62 union bpf_attr attr = {
63 .map_type = map_type,
64 .key_size = key_size,
65 .value_size = value_size,
66 .max_entries = max_entries
67 };
68
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{
74 union bpf_attr attr = {
75 .map_fd = fd,
76 .key = ptr_to_u64(key),
77 .value = ptr_to_u64(value),
78 .flags = flags,
79 };
80
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{
86 union bpf_attr attr = {
87 .map_fd = fd,
88 .key = ptr_to_u64(key),
89 .value = ptr_to_u64(value),
90 };
91
92 return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
93}
94
95int bpf_delete_elem(int fd, void *key)
96{
97 union bpf_attr attr = {
98 .map_fd = fd,
99 .key = ptr_to_u64(key),
100 };
101
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{
107 union bpf_attr attr = {
108 .map_fd = fd,
109 .key = ptr_to_u64(key),
110 .next_key = ptr_to_u64(next_key),
111 };
112
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 Blanco759029f2015-07-29 15:47:51 -0700125 log_buf = log_buf ? log_buf : bpf_log_buf;
126 log_buf_size = log_buf_size ? log_buf_size : LOG_BUF_SIZE;
Brenden Blancoa94bd932015-04-26 00:56:42 -0700127 union bpf_attr attr = {
128 .prog_type = prog_type,
129 .insns = ptr_to_u64((void *) insns),
130 .insn_cnt = prog_len / sizeof(struct bpf_insn),
131 .license = ptr_to_u64((void *) license),
Brenden Blanco759029f2015-07-29 15:47:51 -0700132 .log_buf = ptr_to_u64(log_buf),
133 .log_size = log_buf_size,
Brenden Blancoa94bd932015-04-26 00:56:42 -0700134 .log_level = 1,
135 };
136
Brenden Blanco7009b552015-05-26 11:48:17 -0700137 attr.kern_version = kern_version;
Brenden Blanco759029f2015-07-29 15:47:51 -0700138 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 Blanco759029f2015-07-29 15:47:51 -0700141 if (ret < 0 && log_buf == bpf_log_buf) {
Brenden Blancocd5cb412015-04-26 09:41:58 -0700142 fprintf(stderr, "bpf: %s\n%s\n", strerror(errno), bpf_log_buf);
143 }
144 return ret;
Brenden Blancoa94bd932015-04-26 00:56:42 -0700145}
146
147int bpf_open_raw_sock(const char *name)
148{
149 struct sockaddr_ll sll;
150 int sock;
151
152 sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL));
153 if (sock < 0) {
154 printf("cannot create raw socket\n");
155 return -1;
156 }
157
158 memset(&sll, 0, sizeof(sll));
159 sll.sll_family = AF_PACKET;
160 sll.sll_ifindex = if_nametoindex(name);
161 sll.sll_protocol = htons(ETH_P_ALL);
162 if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
163 printf("bind to %s: %s\n", name, strerror(errno));
164 close(sock);
165 return -1;
166 }
167
168 return sock;
169}
170
171int bpf_attach_socket(int sock, int prog) {
Brenden Blancoaf956732015-06-09 13:58:42 -0700172 return setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog, sizeof(prog));
Brenden Blancoa94bd932015-04-26 00:56:42 -0700173}
174
Brenden Blancocd5cb412015-04-26 09:41:58 -0700175static int bpf_attach_tracing_event(int progfd, const char *event_path, pid_t pid, int cpu, int group_fd)
176{
177 int efd = -1, rc = -1, pfd = -1;
178 ssize_t bytes = -1;
179 char buf[256];
180 struct perf_event_attr attr = {};
181
182 snprintf(buf, sizeof(buf), "%s/id", event_path);
183 efd = open(buf, O_RDONLY, 0);
184 if (efd < 0) {
185 fprintf(stderr, "open(%s): %s\n", buf, strerror(errno));
186 goto cleanup;
187 }
188
189 bytes = read(efd, buf, sizeof(buf));
190 if (bytes <= 0 || bytes >= sizeof(buf)) {
191 fprintf(stderr, "read(%s): %s\n", buf, strerror(errno));
192 goto cleanup;
193 }
194 buf[bytes] = '\0';
195 attr.config = strtol(buf, NULL, 0);
196 attr.type = PERF_TYPE_TRACEPOINT;
197 attr.sample_type = PERF_SAMPLE_RAW;
198 attr.sample_period = 1;
199 attr.wakeup_events = 1;
200 pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, PERF_FLAG_FD_CLOEXEC);
201 if (pfd < 0) {
202 perror("perf_event_open");
203 goto cleanup;
204 }
205 if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, progfd) < 0) {
206 perror("ioctl(PERF_EVENT_IOC_SET_BPF)");
207 goto cleanup;
208 }
209 if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
210 perror("ioctl(PERF_EVENT_IOC_ENABLE)");
211 goto cleanup;
212 }
213
214 rc = pfd;
215 pfd = -1;
216
217cleanup:
218 if (efd >= 0)
219 close(efd);
220 if (pfd >= 0)
221 close(pfd);
222
223 return rc;
224}
225
226int bpf_attach_kprobe(int progfd, const char *event,
227 const char *event_desc, pid_t pid,
228 int cpu, int group_fd) {
229 int rc = -1, kfd = -1;
230 char buf[256];
231
232 kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0);
233 if (kfd < 0) {
234 perror("open(kprobe_events)");
235 goto cleanup;
236 }
237
238 if (write(kfd, event_desc, strlen(event_desc)) < 0) {
239 perror("write(kprobe_events)");
240 goto cleanup;
241 }
242
243 snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/kprobes/%s", event);
244 rc = bpf_attach_tracing_event(progfd, buf, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/);
245
246cleanup:
247 if (kfd >= 0)
248 close(kfd);
249
250 return rc;
251}
252
Brenden Blanco839dd272015-06-11 12:35:55 -0700253int bpf_detach_kprobe(const char *event_desc) {
254 int rc = -1, kfd = -1;
255
256 kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0);
257 if (kfd < 0) {
258 perror("open(kprobe_events)");
259 goto cleanup;
260 }
261
262 if (write(kfd, event_desc, strlen(event_desc)) < 0) {
263 perror("write(kprobe_events)");
264 goto cleanup;
265 }
266 rc = 0;
267
268cleanup:
269 if (kfd >= 0)
270 close(kfd);
271
272 return rc;
273}
274