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 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; 28 29 static bool 30 configure_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", 50 "direct-action object-file ./test_sk_assign.o", 51 "section classifier/sk_assign_test", 52 (env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : ""); 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 60 static int 61 start_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; 73 if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1)) 74 goto close_out; 75 76 goto out; 77 close_out: 78 close(fd); 79 fd = -1; 80 out: 81 return fd; 82 } 83 84 static int 85 connect_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; 99 close_out: 100 close(fd); 101 fd = -1; 102 out: 103 return fd; 104 } 105 106 static in_port_t 107 get_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 129 static ssize_t 130 rcv_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 143 static int 144 run_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 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; 165 } 166 if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) { 167 perror("Can't write on client"); 168 goto out; 169 } 170 if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) { 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; 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", 186 CONNECT_PORT, ntohs(port))) 187 goto out; 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; 192 193 ret = 0; 194 out: 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 203 static void 204 prepare_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 234 struct 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 254 void 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), 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), 267 }; 268 int server = -1; 269 int server_map; 270 int self_net; 271 272 self_net = open(NS_SELF, O_RDONLY); 273 if (CHECK_FAIL(self_net < 0)) { 274 perror("Unable to open "NS_SELF); 275 return; 276 } 277 278 if (!configure_stack()) { 279 perror("configure_stack"); 280 goto cleanup; 281 } 282 283 server_map = bpf_obj_get(SERVER_MAP_PATH); 284 if (CHECK_FAIL(server_map < 0)) { 285 perror("Unable to open " SERVER_MAP_PATH); 286 goto cleanup; 287 } 288 289 for (int i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) { 290 struct test_sk_cfg *test = &tests[i]; 291 const struct sockaddr *addr; 292 const int zero = 0; 293 int err; 294 295 if (!test__start_subtest(test->name)) 296 continue; 297 prepare_addr(test->addr, test->family, BIND_PORT, false); 298 addr = (const struct sockaddr *)test->addr; 299 server = start_server(addr, test->len, test->type); 300 if (server == -1) 301 goto close; 302 303 err = bpf_map_update_elem(server_map, &zero, &server, BPF_ANY); 304 if (CHECK_FAIL(err)) { 305 perror("Unable to update server_map"); 306 goto close; 307 } 308 309 /* connect to unbound ports */ 310 prepare_addr(test->addr, test->family, CONNECT_PORT, 311 test->rewrite_addr); 312 if (run_test(server, addr, test->len, test->type)) 313 goto close; 314 315 close(server); 316 server = -1; 317 } 318 319 close: 320 close(server); 321 close(server_map); 322 cleanup: 323 if (CHECK_FAIL(unlink(SERVER_MAP_PATH))) 324 perror("Unable to unlink " SERVER_MAP_PATH); 325 if (CHECK_FAIL(setns(self_net, CLONE_NEWNET))) 326 perror("Failed to setns("NS_SELF")"); 327 close(self_net); 328 } 329