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