1 // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause 2 /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ 3 4 #define _GNU_SOURCE 5 #include <test_progs.h> 6 #include <network_helpers.h> 7 #include <ctype.h> 8 9 #define CMD_OUT_BUF_SIZE 1023 10 11 #define SYS(cmd) ({ \ 12 if (!ASSERT_OK(system(cmd), (cmd))) \ 13 goto out; \ 14 }) 15 16 #define SYS_OUT(cmd, ...) ({ \ 17 char buf[1024]; \ 18 snprintf(buf, sizeof(buf), (cmd), ##__VA_ARGS__); \ 19 FILE *f = popen(buf, "r"); \ 20 if (!ASSERT_OK_PTR(f, buf)) \ 21 goto out; \ 22 f; \ 23 }) 24 25 /* out must be at least `size * 4 + 1` bytes long */ 26 static void escape_str(char *out, const char *in, size_t size) 27 { 28 static const char *hex = "0123456789ABCDEF"; 29 size_t i; 30 31 for (i = 0; i < size; i++) { 32 if (isprint(in[i]) && in[i] != '\\' && in[i] != '\'') { 33 *out++ = in[i]; 34 } else { 35 *out++ = '\\'; 36 *out++ = 'x'; 37 *out++ = hex[(in[i] >> 4) & 0xf]; 38 *out++ = hex[in[i] & 0xf]; 39 } 40 } 41 *out++ = '\0'; 42 } 43 44 static bool expect_str(char *buf, size_t size, const char *str, const char *name) 45 { 46 static char escbuf_expected[CMD_OUT_BUF_SIZE * 4]; 47 static char escbuf_actual[CMD_OUT_BUF_SIZE * 4]; 48 static int duration = 0; 49 bool ok; 50 51 ok = size == strlen(str) && !memcmp(buf, str, size); 52 53 if (!ok) { 54 escape_str(escbuf_expected, str, strlen(str)); 55 escape_str(escbuf_actual, buf, size); 56 } 57 CHECK(!ok, name, "unexpected %s: actual '%s' != expected '%s'\n", 58 name, escbuf_actual, escbuf_expected); 59 60 return ok; 61 } 62 63 static void test_synproxy(bool xdp) 64 { 65 int server_fd = -1, client_fd = -1, accept_fd = -1; 66 char *prog_id = NULL, *prog_id_end; 67 struct nstoken *ns = NULL; 68 FILE *ctrl_file = NULL; 69 char buf[CMD_OUT_BUF_SIZE]; 70 size_t size; 71 72 SYS("ip netns add synproxy"); 73 74 SYS("ip link add tmp0 type veth peer name tmp1"); 75 SYS("ip link set tmp1 netns synproxy"); 76 SYS("ip link set tmp0 up"); 77 SYS("ip addr replace 198.18.0.1/24 dev tmp0"); 78 79 /* When checksum offload is enabled, the XDP program sees wrong 80 * checksums and drops packets. 81 */ 82 SYS("ethtool -K tmp0 tx off"); 83 if (xdp) 84 /* Workaround required for veth. */ 85 SYS("ip link set tmp0 xdp object xdp_dummy.bpf.o section xdp 2> /dev/null"); 86 87 ns = open_netns("synproxy"); 88 if (!ASSERT_OK_PTR(ns, "setns")) 89 goto out; 90 91 SYS("ip link set lo up"); 92 SYS("ip link set tmp1 up"); 93 SYS("ip addr replace 198.18.0.2/24 dev tmp1"); 94 SYS("sysctl -w net.ipv4.tcp_syncookies=2"); 95 SYS("sysctl -w net.ipv4.tcp_timestamps=1"); 96 SYS("sysctl -w net.netfilter.nf_conntrack_tcp_loose=0"); 97 SYS("iptables -t raw -I PREROUTING \ 98 -i tmp1 -p tcp -m tcp --syn --dport 8080 -j CT --notrack"); 99 SYS("iptables -t filter -A INPUT \ 100 -i tmp1 -p tcp -m tcp --dport 8080 -m state --state INVALID,UNTRACKED \ 101 -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460"); 102 SYS("iptables -t filter -A INPUT \ 103 -i tmp1 -m state --state INVALID -j DROP"); 104 105 ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 \ 106 --single --mss4 1460 --mss6 1440 \ 107 --wscale 7 --ttl 64%s", xdp ? "" : " --tc"); 108 size = fread(buf, 1, sizeof(buf), ctrl_file); 109 pclose(ctrl_file); 110 if (!expect_str(buf, size, "Total SYNACKs generated: 0\n", 111 "initial SYNACKs")) 112 goto out; 113 114 if (!xdp) { 115 ctrl_file = SYS_OUT("tc filter show dev tmp1 ingress"); 116 size = fread(buf, 1, sizeof(buf), ctrl_file); 117 pclose(ctrl_file); 118 prog_id = memmem(buf, size, " id ", 4); 119 if (!ASSERT_OK_PTR(prog_id, "find prog id")) 120 goto out; 121 prog_id += 4; 122 if (!ASSERT_LT(prog_id, buf + size, "find prog id begin")) 123 goto out; 124 prog_id_end = prog_id; 125 while (prog_id_end < buf + size && *prog_id_end >= '0' && 126 *prog_id_end <= '9') 127 prog_id_end++; 128 if (!ASSERT_LT(prog_id_end, buf + size, "find prog id end")) 129 goto out; 130 *prog_id_end = '\0'; 131 } 132 133 server_fd = start_server(AF_INET, SOCK_STREAM, "198.18.0.2", 8080, 0); 134 if (!ASSERT_GE(server_fd, 0, "start_server")) 135 goto out; 136 137 close_netns(ns); 138 ns = NULL; 139 140 client_fd = connect_to_fd(server_fd, 10000); 141 if (!ASSERT_GE(client_fd, 0, "connect_to_fd")) 142 goto out; 143 144 accept_fd = accept(server_fd, NULL, NULL); 145 if (!ASSERT_GE(accept_fd, 0, "accept")) 146 goto out; 147 148 ns = open_netns("synproxy"); 149 if (!ASSERT_OK_PTR(ns, "setns")) 150 goto out; 151 152 if (xdp) 153 ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --single"); 154 else 155 ctrl_file = SYS_OUT("./xdp_synproxy --prog %s --single", 156 prog_id); 157 size = fread(buf, 1, sizeof(buf), ctrl_file); 158 pclose(ctrl_file); 159 if (!expect_str(buf, size, "Total SYNACKs generated: 1\n", 160 "SYNACKs after connection")) 161 goto out; 162 163 out: 164 if (accept_fd >= 0) 165 close(accept_fd); 166 if (client_fd >= 0) 167 close(client_fd); 168 if (server_fd >= 0) 169 close(server_fd); 170 if (ns) 171 close_netns(ns); 172 173 system("ip link del tmp0"); 174 system("ip netns del synproxy"); 175 } 176 177 void test_xdp_synproxy(void) 178 { 179 if (test__start_subtest("xdp")) 180 test_synproxy(true); 181 if (test__start_subtest("tc")) 182 test_synproxy(false); 183 } 184