1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018 Facebook
3 // Copyright (c) 2019 Cloudflare
4 // Copyright (c) 2020 Isovalent, Inc.
5 /*
6 * Test that the socket assign program is able to redirect traffic towards a
7 * socket, regardless of whether the port or address destination of the traffic
8 * matches the port.
9 */
10
11 #define _GNU_SOURCE
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 #include "test_progs.h"
18 #include "network_helpers.h"
19
20 #define BIND_PORT 1234
21 #define CONNECT_PORT 4321
22 #define TEST_DADDR (0xC0A80203)
23 #define NS_SELF "/proc/self/ns/net"
24 #define SERVER_MAP_PATH "/sys/fs/bpf/tc/globals/server_map"
25
26 static int stop, duration;
27
28 static bool
configure_stack(void)29 configure_stack(void)
30 {
31 char tc_version[128];
32 char tc_cmd[BUFSIZ];
33 char *prog;
34 FILE *tc;
35
36 /* Check whether tc is built with libbpf. */
37 tc = popen("tc -V", "r");
38 if (CHECK_FAIL(!tc))
39 return false;
40 if (CHECK_FAIL(!fgets(tc_version, sizeof(tc_version), tc))) {
41 pclose(tc);
42 return false;
43 }
44 if (strstr(tc_version, ", libbpf "))
45 prog = "test_sk_assign_libbpf.bpf.o";
46 else
47 prog = "test_sk_assign.bpf.o";
48 if (CHECK_FAIL(pclose(tc)))
49 return false;
50
51 /* Move to a new networking namespace */
52 if (CHECK_FAIL(unshare(CLONE_NEWNET)))
53 return false;
54
55 /* Configure necessary links, routes */
56 if (CHECK_FAIL(system("ip link set dev lo up")))
57 return false;
58 if (CHECK_FAIL(system("ip route add local default dev lo")))
59 return false;
60 if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
61 return false;
62
63 /* Load qdisc, BPF program */
64 if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
65 return false;
66 sprintf(tc_cmd, "%s %s %s %s %s", "tc filter add dev lo ingress bpf",
67 "direct-action object-file", prog,
68 "section tc",
69 (env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "verbose");
70 if (CHECK(system(tc_cmd), "BPF load failed;",
71 "run with -vv for more info\n"))
72 return false;
73
74 return true;
75 }
76
77 static in_port_t
get_port(int fd)78 get_port(int fd)
79 {
80 struct sockaddr_storage ss;
81 socklen_t slen = sizeof(ss);
82 in_port_t port = 0;
83
84 if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))
85 return port;
86
87 switch (ss.ss_family) {
88 case AF_INET:
89 port = ((struct sockaddr_in *)&ss)->sin_port;
90 break;
91 case AF_INET6:
92 port = ((struct sockaddr_in6 *)&ss)->sin6_port;
93 break;
94 default:
95 CHECK(1, "Invalid address family", "%d\n", ss.ss_family);
96 }
97 return port;
98 }
99
100 static ssize_t
rcv_msg(int srv_client,int type)101 rcv_msg(int srv_client, int type)
102 {
103 char buf[BUFSIZ];
104
105 if (type == SOCK_STREAM)
106 return read(srv_client, &buf, sizeof(buf));
107 else
108 return recvfrom(srv_client, &buf, sizeof(buf), 0, NULL, NULL);
109 }
110
111 static int
run_test(int server_fd,const struct sockaddr * addr,socklen_t len,int type)112 run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
113 {
114 int client = -1, srv_client = -1;
115 char buf[] = "testing";
116 in_port_t port;
117 int ret = 1;
118
119 client = connect_to_addr(type, (struct sockaddr_storage *)addr, len, NULL);
120 if (client == -1) {
121 perror("Cannot connect to server");
122 goto out;
123 }
124
125 if (type == SOCK_STREAM) {
126 srv_client = accept(server_fd, NULL, NULL);
127 if (CHECK_FAIL(srv_client == -1)) {
128 perror("Can't accept connection");
129 goto out;
130 }
131 } else {
132 srv_client = server_fd;
133 }
134 if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
135 perror("Can't write on client");
136 goto out;
137 }
138 if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
139 perror("Can't read on server");
140 goto out;
141 }
142
143 port = get_port(srv_client);
144 if (CHECK_FAIL(!port))
145 goto out;
146 /* SOCK_STREAM is connected via accept(), so the server's local address
147 * will be the CONNECT_PORT rather than the BIND port that corresponds
148 * to the listen socket. SOCK_DGRAM on the other hand is connectionless
149 * so we can't really do the same check there; the server doesn't ever
150 * create a socket with CONNECT_PORT.
151 */
152 if (type == SOCK_STREAM &&
153 CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
154 CONNECT_PORT, ntohs(port)))
155 goto out;
156 else if (type == SOCK_DGRAM &&
157 CHECK(port != htons(BIND_PORT), "Expected",
158 "port %u but got %u", BIND_PORT, ntohs(port)))
159 goto out;
160
161 ret = 0;
162 out:
163 close(client);
164 if (srv_client != server_fd)
165 close(srv_client);
166 if (ret)
167 WRITE_ONCE(stop, 1);
168 return ret;
169 }
170
171 static void
prepare_addr(struct sockaddr * addr,int family,__u16 port,bool rewrite_addr)172 prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)
173 {
174 struct sockaddr_in *addr4;
175 struct sockaddr_in6 *addr6;
176
177 switch (family) {
178 case AF_INET:
179 addr4 = (struct sockaddr_in *)addr;
180 memset(addr4, 0, sizeof(*addr4));
181 addr4->sin_family = family;
182 addr4->sin_port = htons(port);
183 if (rewrite_addr)
184 addr4->sin_addr.s_addr = htonl(TEST_DADDR);
185 else
186 addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
187 break;
188 case AF_INET6:
189 addr6 = (struct sockaddr_in6 *)addr;
190 memset(addr6, 0, sizeof(*addr6));
191 addr6->sin6_family = family;
192 addr6->sin6_port = htons(port);
193 addr6->sin6_addr = in6addr_loopback;
194 if (rewrite_addr)
195 addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);
196 break;
197 default:
198 fprintf(stderr, "Invalid family %d", family);
199 }
200 }
201
202 struct test_sk_cfg {
203 const char *name;
204 int family;
205 struct sockaddr *addr;
206 socklen_t len;
207 int type;
208 bool rewrite_addr;
209 };
210
211 #define TEST(NAME, FAMILY, TYPE, REWRITE) \
212 { \
213 .name = NAME, \
214 .family = FAMILY, \
215 .addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4 \
216 : (struct sockaddr *)&addr6, \
217 .len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6), \
218 .type = TYPE, \
219 .rewrite_addr = REWRITE, \
220 }
221
test_sk_assign(void)222 void test_sk_assign(void)
223 {
224 struct sockaddr_in addr4;
225 struct sockaddr_in6 addr6;
226 struct test_sk_cfg tests[] = {
227 TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),
228 TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
229 TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
230 TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
231 TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
232 TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
233 TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
234 TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
235 };
236 __s64 server = -1;
237 int server_map;
238 int self_net;
239 int i;
240
241 self_net = open(NS_SELF, O_RDONLY);
242 if (CHECK_FAIL(self_net < 0)) {
243 perror("Unable to open "NS_SELF);
244 return;
245 }
246
247 if (!configure_stack()) {
248 perror("configure_stack");
249 goto cleanup;
250 }
251
252 server_map = bpf_obj_get(SERVER_MAP_PATH);
253 if (CHECK_FAIL(server_map < 0)) {
254 perror("Unable to open " SERVER_MAP_PATH);
255 goto cleanup;
256 }
257
258 for (i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
259 struct test_sk_cfg *test = &tests[i];
260 const struct sockaddr *addr;
261 const int zero = 0;
262 int err;
263
264 if (!test__start_subtest(test->name))
265 continue;
266 prepare_addr(test->addr, test->family, BIND_PORT, false);
267 addr = (const struct sockaddr *)test->addr;
268 server = start_server_addr(test->type,
269 (const struct sockaddr_storage *)addr,
270 test->len, NULL);
271 if (server == -1)
272 goto close;
273
274 err = bpf_map_update_elem(server_map, &zero, &server, BPF_ANY);
275 if (CHECK_FAIL(err)) {
276 perror("Unable to update server_map");
277 goto close;
278 }
279
280 /* connect to unbound ports */
281 prepare_addr(test->addr, test->family, CONNECT_PORT,
282 test->rewrite_addr);
283 if (run_test(server, addr, test->len, test->type))
284 goto close;
285
286 close(server);
287 server = -1;
288 }
289
290 close:
291 close(server);
292 close(server_map);
293 cleanup:
294 if (CHECK_FAIL(unlink(SERVER_MAP_PATH)))
295 perror("Unable to unlink " SERVER_MAP_PATH);
296 if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
297 perror("Failed to setns("NS_SELF")");
298 close(self_net);
299 }
300