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