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"
23 #define SERVER_MAP_PATH "/sys/fs/bpf/tc/globals/server_map"
25 static const struct timeval timeo_sec
= { .tv_sec
= 3 };
26 static const size_t timeo_optlen
= sizeof(timeo_sec
);
27 static int stop
, duration
;
34 /* Move to a new networking namespace */
35 if (CHECK_FAIL(unshare(CLONE_NEWNET
)))
38 /* Configure necessary links, routes */
39 if (CHECK_FAIL(system("ip link set dev lo up")))
41 if (CHECK_FAIL(system("ip route add local default dev lo")))
43 if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
46 /* Load qdisc, BPF program */
47 if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
49 sprintf(tc_cmd
, "%s %s %s %s", "tc filter add dev lo ingress bpf",
50 "direct-action object-file ./test_sk_assign.o",
51 "section classifier/sk_assign_test",
52 (env
.verbosity
< VERBOSE_VERY
) ? " 2>/dev/null" : "verbose");
53 if (CHECK(system(tc_cmd
), "BPF load failed;",
54 "run with -vv for more info\n"))
61 start_server(const struct sockaddr
*addr
, socklen_t len
, int type
)
65 fd
= socket(addr
->sa_family
, type
, 0);
66 if (CHECK_FAIL(fd
== -1))
68 if (CHECK_FAIL(setsockopt(fd
, SOL_SOCKET
, SO_RCVTIMEO
, &timeo_sec
,
71 if (CHECK_FAIL(bind(fd
, addr
, len
) == -1))
73 if (type
== SOCK_STREAM
&& CHECK_FAIL(listen(fd
, 128) == -1))
85 connect_to_server(const struct sockaddr
*addr
, socklen_t len
, int type
)
89 fd
= socket(addr
->sa_family
, type
, 0);
90 if (CHECK_FAIL(fd
== -1))
92 if (CHECK_FAIL(setsockopt(fd
, SOL_SOCKET
, SO_SNDTIMEO
, &timeo_sec
,
95 if (CHECK_FAIL(connect(fd
, addr
, len
)))
109 struct sockaddr_storage ss
;
110 socklen_t slen
= sizeof(ss
);
113 if (CHECK_FAIL(getsockname(fd
, (struct sockaddr
*)&ss
, &slen
)))
116 switch (ss
.ss_family
) {
118 port
= ((struct sockaddr_in
*)&ss
)->sin_port
;
121 port
= ((struct sockaddr_in6
*)&ss
)->sin6_port
;
124 CHECK(1, "Invalid address family", "%d\n", ss
.ss_family
);
130 rcv_msg(int srv_client
, int type
)
132 struct sockaddr_storage ss
;
136 if (type
== SOCK_STREAM
)
137 return read(srv_client
, &buf
, sizeof(buf
));
139 return recvfrom(srv_client
, &buf
, sizeof(buf
), 0,
140 (struct sockaddr
*)&ss
, &slen
);
144 run_test(int server_fd
, const struct sockaddr
*addr
, socklen_t len
, int type
)
146 int client
= -1, srv_client
= -1;
147 char buf
[] = "testing";
151 client
= connect_to_server(addr
, len
, type
);
153 perror("Cannot connect to server");
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");
164 srv_client
= server_fd
;
166 if (CHECK_FAIL(write(client
, buf
, sizeof(buf
)) != sizeof(buf
))) {
167 perror("Can't write on client");
170 if (CHECK_FAIL(rcv_msg(srv_client
, type
) != sizeof(buf
))) {
171 perror("Can't read on server");
175 port
= get_port(srv_client
);
176 if (CHECK_FAIL(!port
))
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.
184 if (type
== SOCK_STREAM
&&
185 CHECK(port
!= htons(CONNECT_PORT
), "Expected", "port %u but got %u",
186 CONNECT_PORT
, ntohs(port
)))
188 else if (type
== SOCK_DGRAM
&&
189 CHECK(port
!= htons(BIND_PORT
), "Expected",
190 "port %u but got %u", BIND_PORT
, ntohs(port
)))
196 if (srv_client
!= server_fd
)
204 prepare_addr(struct sockaddr
*addr
, int family
, __u16 port
, bool rewrite_addr
)
206 struct sockaddr_in
*addr4
;
207 struct sockaddr_in6
*addr6
;
211 addr4
= (struct sockaddr_in
*)addr
;
212 memset(addr4
, 0, sizeof(*addr4
));
213 addr4
->sin_family
= family
;
214 addr4
->sin_port
= htons(port
);
216 addr4
->sin_addr
.s_addr
= htonl(TEST_DADDR
);
218 addr4
->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
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
;
227 addr6
->sin6_addr
.s6_addr32
[3] = htonl(TEST_DADDR
);
230 fprintf(stderr
, "Invalid family %d", family
);
237 struct sockaddr
*addr
;
243 #define TEST(NAME, FAMILY, TYPE, REWRITE) \
247 .addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4 \
248 : (struct sockaddr *)&addr6, \
249 .len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6), \
251 .rewrite_addr = REWRITE, \
254 void test_sk_assign(void)
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),
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),
273 self_net
= open(NS_SELF
, O_RDONLY
);
274 if (CHECK_FAIL(self_net
< 0)) {
275 perror("Unable to open "NS_SELF
);
279 if (!configure_stack()) {
280 perror("configure_stack");
284 server_map
= bpf_obj_get(SERVER_MAP_PATH
);
285 if (CHECK_FAIL(server_map
< 0)) {
286 perror("Unable to open " SERVER_MAP_PATH
);
290 for (i
= 0; i
< ARRAY_SIZE(tests
) && !READ_ONCE(stop
); i
++) {
291 struct test_sk_cfg
*test
= &tests
[i
];
292 const struct sockaddr
*addr
;
296 if (!test__start_subtest(test
->name
))
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
);
304 err
= bpf_map_update_elem(server_map
, &zero
, &server
, BPF_ANY
);
305 if (CHECK_FAIL(err
)) {
306 perror("Unable to update server_map");
310 /* connect to unbound ports */
311 prepare_addr(test
->addr
, test
->family
, CONNECT_PORT
,
313 if (run_test(server
, addr
, test
->len
, test
->type
))
324 if (CHECK_FAIL(unlink(SERVER_MAP_PATH
)))
325 perror("Unable to unlink " SERVER_MAP_PATH
);
326 if (CHECK_FAIL(setns(self_net
, CLONE_NEWNET
)))
327 perror("Failed to setns("NS_SELF
")");