1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018 Facebook
3 // Copyright (c) 2019 Cloudflare
4 // Copyright (c) 2020 Isovalent, Inc.
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
17 #include "test_progs.h"
19 #define BIND_PORT 1234
20 #define CONNECT_PORT 4321
21 #define TEST_DADDR (0xC0A80203)
22 #define NS_SELF "/proc/self/ns/net"
24 static const struct timeval timeo_sec = { .tv_sec = 3 };
25 static const size_t timeo_optlen = sizeof(timeo_sec);
26 static int stop, duration;
33 /* Move to a new networking namespace */
34 if (CHECK_FAIL(unshare(CLONE_NEWNET)))
37 /* Configure necessary links, routes */
38 if (CHECK_FAIL(system("ip link set dev lo up")))
40 if (CHECK_FAIL(system("ip route add local default dev lo")))
42 if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
45 /* Load qdisc, BPF program */
46 if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
48 sprintf(tc_cmd, "%s %s %s %s", "tc filter add dev lo ingress bpf",
49 "direct-action object-file ./test_sk_assign.o",
50 "section classifier/sk_assign_test",
51 (env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "");
52 if (CHECK(system(tc_cmd), "BPF load failed;",
53 "run with -vv for more info\n"))
60 start_server(const struct sockaddr *addr, socklen_t len, int type)
64 fd = socket(addr->sa_family, type, 0);
65 if (CHECK_FAIL(fd == -1))
67 if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec,
70 if (CHECK_FAIL(bind(fd, addr, len) == -1))
72 if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
84 connect_to_server(const struct sockaddr *addr, socklen_t len, int type)
88 fd = socket(addr->sa_family, type, 0);
89 if (CHECK_FAIL(fd == -1))
91 if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo_sec,
94 if (CHECK_FAIL(connect(fd, addr, len)))
108 struct sockaddr_storage ss;
109 socklen_t slen = sizeof(ss);
112 if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))
115 switch (ss.ss_family) {
117 port = ((struct sockaddr_in *)&ss)->sin_port;
120 port = ((struct sockaddr_in6 *)&ss)->sin6_port;
123 CHECK(1, "Invalid address family", "%d\n", ss.ss_family);
129 rcv_msg(int srv_client, int type)
131 struct sockaddr_storage ss;
135 if (type == SOCK_STREAM)
136 return read(srv_client, &buf, sizeof(buf));
138 return recvfrom(srv_client, &buf, sizeof(buf), 0,
139 (struct sockaddr *)&ss, &slen);
143 run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
145 int client = -1, srv_client = -1;
146 char buf[] = "testing";
150 client = connect_to_server(addr, len, type);
152 perror("Cannot connect to server");
156 if (type == SOCK_STREAM) {
157 srv_client = accept(server_fd, NULL, NULL);
158 if (CHECK_FAIL(srv_client == -1)) {
159 perror("Can't accept connection");
163 srv_client = server_fd;
165 if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
166 perror("Can't write on client");
169 if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
170 perror("Can't read on server");
174 port = get_port(srv_client);
175 if (CHECK_FAIL(!port))
177 /* SOCK_STREAM is connected via accept(), so the server's local address
178 * will be the CONNECT_PORT rather than the BIND port that corresponds
179 * to the listen socket. SOCK_DGRAM on the other hand is connectionless
180 * so we can't really do the same check there; the server doesn't ever
181 * create a socket with CONNECT_PORT.
183 if (type == SOCK_STREAM &&
184 CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
185 CONNECT_PORT, ntohs(port)))
187 else if (type == SOCK_DGRAM &&
188 CHECK(port != htons(BIND_PORT), "Expected",
189 "port %u but got %u", BIND_PORT, ntohs(port)))
195 if (srv_client != server_fd)
203 prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)
205 struct sockaddr_in *addr4;
206 struct sockaddr_in6 *addr6;
210 addr4 = (struct sockaddr_in *)addr;
211 memset(addr4, 0, sizeof(*addr4));
212 addr4->sin_family = family;
213 addr4->sin_port = htons(port);
215 addr4->sin_addr.s_addr = htonl(TEST_DADDR);
217 addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
220 addr6 = (struct sockaddr_in6 *)addr;
221 memset(addr6, 0, sizeof(*addr6));
222 addr6->sin6_family = family;
223 addr6->sin6_port = htons(port);
224 addr6->sin6_addr = in6addr_loopback;
226 addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);
229 fprintf(stderr, "Invalid family %d", family);
236 struct sockaddr *addr;
242 #define TEST(NAME, FAMILY, TYPE, REWRITE) \
246 .addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4 \
247 : (struct sockaddr *)&addr6, \
248 .len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6), \
250 .rewrite_addr = REWRITE, \
253 void test_sk_assign(void)
255 struct sockaddr_in addr4;
256 struct sockaddr_in6 addr6;
257 struct test_sk_cfg tests[] = {
258 TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),
259 TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
260 TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
261 TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
262 TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
263 TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
264 TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
265 TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
270 self_net = open(NS_SELF, O_RDONLY);
271 if (CHECK_FAIL(self_net < 0)) {
272 perror("Unable to open "NS_SELF);
276 if (!configure_stack()) {
277 perror("configure_stack");
281 for (int i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
282 struct test_sk_cfg *test = &tests[i];
283 const struct sockaddr *addr;
285 if (!test__start_subtest(test->name))
287 prepare_addr(test->addr, test->family, BIND_PORT, false);
288 addr = (const struct sockaddr *)test->addr;
289 server = start_server(addr, test->len, test->type);
293 /* connect to unbound ports */
294 prepare_addr(test->addr, test->family, CONNECT_PORT,
296 if (run_test(server, addr, test->len, test->type))
306 if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
307 perror("Failed to setns("NS_SELF")");