Skip to content

Commit 8a027dc

Browse files
fomichevAlexei Starovoitov
authored andcommitted
selftests/bpf: add sockopt test that exercises sk helpers
socktop test that introduces new SOL_CUSTOM sockopt level and stores whatever users sets in sk storage. Whenever getsockopt is called, the original value is retrieved. v9: * SO_SNDBUF example to override user-supplied buffer v7: * use retval=0 and optlen-1 v6: * test 'ret=1' use-case as well (Alexei Starovoitov) v4: * don't call bpf_sk_fullsock helper v3: * drop (__u8 *)(long) casts for optval{,_end} v2: * new test Cc: Andrii Nakryiko <andriin@fb.com> Cc: Martin Lau <kafai@fb.com> Signed-off-by: Stanislav Fomichev <sdf@google.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 9ec8a4c commit 8a027dc

File tree

4 files changed

+328
-1
lines changed

4 files changed

+328
-1
lines changed

tools/testing/selftests/bpf/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ test_hashmap
4040
test_btf_dump
4141
xdping
4242
test_sockopt
43+
test_sockopt_sk

tools/testing/selftests/bpf/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
2626
test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \
2727
test_cgroup_storage test_select_reuseport test_section_names \
2828
test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \
29-
test_btf_dump test_cgroup_attach xdping test_sockopt
29+
test_btf_dump test_cgroup_attach xdping test_sockopt test_sockopt_sk
3030

3131
BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c)))
3232
TEST_GEN_FILES = $(BPF_OBJ_FILES)
@@ -104,6 +104,7 @@ $(OUTPUT)/test_sock_fields: cgroup_helpers.c
104104
$(OUTPUT)/test_sysctl: cgroup_helpers.c
105105
$(OUTPUT)/test_cgroup_attach: cgroup_helpers.c
106106
$(OUTPUT)/test_sockopt: cgroup_helpers.c
107+
$(OUTPUT)/test_sockopt_sk: cgroup_helpers.c
107108

108109
.PHONY: force
109110

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <netinet/in.h>
3+
#include <linux/bpf.h>
4+
#include "bpf_helpers.h"
5+
6+
char _license[] SEC("license") = "GPL";
7+
__u32 _version SEC("version") = 1;
8+
9+
#define SOL_CUSTOM 0xdeadbeef
10+
11+
struct sockopt_sk {
12+
__u8 val;
13+
};
14+
15+
struct bpf_map_def SEC("maps") socket_storage_map = {
16+
.type = BPF_MAP_TYPE_SK_STORAGE,
17+
.key_size = sizeof(int),
18+
.value_size = sizeof(struct sockopt_sk),
19+
.map_flags = BPF_F_NO_PREALLOC,
20+
};
21+
BPF_ANNOTATE_KV_PAIR(socket_storage_map, int, struct sockopt_sk);
22+
23+
SEC("cgroup/getsockopt")
24+
int _getsockopt(struct bpf_sockopt *ctx)
25+
{
26+
__u8 *optval_end = ctx->optval_end;
27+
__u8 *optval = ctx->optval;
28+
struct sockopt_sk *storage;
29+
30+
if (ctx->level == SOL_IP && ctx->optname == IP_TOS)
31+
/* Not interested in SOL_IP:IP_TOS;
32+
* let next BPF program in the cgroup chain or kernel
33+
* handle it.
34+
*/
35+
return 1;
36+
37+
if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
38+
/* Not interested in SOL_SOCKET:SO_SNDBUF;
39+
* let next BPF program in the cgroup chain or kernel
40+
* handle it.
41+
*/
42+
return 1;
43+
}
44+
45+
if (ctx->level != SOL_CUSTOM)
46+
return 0; /* EPERM, deny everything except custom level */
47+
48+
if (optval + 1 > optval_end)
49+
return 0; /* EPERM, bounds check */
50+
51+
storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0,
52+
BPF_SK_STORAGE_GET_F_CREATE);
53+
if (!storage)
54+
return 0; /* EPERM, couldn't get sk storage */
55+
56+
if (!ctx->retval)
57+
return 0; /* EPERM, kernel should not have handled
58+
* SOL_CUSTOM, something is wrong!
59+
*/
60+
ctx->retval = 0; /* Reset system call return value to zero */
61+
62+
optval[0] = storage->val;
63+
ctx->optlen = 1;
64+
65+
return 1;
66+
}
67+
68+
SEC("cgroup/setsockopt")
69+
int _setsockopt(struct bpf_sockopt *ctx)
70+
{
71+
__u8 *optval_end = ctx->optval_end;
72+
__u8 *optval = ctx->optval;
73+
struct sockopt_sk *storage;
74+
75+
if (ctx->level == SOL_IP && ctx->optname == IP_TOS)
76+
/* Not interested in SOL_IP:IP_TOS;
77+
* let next BPF program in the cgroup chain or kernel
78+
* handle it.
79+
*/
80+
return 1;
81+
82+
if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
83+
/* Overwrite SO_SNDBUF value */
84+
85+
if (optval + sizeof(__u32) > optval_end)
86+
return 0; /* EPERM, bounds check */
87+
88+
*(__u32 *)optval = 0x55AA;
89+
ctx->optlen = 4;
90+
91+
return 1;
92+
}
93+
94+
if (ctx->level != SOL_CUSTOM)
95+
return 0; /* EPERM, deny everything except custom level */
96+
97+
if (optval + 1 > optval_end)
98+
return 0; /* EPERM, bounds check */
99+
100+
storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0,
101+
BPF_SK_STORAGE_GET_F_CREATE);
102+
if (!storage)
103+
return 0; /* EPERM, couldn't get sk storage */
104+
105+
storage->val = optval[0];
106+
ctx->optlen = -1; /* BPF has consumed this option, don't call kernel
107+
* setsockopt handler.
108+
*/
109+
110+
return 1;
111+
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <errno.h>
4+
#include <stdio.h>
5+
#include <unistd.h>
6+
#include <sys/types.h>
7+
#include <sys/socket.h>
8+
#include <netinet/in.h>
9+
10+
#include <linux/filter.h>
11+
#include <bpf/bpf.h>
12+
#include <bpf/libbpf.h>
13+
14+
#include "bpf_rlimit.h"
15+
#include "bpf_util.h"
16+
#include "cgroup_helpers.h"
17+
18+
#define CG_PATH "/sockopt"
19+
20+
#define SOL_CUSTOM 0xdeadbeef
21+
22+
static int getsetsockopt(void)
23+
{
24+
int fd, err;
25+
char buf[4] = {};
26+
socklen_t optlen;
27+
28+
fd = socket(AF_INET, SOCK_STREAM, 0);
29+
if (fd < 0) {
30+
log_err("Failed to create socket");
31+
return -1;
32+
}
33+
34+
/* IP_TOS - BPF bypass */
35+
36+
buf[0] = 0x08;
37+
err = setsockopt(fd, SOL_IP, IP_TOS, buf, 1);
38+
if (err) {
39+
log_err("Failed to call setsockopt(IP_TOS)");
40+
goto err;
41+
}
42+
43+
buf[0] = 0x00;
44+
optlen = 1;
45+
err = getsockopt(fd, SOL_IP, IP_TOS, buf, &optlen);
46+
if (err) {
47+
log_err("Failed to call getsockopt(IP_TOS)");
48+
goto err;
49+
}
50+
51+
if (buf[0] != 0x08) {
52+
log_err("Unexpected getsockopt(IP_TOS) buf[0] 0x%02x != 0x08",
53+
buf[0]);
54+
goto err;
55+
}
56+
57+
/* IP_TTL - EPERM */
58+
59+
buf[0] = 1;
60+
err = setsockopt(fd, SOL_IP, IP_TTL, buf, 1);
61+
if (!err || errno != EPERM) {
62+
log_err("Unexpected success from setsockopt(IP_TTL)");
63+
goto err;
64+
}
65+
66+
/* SOL_CUSTOM - handled by BPF */
67+
68+
buf[0] = 0x01;
69+
err = setsockopt(fd, SOL_CUSTOM, 0, buf, 1);
70+
if (err) {
71+
log_err("Failed to call setsockopt");
72+
goto err;
73+
}
74+
75+
buf[0] = 0x00;
76+
optlen = 4;
77+
err = getsockopt(fd, SOL_CUSTOM, 0, buf, &optlen);
78+
if (err) {
79+
log_err("Failed to call getsockopt");
80+
goto err;
81+
}
82+
83+
if (optlen != 1) {
84+
log_err("Unexpected optlen %d != 1", optlen);
85+
goto err;
86+
}
87+
if (buf[0] != 0x01) {
88+
log_err("Unexpected buf[0] 0x%02x != 0x01", buf[0]);
89+
goto err;
90+
}
91+
92+
/* SO_SNDBUF is overwritten */
93+
94+
buf[0] = 0x01;
95+
buf[1] = 0x01;
96+
buf[2] = 0x01;
97+
buf[3] = 0x01;
98+
err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, buf, 4);
99+
if (err) {
100+
log_err("Failed to call setsockopt(SO_SNDBUF)");
101+
goto err;
102+
}
103+
104+
buf[0] = 0x00;
105+
buf[1] = 0x00;
106+
buf[2] = 0x00;
107+
buf[3] = 0x00;
108+
optlen = 4;
109+
err = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, buf, &optlen);
110+
if (err) {
111+
log_err("Failed to call getsockopt(SO_SNDBUF)");
112+
goto err;
113+
}
114+
115+
if (*(__u32 *)buf != 0x55AA*2) {
116+
log_err("Unexpected getsockopt(SO_SNDBUF) 0x%x != 0x55AA*2",
117+
*(__u32 *)buf);
118+
goto err;
119+
}
120+
121+
close(fd);
122+
return 0;
123+
err:
124+
close(fd);
125+
return -1;
126+
}
127+
128+
static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
129+
{
130+
enum bpf_attach_type attach_type;
131+
enum bpf_prog_type prog_type;
132+
struct bpf_program *prog;
133+
int err;
134+
135+
err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
136+
if (err) {
137+
log_err("Failed to deduct types for %s BPF program", title);
138+
return -1;
139+
}
140+
141+
prog = bpf_object__find_program_by_title(obj, title);
142+
if (!prog) {
143+
log_err("Failed to find %s BPF program", title);
144+
return -1;
145+
}
146+
147+
err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
148+
attach_type, 0);
149+
if (err) {
150+
log_err("Failed to attach %s BPF program", title);
151+
return -1;
152+
}
153+
154+
return 0;
155+
}
156+
157+
static int run_test(int cgroup_fd)
158+
{
159+
struct bpf_prog_load_attr attr = {
160+
.file = "./sockopt_sk.o",
161+
};
162+
struct bpf_object *obj;
163+
int ignored;
164+
int err;
165+
166+
err = bpf_prog_load_xattr(&attr, &obj, &ignored);
167+
if (err) {
168+
log_err("Failed to load BPF object");
169+
return -1;
170+
}
171+
172+
err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt");
173+
if (err)
174+
goto close_bpf_object;
175+
176+
err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt");
177+
if (err)
178+
goto close_bpf_object;
179+
180+
err = getsetsockopt();
181+
182+
close_bpf_object:
183+
bpf_object__close(obj);
184+
return err;
185+
}
186+
187+
int main(int args, char **argv)
188+
{
189+
int cgroup_fd;
190+
int err = EXIT_SUCCESS;
191+
192+
if (setup_cgroup_environment())
193+
goto cleanup_obj;
194+
195+
cgroup_fd = create_and_get_cgroup(CG_PATH);
196+
if (cgroup_fd < 0)
197+
goto cleanup_cgroup_env;
198+
199+
if (join_cgroup(CG_PATH))
200+
goto cleanup_cgroup;
201+
202+
if (run_test(cgroup_fd))
203+
err = EXIT_FAILURE;
204+
205+
printf("test_sockopt_sk: %s\n",
206+
err == EXIT_SUCCESS ? "PASSED" : "FAILED");
207+
208+
cleanup_cgroup:
209+
close(cgroup_fd);
210+
cleanup_cgroup_env:
211+
cleanup_cgroup_environment();
212+
cleanup_obj:
213+
return err;
214+
}

0 commit comments

Comments
 (0)