Loading...
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 */
21static 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
39static 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
58static 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
158out:
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
172void 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}
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 */
26static 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
44static 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
63static 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-legacy -t raw -I PREROUTING \
98 -i tmp1 -p tcp -m tcp --syn --dport 8080 -j CT --notrack");
99 SYS("iptables-legacy -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-legacy -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
163out:
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
177void 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}