1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020 Facebook */
3 
4 #define _GNU_SOURCE
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <sched.h>
12 #include <net/if.h>
13 #include <linux/compiler.h>
14 #include <bpf/libbpf.h>
15 
16 #include "network_helpers.h"
17 #include "test_progs.h"
18 #include "test_btf_skc_cls_ingress.skel.h"
19 
20 #define TEST_NS "skc_cls_ingress"
21 
22 #define BIT(n)		(1 << (n))
23 #define TEST_MODE_IPV4	BIT(0)
24 #define TEST_MODE_IPV6	BIT(1)
25 #define TEST_MODE_DUAL	(TEST_MODE_IPV4 | TEST_MODE_IPV6)
26 
27 #define SERVER_ADDR_IPV4	"127.0.0.1"
28 #define SERVER_ADDR_IPV6	"::1"
29 #define SERVER_ADDR_DUAL	"::0"
30 /* RFC791, 576 for minimal IPv4 datagram, minus 40 bytes of TCP header */
31 #define MIN_IPV4_MSS		536
32 
prepare_netns(struct test_btf_skc_cls_ingress * skel)33 static struct netns_obj *prepare_netns(struct test_btf_skc_cls_ingress *skel)
34 {
35 	LIBBPF_OPTS(bpf_tc_hook, qdisc_lo, .attach_point = BPF_TC_INGRESS);
36 	LIBBPF_OPTS(bpf_tc_opts, tc_attach,
37 		    .prog_fd = bpf_program__fd(skel->progs.cls_ingress));
38 	struct netns_obj *ns = NULL;
39 
40 	ns = netns_new(TEST_NS, true);
41 	if (!ASSERT_OK_PTR(ns, "create and join netns"))
42 		return ns;
43 
44 	qdisc_lo.ifindex = if_nametoindex("lo");
45 	if (!ASSERT_OK(bpf_tc_hook_create(&qdisc_lo), "qdisc add dev lo clsact"))
46 		goto free_ns;
47 
48 	if (!ASSERT_OK(bpf_tc_attach(&qdisc_lo, &tc_attach),
49 		       "filter add dev lo ingress"))
50 		goto free_ns;
51 
52 	/* Ensure 20 bytes options (i.e. in total 40 bytes tcp header) for the
53 	 * bpf_tcp_gen_syncookie() helper.
54 	 */
55 	if (write_sysctl("/proc/sys/net/ipv4/tcp_window_scaling", "1") ||
56 	    write_sysctl("/proc/sys/net/ipv4/tcp_timestamps", "1") ||
57 	    write_sysctl("/proc/sys/net/ipv4/tcp_sack", "1"))
58 		goto free_ns;
59 
60 	return ns;
61 
62 free_ns:
63 	netns_free(ns);
64 	return NULL;
65 }
66 
reset_test(struct test_btf_skc_cls_ingress * skel)67 static void reset_test(struct test_btf_skc_cls_ingress *skel)
68 {
69 	memset(&skel->bss->srv_sa4, 0, sizeof(skel->bss->srv_sa4));
70 	memset(&skel->bss->srv_sa6, 0, sizeof(skel->bss->srv_sa6));
71 	skel->bss->listen_tp_sport = 0;
72 	skel->bss->req_sk_sport = 0;
73 	skel->bss->recv_cookie = 0;
74 	skel->bss->gen_cookie = 0;
75 	skel->bss->linum = 0;
76 	skel->bss->mss = 0;
77 }
78 
print_err_line(struct test_btf_skc_cls_ingress * skel)79 static void print_err_line(struct test_btf_skc_cls_ingress *skel)
80 {
81 	if (skel->bss->linum)
82 		printf("bpf prog error at line %u\n", skel->bss->linum);
83 }
84 
v6only_true(int fd,void * opts)85 static int v6only_true(int fd, void *opts)
86 {
87 	int mode = true;
88 
89 	return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode));
90 }
91 
v6only_false(int fd,void * opts)92 static int v6only_false(int fd, void *opts)
93 {
94 	int mode = false;
95 
96 	return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode));
97 }
98 
run_test(struct test_btf_skc_cls_ingress * skel,bool gen_cookies,int ip_mode)99 static void run_test(struct test_btf_skc_cls_ingress *skel, bool gen_cookies,
100 		     int ip_mode)
101 {
102 	const char *tcp_syncookies = gen_cookies ? "2" : "1";
103 	int listen_fd = -1, cli_fd = -1, srv_fd = -1, err;
104 	struct network_helper_opts opts = { 0 };
105 	struct sockaddr_storage *addr;
106 	struct sockaddr_in6 srv_sa6;
107 	struct sockaddr_in srv_sa4;
108 	socklen_t addr_len;
109 	int sock_family;
110 	char *srv_addr;
111 	int srv_port;
112 
113 	switch (ip_mode) {
114 	case TEST_MODE_IPV4:
115 		sock_family = AF_INET;
116 		srv_addr = SERVER_ADDR_IPV4;
117 		addr = (struct sockaddr_storage *)&srv_sa4;
118 		addr_len = sizeof(srv_sa4);
119 		break;
120 	case TEST_MODE_IPV6:
121 		opts.post_socket_cb = v6only_true;
122 		sock_family = AF_INET6;
123 		srv_addr = SERVER_ADDR_IPV6;
124 		addr = (struct sockaddr_storage *)&srv_sa6;
125 		addr_len = sizeof(srv_sa6);
126 		break;
127 	case TEST_MODE_DUAL:
128 		opts.post_socket_cb = v6only_false;
129 		sock_family = AF_INET6;
130 		srv_addr = SERVER_ADDR_DUAL;
131 		addr = (struct sockaddr_storage *)&srv_sa6;
132 		addr_len = sizeof(srv_sa6);
133 		break;
134 	default:
135 		PRINT_FAIL("Unknown IP mode %d", ip_mode);
136 		return;
137 	}
138 
139 	if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", tcp_syncookies))
140 		return;
141 
142 	listen_fd = start_server_str(sock_family, SOCK_STREAM, srv_addr,  0,
143 				     &opts);
144 	if (!ASSERT_OK_FD(listen_fd, "start server"))
145 		return;
146 
147 	err = getsockname(listen_fd, (struct sockaddr *)addr, &addr_len);
148 	if (!ASSERT_OK(err, "getsockname(listen_fd)"))
149 		goto done;
150 
151 	switch (ip_mode) {
152 	case TEST_MODE_IPV4:
153 		memcpy(&skel->bss->srv_sa4, &srv_sa4, sizeof(srv_sa4));
154 		srv_port = ntohs(srv_sa4.sin_port);
155 		break;
156 	case TEST_MODE_IPV6:
157 	case TEST_MODE_DUAL:
158 		memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6));
159 		srv_port = ntohs(srv_sa6.sin6_port);
160 		break;
161 	default:
162 		goto done;
163 	}
164 
165 	cli_fd = connect_to_fd(listen_fd, 0);
166 	if (!ASSERT_OK_FD(cli_fd, "connect client"))
167 		goto done;
168 
169 	srv_fd = accept(listen_fd, NULL, NULL);
170 	if (!ASSERT_OK_FD(srv_fd, "accept connection"))
171 		goto done;
172 
173 	ASSERT_EQ(skel->bss->listen_tp_sport, srv_port, "listen tp src port");
174 
175 	if (!gen_cookies) {
176 		ASSERT_EQ(skel->bss->req_sk_sport, srv_port,
177 			  "request socket source port with syncookies disabled");
178 		ASSERT_EQ(skel->bss->gen_cookie, 0,
179 			  "generated syncookie with syncookies disabled");
180 		ASSERT_EQ(skel->bss->recv_cookie, 0,
181 			  "received syncookie with syncookies disabled");
182 	} else {
183 		ASSERT_EQ(skel->bss->req_sk_sport, 0,
184 			  "request socket source port with syncookies enabled");
185 		ASSERT_NEQ(skel->bss->gen_cookie, 0,
186 			   "syncookie properly generated");
187 		ASSERT_EQ(skel->bss->gen_cookie, skel->bss->recv_cookie,
188 			  "matching syncookies on client and server");
189 		ASSERT_GT(skel->bss->mss, MIN_IPV4_MSS,
190 			  "MSS in cookie min value");
191 		ASSERT_LT(skel->bss->mss, USHRT_MAX,
192 			  "MSS in cookie max value");
193 	}
194 
195 done:
196 	if (listen_fd != -1)
197 		close(listen_fd);
198 	if (cli_fd != -1)
199 		close(cli_fd);
200 	if (srv_fd != -1)
201 		close(srv_fd);
202 }
203 
test_conn_ipv4(struct test_btf_skc_cls_ingress * skel)204 static void test_conn_ipv4(struct test_btf_skc_cls_ingress *skel)
205 {
206 	run_test(skel, false, TEST_MODE_IPV4);
207 }
208 
test_conn_ipv6(struct test_btf_skc_cls_ingress * skel)209 static void test_conn_ipv6(struct test_btf_skc_cls_ingress *skel)
210 {
211 	run_test(skel, false, TEST_MODE_IPV6);
212 }
213 
test_conn_dual(struct test_btf_skc_cls_ingress * skel)214 static void test_conn_dual(struct test_btf_skc_cls_ingress *skel)
215 {
216 	run_test(skel, false, TEST_MODE_DUAL);
217 }
218 
test_syncookie_ipv4(struct test_btf_skc_cls_ingress * skel)219 static void test_syncookie_ipv4(struct test_btf_skc_cls_ingress *skel)
220 {
221 	run_test(skel, true, TEST_MODE_IPV4);
222 }
223 
test_syncookie_ipv6(struct test_btf_skc_cls_ingress * skel)224 static void test_syncookie_ipv6(struct test_btf_skc_cls_ingress *skel)
225 {
226 	run_test(skel, true, TEST_MODE_IPV6);
227 }
228 
test_syncookie_dual(struct test_btf_skc_cls_ingress * skel)229 static void test_syncookie_dual(struct test_btf_skc_cls_ingress *skel)
230 {
231 	run_test(skel, true, TEST_MODE_DUAL);
232 }
233 
234 struct test {
235 	const char *desc;
236 	void (*run)(struct test_btf_skc_cls_ingress *skel);
237 };
238 
239 #define DEF_TEST(name) { #name, test_##name }
240 static struct test tests[] = {
241 	DEF_TEST(conn_ipv4),
242 	DEF_TEST(conn_ipv6),
243 	DEF_TEST(conn_dual),
244 	DEF_TEST(syncookie_ipv4),
245 	DEF_TEST(syncookie_ipv6),
246 	DEF_TEST(syncookie_dual),
247 };
248 
test_btf_skc_cls_ingress(void)249 void test_btf_skc_cls_ingress(void)
250 {
251 	struct test_btf_skc_cls_ingress *skel;
252 	struct netns_obj *ns;
253 	int i;
254 
255 	skel = test_btf_skc_cls_ingress__open_and_load();
256 	if (!ASSERT_OK_PTR(skel, "test_btf_skc_cls_ingress__open_and_load"))
257 		return;
258 
259 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
260 		if (!test__start_subtest(tests[i].desc))
261 			continue;
262 
263 		ns = prepare_netns(skel);
264 		if (!ns)
265 			break;
266 
267 		tests[i].run(skel);
268 
269 		print_err_line(skel);
270 		reset_test(skel);
271 		netns_free(ns);
272 	}
273 
274 	test_btf_skc_cls_ingress__destroy(skel);
275 }
276