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 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 75 static in_port_t 76 get_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 98 static ssize_t 99 rcv_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 109 static int 110 run_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; 160 out: 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 169 static void 170 prepare_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 200 struct 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 220 void 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 288 close: 289 close(server); 290 close(server_map); 291 cleanup: 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 } 298
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.