Skip to content

Commit cf9b119

Browse files
sargundavem330
authored andcommitted
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>
1 parent 96ae522 commit cf9b119

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed

samples/bpf/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ hostprogs-y += tracex3
1414
hostprogs-y += tracex4
1515
hostprogs-y += tracex5
1616
hostprogs-y += tracex6
17+
hostprogs-y += test_probe_write_user
1718
hostprogs-y += trace_output
1819
hostprogs-y += lathist
1920
hostprogs-y += offwaketime
@@ -37,6 +38,7 @@ tracex3-objs := bpf_load.o libbpf.o tracex3_user.o
3738
tracex4-objs := bpf_load.o libbpf.o tracex4_user.o
3839
tracex5-objs := bpf_load.o libbpf.o tracex5_user.o
3940
tracex6-objs := bpf_load.o libbpf.o tracex6_user.o
41+
test_probe_write_user-objs := bpf_load.o libbpf.o test_probe_write_user_user.o
4042
trace_output-objs := bpf_load.o libbpf.o trace_output_user.o
4143
lathist-objs := bpf_load.o libbpf.o lathist_user.o
4244
offwaketime-objs := bpf_load.o libbpf.o offwaketime_user.o
@@ -59,6 +61,7 @@ always += tracex3_kern.o
5961
always += tracex4_kern.o
6062
always += tracex5_kern.o
6163
always += tracex6_kern.o
64+
always += test_probe_write_user_kern.o
6265
always += trace_output_kern.o
6366
always += tcbpf1_kern.o
6467
always += lathist_kern.o
@@ -85,6 +88,7 @@ HOSTLOADLIBES_tracex3 += -lelf
8588
HOSTLOADLIBES_tracex4 += -lelf -lrt
8689
HOSTLOADLIBES_tracex5 += -lelf
8790
HOSTLOADLIBES_tracex6 += -lelf
91+
HOSTLOADLIBES_test_probe_write_user += -lelf
8892
HOSTLOADLIBES_trace_output += -lelf -lrt
8993
HOSTLOADLIBES_lathist += -lelf
9094
HOSTLOADLIBES_offwaketime += -lelf
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
13+
struct 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+
*/
28+
SEC("kprobe/sys_connect")
29+
int 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+
51+
char _license[] SEC("license") = "GPL";
52+
u32 _version SEC("version") = LINUX_VERSION_CODE;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
12+
int 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+
}

0 commit comments

Comments
 (0)