xref: /openbmc/linux/tools/testing/selftests/bpf/prog_tests/assign_reuse.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1  // SPDX-License-Identifier: GPL-2.0
2  /* Copyright (c) 2023 Isovalent */
3  #include <uapi/linux/if_link.h>
4  #include <test_progs.h>
5  
6  #include <netinet/tcp.h>
7  #include <netinet/udp.h>
8  
9  #include "network_helpers.h"
10  #include "test_assign_reuse.skel.h"
11  
12  #define NS_TEST "assign_reuse"
13  #define LOOPBACK 1
14  #define PORT 4443
15  
attach_reuseport(int sock_fd,int prog_fd)16  static int attach_reuseport(int sock_fd, int prog_fd)
17  {
18  	return setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF,
19  			  &prog_fd, sizeof(prog_fd));
20  }
21  
cookie(int fd)22  static __u64 cookie(int fd)
23  {
24  	__u64 cookie = 0;
25  	socklen_t cookie_len = sizeof(cookie);
26  	int ret;
27  
28  	ret = getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len);
29  	ASSERT_OK(ret, "cookie");
30  	ASSERT_GT(cookie, 0, "cookie_invalid");
31  
32  	return cookie;
33  }
34  
echo_test_udp(int fd_sv)35  static int echo_test_udp(int fd_sv)
36  {
37  	struct sockaddr_storage addr = {};
38  	socklen_t len = sizeof(addr);
39  	char buff[1] = {};
40  	int fd_cl = -1, ret;
41  
42  	fd_cl = connect_to_fd(fd_sv, 100);
43  	ASSERT_GT(fd_cl, 0, "create_client");
44  	ASSERT_EQ(getsockname(fd_cl, (void *)&addr, &len), 0, "getsockname");
45  
46  	ASSERT_EQ(send(fd_cl, buff, sizeof(buff), 0), 1, "send_client");
47  
48  	ret = recv(fd_sv, buff, sizeof(buff), 0);
49  	if (ret < 0) {
50  		close(fd_cl);
51  		return errno;
52  	}
53  
54  	ASSERT_EQ(ret, 1, "recv_server");
55  	ASSERT_EQ(sendto(fd_sv, buff, sizeof(buff), 0, (void *)&addr, len), 1, "send_server");
56  	ASSERT_EQ(recv(fd_cl, buff, sizeof(buff), 0), 1, "recv_client");
57  	close(fd_cl);
58  	return 0;
59  }
60  
echo_test_tcp(int fd_sv)61  static int echo_test_tcp(int fd_sv)
62  {
63  	char buff[1] = {};
64  	int fd_cl = -1, fd_sv_cl = -1;
65  
66  	fd_cl = connect_to_fd(fd_sv, 100);
67  	if (fd_cl < 0)
68  		return errno;
69  
70  	fd_sv_cl = accept(fd_sv, NULL, NULL);
71  	ASSERT_GE(fd_sv_cl, 0, "accept_fd");
72  
73  	ASSERT_EQ(send(fd_cl, buff, sizeof(buff), 0), 1, "send_client");
74  	ASSERT_EQ(recv(fd_sv_cl, buff, sizeof(buff), 0), 1, "recv_server");
75  	ASSERT_EQ(send(fd_sv_cl, buff, sizeof(buff), 0), 1, "send_server");
76  	ASSERT_EQ(recv(fd_cl, buff, sizeof(buff), 0), 1, "recv_client");
77  	close(fd_sv_cl);
78  	close(fd_cl);
79  	return 0;
80  }
81  
run_assign_reuse(int family,int sotype,const char * ip,__u16 port)82  void run_assign_reuse(int family, int sotype, const char *ip, __u16 port)
83  {
84  	DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
85  		.ifindex = LOOPBACK,
86  		.attach_point = BPF_TC_INGRESS,
87  	);
88  	DECLARE_LIBBPF_OPTS(bpf_tc_opts, tc_opts,
89  		.handle = 1,
90  		.priority = 1,
91  	);
92  	bool hook_created = false, tc_attached = false;
93  	int ret, fd_tc, fd_accept, fd_drop, fd_map;
94  	int *fd_sv = NULL;
95  	__u64 fd_val;
96  	struct test_assign_reuse *skel;
97  	const int zero = 0;
98  
99  	skel = test_assign_reuse__open();
100  	if (!ASSERT_OK_PTR(skel, "skel_open"))
101  		goto cleanup;
102  
103  	skel->rodata->dest_port = port;
104  
105  	ret = test_assign_reuse__load(skel);
106  	if (!ASSERT_OK(ret, "skel_load"))
107  		goto cleanup;
108  
109  	ASSERT_EQ(skel->bss->sk_cookie_seen, 0, "cookie_init");
110  
111  	fd_tc = bpf_program__fd(skel->progs.tc_main);
112  	fd_accept = bpf_program__fd(skel->progs.reuse_accept);
113  	fd_drop = bpf_program__fd(skel->progs.reuse_drop);
114  	fd_map = bpf_map__fd(skel->maps.sk_map);
115  
116  	fd_sv = start_reuseport_server(family, sotype, ip, port, 100, 1);
117  	if (!ASSERT_NEQ(fd_sv, NULL, "start_reuseport_server"))
118  		goto cleanup;
119  
120  	ret = attach_reuseport(*fd_sv, fd_drop);
121  	if (!ASSERT_OK(ret, "attach_reuseport"))
122  		goto cleanup;
123  
124  	fd_val = *fd_sv;
125  	ret = bpf_map_update_elem(fd_map, &zero, &fd_val, BPF_NOEXIST);
126  	if (!ASSERT_OK(ret, "bpf_sk_map"))
127  		goto cleanup;
128  
129  	ret = bpf_tc_hook_create(&tc_hook);
130  	if (ret == 0)
131  		hook_created = true;
132  	ret = ret == -EEXIST ? 0 : ret;
133  	if (!ASSERT_OK(ret, "bpf_tc_hook_create"))
134  		goto cleanup;
135  
136  	tc_opts.prog_fd = fd_tc;
137  	ret = bpf_tc_attach(&tc_hook, &tc_opts);
138  	if (!ASSERT_OK(ret, "bpf_tc_attach"))
139  		goto cleanup;
140  	tc_attached = true;
141  
142  	if (sotype == SOCK_STREAM)
143  		ASSERT_EQ(echo_test_tcp(*fd_sv), ECONNREFUSED, "drop_tcp");
144  	else
145  		ASSERT_EQ(echo_test_udp(*fd_sv), EAGAIN, "drop_udp");
146  	ASSERT_EQ(skel->bss->reuseport_executed, 1, "program executed once");
147  
148  	skel->bss->sk_cookie_seen = 0;
149  	skel->bss->reuseport_executed = 0;
150  	ASSERT_OK(attach_reuseport(*fd_sv, fd_accept), "attach_reuseport(accept)");
151  
152  	if (sotype == SOCK_STREAM)
153  		ASSERT_EQ(echo_test_tcp(*fd_sv), 0, "echo_tcp");
154  	else
155  		ASSERT_EQ(echo_test_udp(*fd_sv), 0, "echo_udp");
156  
157  	ASSERT_EQ(skel->bss->sk_cookie_seen, cookie(*fd_sv),
158  		  "cookie_mismatch");
159  	ASSERT_EQ(skel->bss->reuseport_executed, 1, "program executed once");
160  cleanup:
161  	if (tc_attached) {
162  		tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
163  		ret = bpf_tc_detach(&tc_hook, &tc_opts);
164  		ASSERT_OK(ret, "bpf_tc_detach");
165  	}
166  	if (hook_created) {
167  		tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
168  		bpf_tc_hook_destroy(&tc_hook);
169  	}
170  	test_assign_reuse__destroy(skel);
171  	free_fds(fd_sv, 1);
172  }
173  
test_assign_reuse(void)174  void test_assign_reuse(void)
175  {
176  	struct nstoken *tok = NULL;
177  
178  	SYS(out, "ip netns add %s", NS_TEST);
179  	SYS(cleanup, "ip -net %s link set dev lo up", NS_TEST);
180  
181  	tok = open_netns(NS_TEST);
182  	if (!ASSERT_OK_PTR(tok, "netns token"))
183  		return;
184  
185  	if (test__start_subtest("tcpv4"))
186  		run_assign_reuse(AF_INET, SOCK_STREAM, "127.0.0.1", PORT);
187  	if (test__start_subtest("tcpv6"))
188  		run_assign_reuse(AF_INET6, SOCK_STREAM, "::1", PORT);
189  	if (test__start_subtest("udpv4"))
190  		run_assign_reuse(AF_INET, SOCK_DGRAM, "127.0.0.1", PORT);
191  	if (test__start_subtest("udpv6"))
192  		run_assign_reuse(AF_INET6, SOCK_DGRAM, "::1", PORT);
193  
194  cleanup:
195  	close_netns(tok);
196  	SYS_NOFAIL("ip netns delete %s", NS_TEST);
197  out:
198  	return;
199  }
200