aboutsummaryrefslogtreecommitdiffstats
path: root/samples/bpf
diff options
context:
space:
mode:
authorSargun Dhillon <sargun@sargun.me>2016-07-25 08:55:02 -0400
committerDavid S. Miller <davem@davemloft.net>2016-07-25 21:07:48 -0400
commitcf9b1199de27ece1eafacf165933df10f0314232 (patch)
tree17cb747b81a9fce3a7052efc639bbb83488df191 /samples/bpf
parent96ae52279594470622ff0585621a13e96b700600 (diff)
samples/bpf: Add test/example of using bpf_probe_write_user bpf helper
This example shows using a kprobe to act as a dnat mechanism to divert traffic for arbitrary endpoints. It rewrite the arguments to a syscall while they're still in userspace, and before the syscall has a chance to copy the argument into kernel space. Although this is an example, it also acts as a test because the mapped address is 255.255.255.255:555 -> real address, and that's not a legal address to connect to. If the helper is broken, the example will fail on the intermediate steps, as well as the final step to verify the rewrite of userspace memory succeeded. Signed-off-by: Sargun Dhillon <sargun@sargun.me> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples/bpf')
-rw-r--r--samples/bpf/Makefile4
-rw-r--r--samples/bpf/test_probe_write_user_kern.c52
-rw-r--r--samples/bpf/test_probe_write_user_user.c78
3 files changed, 134 insertions, 0 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index d2d2b35c67eb..90ebf7d35c07 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -14,6 +14,7 @@ hostprogs-y += tracex3
14hostprogs-y += tracex4 14hostprogs-y += tracex4
15hostprogs-y += tracex5 15hostprogs-y += tracex5
16hostprogs-y += tracex6 16hostprogs-y += tracex6
17hostprogs-y += test_probe_write_user
17hostprogs-y += trace_output 18hostprogs-y += trace_output
18hostprogs-y += lathist 19hostprogs-y += lathist
19hostprogs-y += offwaketime 20hostprogs-y += offwaketime
@@ -37,6 +38,7 @@ tracex3-objs := bpf_load.o libbpf.o tracex3_user.o
37tracex4-objs := bpf_load.o libbpf.o tracex4_user.o 38tracex4-objs := bpf_load.o libbpf.o tracex4_user.o
38tracex5-objs := bpf_load.o libbpf.o tracex5_user.o 39tracex5-objs := bpf_load.o libbpf.o tracex5_user.o
39tracex6-objs := bpf_load.o libbpf.o tracex6_user.o 40tracex6-objs := bpf_load.o libbpf.o tracex6_user.o
41test_probe_write_user-objs := bpf_load.o libbpf.o test_probe_write_user_user.o
40trace_output-objs := bpf_load.o libbpf.o trace_output_user.o 42trace_output-objs := bpf_load.o libbpf.o trace_output_user.o
41lathist-objs := bpf_load.o libbpf.o lathist_user.o 43lathist-objs := bpf_load.o libbpf.o lathist_user.o
42offwaketime-objs := bpf_load.o libbpf.o offwaketime_user.o 44offwaketime-objs := bpf_load.o libbpf.o offwaketime_user.o
@@ -59,6 +61,7 @@ always += tracex3_kern.o
59always += tracex4_kern.o 61always += tracex4_kern.o
60always += tracex5_kern.o 62always += tracex5_kern.o
61always += tracex6_kern.o 63always += tracex6_kern.o
64always += test_probe_write_user_kern.o
62always += trace_output_kern.o 65always += trace_output_kern.o
63always += tcbpf1_kern.o 66always += tcbpf1_kern.o
64always += lathist_kern.o 67always += lathist_kern.o
@@ -85,6 +88,7 @@ HOSTLOADLIBES_tracex3 += -lelf
85HOSTLOADLIBES_tracex4 += -lelf -lrt 88HOSTLOADLIBES_tracex4 += -lelf -lrt
86HOSTLOADLIBES_tracex5 += -lelf 89HOSTLOADLIBES_tracex5 += -lelf
87HOSTLOADLIBES_tracex6 += -lelf 90HOSTLOADLIBES_tracex6 += -lelf
91HOSTLOADLIBES_test_probe_write_user += -lelf
88HOSTLOADLIBES_trace_output += -lelf -lrt 92HOSTLOADLIBES_trace_output += -lelf -lrt
89HOSTLOADLIBES_lathist += -lelf 93HOSTLOADLIBES_lathist += -lelf
90HOSTLOADLIBES_offwaketime += -lelf 94HOSTLOADLIBES_offwaketime += -lelf
diff --git a/samples/bpf/test_probe_write_user_kern.c b/samples/bpf/test_probe_write_user_kern.c
new file mode 100644
index 000000000000..3a677c807044
--- /dev/null
+++ b/samples/bpf/test_probe_write_user_kern.c
@@ -0,0 +1,52 @@
1/* Copyright (c) 2016 Sargun Dhillon <sargun@sargun.me>
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of version 2 of the GNU General Public
5 * License as published by the Free Software Foundation.
6 */
7#include <linux/skbuff.h>
8#include <linux/netdevice.h>
9#include <uapi/linux/bpf.h>
10#include <linux/version.h>
11#include "bpf_helpers.h"
12
13struct bpf_map_def SEC("maps") dnat_map = {
14 .type = BPF_MAP_TYPE_HASH,
15 .key_size = sizeof(struct sockaddr_in),
16 .value_size = sizeof(struct sockaddr_in),
17 .max_entries = 256,
18};
19
20/* kprobe is NOT a stable ABI
21 * kernel functions can be removed, renamed or completely change semantics.
22 * Number of arguments and their positions can change, etc.
23 * In such case this bpf+kprobe example will no longer be meaningful
24 *
25 * This example sits on a syscall, and the syscall ABI is relatively stable
26 * of course, across platforms, and over time, the ABI may change.
27 */
28SEC("kprobe/sys_connect")
29int bpf_prog1(struct pt_regs *ctx)
30{
31 struct sockaddr_in new_addr, orig_addr = {};
32 struct sockaddr_in *mapped_addr;
33 void *sockaddr_arg = (void *)PT_REGS_PARM2(ctx);
34 int sockaddr_len = (int)PT_REGS_PARM3(ctx);
35
36 if (sockaddr_len > sizeof(orig_addr))
37 return 0;
38
39 if (bpf_probe_read(&orig_addr, sizeof(orig_addr), sockaddr_arg) != 0)
40 return 0;
41
42 mapped_addr = bpf_map_lookup_elem(&dnat_map, &orig_addr);
43 if (mapped_addr != NULL) {
44 memcpy(&new_addr, mapped_addr, sizeof(new_addr));
45 bpf_probe_write_user(sockaddr_arg, &new_addr,
46 sizeof(new_addr));
47 }
48 return 0;
49}
50
51char _license[] SEC("license") = "GPL";
52u32 _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/samples/bpf/test_probe_write_user_user.c b/samples/bpf/test_probe_write_user_user.c
new file mode 100644
index 000000000000..a44bf347bedd
--- /dev/null
+++ b/samples/bpf/test_probe_write_user_user.c
@@ -0,0 +1,78 @@
1#include <stdio.h>
2#include <assert.h>
3#include <linux/bpf.h>
4#include <unistd.h>
5#include "libbpf.h"
6#include "bpf_load.h"
7#include <sys/socket.h>
8#include <string.h>
9#include <netinet/in.h>
10#include <arpa/inet.h>
11
12int main(int ac, char **argv)
13{
14 int serverfd, serverconnfd, clientfd;
15 socklen_t sockaddr_len;
16 struct sockaddr serv_addr, mapped_addr, tmp_addr;
17 struct sockaddr_in *serv_addr_in, *mapped_addr_in, *tmp_addr_in;
18 char filename[256];
19 char *ip;
20
21 serv_addr_in = (struct sockaddr_in *)&serv_addr;
22 mapped_addr_in = (struct sockaddr_in *)&mapped_addr;
23 tmp_addr_in = (struct sockaddr_in *)&tmp_addr;
24
25 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
26
27 if (load_bpf_file(filename)) {
28 printf("%s", bpf_log_buf);
29 return 1;
30 }
31
32 assert((serverfd = socket(AF_INET, SOCK_STREAM, 0)) > 0);
33 assert((clientfd = socket(AF_INET, SOCK_STREAM, 0)) > 0);
34
35 /* Bind server to ephemeral port on lo */
36 memset(&serv_addr, 0, sizeof(serv_addr));
37 serv_addr_in->sin_family = AF_INET;
38 serv_addr_in->sin_port = 0;
39 serv_addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
40
41 assert(bind(serverfd, &serv_addr, sizeof(serv_addr)) == 0);
42
43 sockaddr_len = sizeof(serv_addr);
44 assert(getsockname(serverfd, &serv_addr, &sockaddr_len) == 0);
45 ip = inet_ntoa(serv_addr_in->sin_addr);
46 printf("Server bound to: %s:%d\n", ip, ntohs(serv_addr_in->sin_port));
47
48 memset(&mapped_addr, 0, sizeof(mapped_addr));
49 mapped_addr_in->sin_family = AF_INET;
50 mapped_addr_in->sin_port = htons(5555);
51 mapped_addr_in->sin_addr.s_addr = inet_addr("255.255.255.255");
52
53 assert(!bpf_update_elem(map_fd[0], &mapped_addr, &serv_addr, BPF_ANY));
54
55 assert(listen(serverfd, 5) == 0);
56
57 ip = inet_ntoa(mapped_addr_in->sin_addr);
58 printf("Client connecting to: %s:%d\n",
59 ip, ntohs(mapped_addr_in->sin_port));
60 assert(connect(clientfd, &mapped_addr, sizeof(mapped_addr)) == 0);
61
62 sockaddr_len = sizeof(tmp_addr);
63 ip = inet_ntoa(tmp_addr_in->sin_addr);
64 assert((serverconnfd = accept(serverfd, &tmp_addr, &sockaddr_len)) > 0);
65 printf("Server received connection from: %s:%d\n",
66 ip, ntohs(tmp_addr_in->sin_port));
67
68 sockaddr_len = sizeof(tmp_addr);
69 assert(getpeername(clientfd, &tmp_addr, &sockaddr_len) == 0);
70 ip = inet_ntoa(tmp_addr_in->sin_addr);
71 printf("Client's peer address: %s:%d\n",
72 ip, ntohs(tmp_addr_in->sin_port));
73
74 /* Is the server's getsockname = the socket getpeername */
75 assert(memcmp(&serv_addr, &tmp_addr, sizeof(struct sockaddr_in)) == 0);
76
77 return 0;
78}