0001
0002
0003
0004 #define _GNU_SOURCE
0005 #include <test_progs.h>
0006 #include <network_helpers.h>
0007 #include <ctype.h>
0008
0009 #define CMD_OUT_BUF_SIZE 1023
0010
0011 #define SYS(cmd) ({ \
0012 if (!ASSERT_OK(system(cmd), (cmd))) \
0013 goto out; \
0014 })
0015
0016 #define SYS_OUT(cmd, ...) ({ \
0017 char buf[1024]; \
0018 snprintf(buf, sizeof(buf), (cmd), ##__VA_ARGS__); \
0019 FILE *f = popen(buf, "r"); \
0020 if (!ASSERT_OK_PTR(f, buf)) \
0021 goto out; \
0022 f; \
0023 })
0024
0025
0026 static void escape_str(char *out, const char *in, size_t size)
0027 {
0028 static const char *hex = "0123456789ABCDEF";
0029 size_t i;
0030
0031 for (i = 0; i < size; i++) {
0032 if (isprint(in[i]) && in[i] != '\\' && in[i] != '\'') {
0033 *out++ = in[i];
0034 } else {
0035 *out++ = '\\';
0036 *out++ = 'x';
0037 *out++ = hex[(in[i] >> 4) & 0xf];
0038 *out++ = hex[in[i] & 0xf];
0039 }
0040 }
0041 *out++ = '\0';
0042 }
0043
0044 static bool expect_str(char *buf, size_t size, const char *str, const char *name)
0045 {
0046 static char escbuf_expected[CMD_OUT_BUF_SIZE * 4];
0047 static char escbuf_actual[CMD_OUT_BUF_SIZE * 4];
0048 static int duration = 0;
0049 bool ok;
0050
0051 ok = size == strlen(str) && !memcmp(buf, str, size);
0052
0053 if (!ok) {
0054 escape_str(escbuf_expected, str, strlen(str));
0055 escape_str(escbuf_actual, buf, size);
0056 }
0057 CHECK(!ok, name, "unexpected %s: actual '%s' != expected '%s'\n",
0058 name, escbuf_actual, escbuf_expected);
0059
0060 return ok;
0061 }
0062
0063 static void test_synproxy(bool xdp)
0064 {
0065 int server_fd = -1, client_fd = -1, accept_fd = -1;
0066 char *prog_id = NULL, *prog_id_end;
0067 struct nstoken *ns = NULL;
0068 FILE *ctrl_file = NULL;
0069 char buf[CMD_OUT_BUF_SIZE];
0070 size_t size;
0071
0072 SYS("ip netns add synproxy");
0073
0074 SYS("ip link add tmp0 type veth peer name tmp1");
0075 SYS("ip link set tmp1 netns synproxy");
0076 SYS("ip link set tmp0 up");
0077 SYS("ip addr replace 198.18.0.1/24 dev tmp0");
0078
0079
0080
0081
0082 SYS("ethtool -K tmp0 tx off");
0083 if (xdp)
0084
0085 SYS("ip link set tmp0 xdp object xdp_dummy.o section xdp 2> /dev/null");
0086
0087 ns = open_netns("synproxy");
0088 if (!ASSERT_OK_PTR(ns, "setns"))
0089 goto out;
0090
0091 SYS("ip link set lo up");
0092 SYS("ip link set tmp1 up");
0093 SYS("ip addr replace 198.18.0.2/24 dev tmp1");
0094 SYS("sysctl -w net.ipv4.tcp_syncookies=2");
0095 SYS("sysctl -w net.ipv4.tcp_timestamps=1");
0096 SYS("sysctl -w net.netfilter.nf_conntrack_tcp_loose=0");
0097 SYS("iptables -t raw -I PREROUTING \
0098 -i tmp1 -p tcp -m tcp --syn --dport 8080 -j CT --notrack");
0099 SYS("iptables -t filter -A INPUT \
0100 -i tmp1 -p tcp -m tcp --dport 8080 -m state --state INVALID,UNTRACKED \
0101 -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460");
0102 SYS("iptables -t filter -A INPUT \
0103 -i tmp1 -m state --state INVALID -j DROP");
0104
0105 ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 \
0106 --single --mss4 1460 --mss6 1440 \
0107 --wscale 7 --ttl 64%s", xdp ? "" : " --tc");
0108 size = fread(buf, 1, sizeof(buf), ctrl_file);
0109 pclose(ctrl_file);
0110 if (!expect_str(buf, size, "Total SYNACKs generated: 0\n",
0111 "initial SYNACKs"))
0112 goto out;
0113
0114 if (!xdp) {
0115 ctrl_file = SYS_OUT("tc filter show dev tmp1 ingress");
0116 size = fread(buf, 1, sizeof(buf), ctrl_file);
0117 pclose(ctrl_file);
0118 prog_id = memmem(buf, size, " id ", 4);
0119 if (!ASSERT_OK_PTR(prog_id, "find prog id"))
0120 goto out;
0121 prog_id += 4;
0122 if (!ASSERT_LT(prog_id, buf + size, "find prog id begin"))
0123 goto out;
0124 prog_id_end = prog_id;
0125 while (prog_id_end < buf + size && *prog_id_end >= '0' &&
0126 *prog_id_end <= '9')
0127 prog_id_end++;
0128 if (!ASSERT_LT(prog_id_end, buf + size, "find prog id end"))
0129 goto out;
0130 *prog_id_end = '\0';
0131 }
0132
0133 server_fd = start_server(AF_INET, SOCK_STREAM, "198.18.0.2", 8080, 0);
0134 if (!ASSERT_GE(server_fd, 0, "start_server"))
0135 goto out;
0136
0137 close_netns(ns);
0138 ns = NULL;
0139
0140 client_fd = connect_to_fd(server_fd, 10000);
0141 if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
0142 goto out;
0143
0144 accept_fd = accept(server_fd, NULL, NULL);
0145 if (!ASSERT_GE(accept_fd, 0, "accept"))
0146 goto out;
0147
0148 ns = open_netns("synproxy");
0149 if (!ASSERT_OK_PTR(ns, "setns"))
0150 goto out;
0151
0152 if (xdp)
0153 ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --single");
0154 else
0155 ctrl_file = SYS_OUT("./xdp_synproxy --prog %s --single",
0156 prog_id);
0157 size = fread(buf, 1, sizeof(buf), ctrl_file);
0158 pclose(ctrl_file);
0159 if (!expect_str(buf, size, "Total SYNACKs generated: 1\n",
0160 "SYNACKs after connection"))
0161 goto out;
0162
0163 out:
0164 if (accept_fd >= 0)
0165 close(accept_fd);
0166 if (client_fd >= 0)
0167 close(client_fd);
0168 if (server_fd >= 0)
0169 close(server_fd);
0170 if (ns)
0171 close_netns(ns);
0172
0173 system("ip link del tmp0");
0174 system("ip netns del synproxy");
0175 }
0176
0177 void test_xdp_synproxy(void)
0178 {
0179 if (test__start_subtest("xdp"))
0180 test_synproxy(true);
0181 if (test__start_subtest("tc"))
0182 test_synproxy(false);
0183 }