1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2020 Cloudflare
3 /*
4  * Tests for sockmap/sockhash holding kTLS sockets.
5  */
6 
7 #include <netinet/tcp.h>
8 #include "test_progs.h"
9 
10 #define MAX_TEST_NAME 80
11 #define TCP_ULP 31
12 
13 static int tcp_server(int family)
14 {
15 	int err, s;
16 
17 	s = socket(family, SOCK_STREAM, 0);
18 	if (CHECK_FAIL(s == -1)) {
19 		perror("socket");
20 		return -1;
21 	}
22 
23 	err = listen(s, SOMAXCONN);
24 	if (CHECK_FAIL(err)) {
25 		perror("listen");
26 		return -1;
27 	}
28 
29 	return s;
30 }
31 
32 static int disconnect(int fd)
33 {
34 	struct sockaddr unspec = { AF_UNSPEC };
35 
36 	return connect(fd, &unspec, sizeof(unspec));
37 }
38 
39 /* Disconnect (unhash) a kTLS socket after removing it from sockmap. */
40 static void test_sockmap_ktls_disconnect_after_delete(int family, int map)
41 {
42 	struct sockaddr_storage addr = {0};
43 	socklen_t len = sizeof(addr);
44 	int err, cli, srv, zero = 0;
45 
46 	srv = tcp_server(family);
47 	if (srv == -1)
48 		return;
49 
50 	err = getsockname(srv, (struct sockaddr *)&addr, &len);
51 	if (CHECK_FAIL(err)) {
52 		perror("getsockopt");
53 		goto close_srv;
54 	}
55 
56 	cli = socket(family, SOCK_STREAM, 0);
57 	if (CHECK_FAIL(cli == -1)) {
58 		perror("socket");
59 		goto close_srv;
60 	}
61 
62 	err = connect(cli, (struct sockaddr *)&addr, len);
63 	if (CHECK_FAIL(err)) {
64 		perror("connect");
65 		goto close_cli;
66 	}
67 
68 	err = bpf_map_update_elem(map, &zero, &cli, 0);
69 	if (CHECK_FAIL(err)) {
70 		perror("bpf_map_update_elem");
71 		goto close_cli;
72 	}
73 
74 	err = setsockopt(cli, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls"));
75 	if (CHECK_FAIL(err)) {
76 		perror("setsockopt(TCP_ULP)");
77 		goto close_cli;
78 	}
79 
80 	err = bpf_map_delete_elem(map, &zero);
81 	if (CHECK_FAIL(err)) {
82 		perror("bpf_map_delete_elem");
83 		goto close_cli;
84 	}
85 
86 	err = disconnect(cli);
87 	if (CHECK_FAIL(err))
88 		perror("disconnect");
89 
90 close_cli:
91 	close(cli);
92 close_srv:
93 	close(srv);
94 }
95 
96 static void test_sockmap_ktls_update_fails_when_sock_has_ulp(int family, int map)
97 {
98 	struct sockaddr_storage addr = {};
99 	socklen_t len = sizeof(addr);
100 	struct sockaddr_in6 *v6;
101 	struct sockaddr_in *v4;
102 	int err, s, zero = 0;
103 
104 	switch (family) {
105 	case AF_INET:
106 		v4 = (struct sockaddr_in *)&addr;
107 		v4->sin_family = AF_INET;
108 		break;
109 	case AF_INET6:
110 		v6 = (struct sockaddr_in6 *)&addr;
111 		v6->sin6_family = AF_INET6;
112 		break;
113 	default:
114 		PRINT_FAIL("unsupported socket family %d", family);
115 		return;
116 	}
117 
118 	s = socket(family, SOCK_STREAM, 0);
119 	if (!ASSERT_GE(s, 0, "socket"))
120 		return;
121 
122 	err = bind(s, (struct sockaddr *)&addr, len);
123 	if (!ASSERT_OK(err, "bind"))
124 		goto close;
125 
126 	err = getsockname(s, (struct sockaddr *)&addr, &len);
127 	if (!ASSERT_OK(err, "getsockname"))
128 		goto close;
129 
130 	err = connect(s, (struct sockaddr *)&addr, len);
131 	if (!ASSERT_OK(err, "connect"))
132 		goto close;
133 
134 	/* save sk->sk_prot and set it to tls_prots */
135 	err = setsockopt(s, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls"));
136 	if (!ASSERT_OK(err, "setsockopt(TCP_ULP)"))
137 		goto close;
138 
139 	/* sockmap update should not affect saved sk_prot */
140 	err = bpf_map_update_elem(map, &zero, &s, BPF_ANY);
141 	if (!ASSERT_ERR(err, "sockmap update elem"))
142 		goto close;
143 
144 	/* call sk->sk_prot->setsockopt to dispatch to saved sk_prot */
145 	err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &zero, sizeof(zero));
146 	ASSERT_OK(err, "setsockopt(TCP_NODELAY)");
147 
148 close:
149 	close(s);
150 }
151 
152 static const char *fmt_test_name(const char *subtest_name, int family,
153 				 enum bpf_map_type map_type)
154 {
155 	const char *map_type_str = BPF_MAP_TYPE_SOCKMAP ? "SOCKMAP" : "SOCKHASH";
156 	const char *family_str = AF_INET ? "IPv4" : "IPv6";
157 	static char test_name[MAX_TEST_NAME];
158 
159 	snprintf(test_name, MAX_TEST_NAME,
160 		 "sockmap_ktls %s %s %s",
161 		 subtest_name, family_str, map_type_str);
162 
163 	return test_name;
164 }
165 
166 static void run_tests(int family, enum bpf_map_type map_type)
167 {
168 	int map;
169 
170 	map = bpf_map_create(map_type, NULL, sizeof(int), sizeof(int), 1, NULL);
171 	if (CHECK_FAIL(map < 0)) {
172 		perror("bpf_map_create");
173 		return;
174 	}
175 
176 	if (test__start_subtest(fmt_test_name("disconnect_after_delete", family, map_type)))
177 		test_sockmap_ktls_disconnect_after_delete(family, map);
178 	if (test__start_subtest(fmt_test_name("update_fails_when_sock_has_ulp", family, map_type)))
179 		test_sockmap_ktls_update_fails_when_sock_has_ulp(family, map);
180 
181 	close(map);
182 }
183 
184 void test_sockmap_ktls(void)
185 {
186 	run_tests(AF_INET, BPF_MAP_TYPE_SOCKMAP);
187 	run_tests(AF_INET, BPF_MAP_TYPE_SOCKHASH);
188 	run_tests(AF_INET6, BPF_MAP_TYPE_SOCKMAP);
189 	run_tests(AF_INET6, BPF_MAP_TYPE_SOCKHASH);
190 }
191