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