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