Skip to content

Commit 0d16eae

Browse files
0x7f454c46davem330
authored andcommitted
selftests/net: Add SEQ number extension test
Check that on SEQ number wraparound there is no disruption or TCPAOBad segments produced. Sample of expected output: > # ./seq-ext_ipv4 > 1..7 > # 1436[lib/setup.c:254] rand seed 1686611079 > TAP version 13 > ok 1 server alive > ok 2 post-migrate connection alive > ok 3 TCPAOGood counter increased 1002 => 3002 > ok 4 TCPAOGood counter increased 1003 => 3003 > ok 5 TCPAOBad counter didn't increase > ok 6 TCPAOBad counter didn't increase > ok 7 SEQ extension incremented: 1/1999, 1/998999 > # Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Dmitry Safonov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3715d32 commit 0d16eae

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed

tools/testing/selftests/net/tcp_ao/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ TEST_BOTH_AF += connect
44
TEST_BOTH_AF += connect-deny
55
TEST_BOTH_AF += icmps-accept icmps-discard
66
TEST_BOTH_AF += restore
7+
TEST_BOTH_AF += seq-ext
78
TEST_BOTH_AF += setsockopt-closed
89
TEST_BOTH_AF += unsigned-md5
910

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Check that after SEQ number wrap-around:
3+
* 1. SEQ-extension has upper bytes set
4+
* 2. TCP conneciton is alive and no TCPAOBad segments
5+
* In order to test (2), the test doesn't just adjust seq number for a queue
6+
* on a connected socket, but migrates it to another sk+port number, so
7+
* that there won't be any delayed packets that will fail to verify
8+
* with the new SEQ numbers.
9+
*/
10+
#include <inttypes.h>
11+
#include "aolib.h"
12+
13+
const unsigned int nr_packets = 1000;
14+
const unsigned int msg_len = 1000;
15+
const unsigned int quota = nr_packets * msg_len;
16+
unsigned int client_new_port;
17+
18+
/* Move them closer to roll-over */
19+
static void test_adjust_seqs(struct tcp_sock_state *img,
20+
struct tcp_ao_repair *ao_img,
21+
bool server)
22+
{
23+
uint32_t new_seq1, new_seq2;
24+
25+
/* make them roll-over during quota, but on different segments */
26+
if (server) {
27+
new_seq1 = ((uint32_t)-1) - msg_len;
28+
new_seq2 = ((uint32_t)-1) - (quota - 2 * msg_len);
29+
} else {
30+
new_seq1 = ((uint32_t)-1) - (quota - 2 * msg_len);
31+
new_seq2 = ((uint32_t)-1) - msg_len;
32+
}
33+
34+
img->in.seq = new_seq1;
35+
img->trw.snd_wl1 = img->in.seq - msg_len;
36+
img->out.seq = new_seq2;
37+
img->trw.rcv_wup = img->in.seq;
38+
}
39+
40+
static int test_sk_restore(struct tcp_sock_state *img,
41+
struct tcp_ao_repair *ao_img, sockaddr_af *saddr,
42+
const union tcp_addr daddr, unsigned int dport,
43+
struct tcp_ao_counters *cnt)
44+
{
45+
int sk;
46+
47+
sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
48+
if (sk < 0)
49+
test_error("socket()");
50+
51+
test_enable_repair(sk);
52+
test_sock_restore(sk, img, saddr, daddr, dport);
53+
if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0, daddr, -1, 100, 100))
54+
test_error("setsockopt(TCP_AO_ADD_KEY)");
55+
test_ao_restore(sk, ao_img);
56+
57+
if (test_get_tcp_ao_counters(sk, cnt))
58+
test_error("test_get_tcp_ao_counters()");
59+
60+
test_disable_repair(sk);
61+
test_sock_state_free(img);
62+
return sk;
63+
}
64+
65+
static void *server_fn(void *arg)
66+
{
67+
uint64_t before_good, after_good, after_bad;
68+
struct tcp_ao_counters ao1, ao2;
69+
struct tcp_sock_state img;
70+
struct tcp_ao_repair ao_img;
71+
sockaddr_af saddr;
72+
ssize_t bytes;
73+
int sk, lsk;
74+
75+
lsk = test_listen_socket(this_ip_addr, test_server_port, 1);
76+
77+
if (test_add_key(lsk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100))
78+
test_error("setsockopt(TCP_AO_ADD_KEY)");
79+
80+
synchronize_threads(); /* 1: MKT added => connect() */
81+
82+
if (test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0))
83+
test_error("test_wait_fd()");
84+
85+
sk = accept(lsk, NULL, NULL);
86+
if (sk < 0)
87+
test_error("accept()");
88+
89+
synchronize_threads(); /* 2: accepted => send data */
90+
close(lsk);
91+
92+
bytes = test_server_run(sk, quota, TEST_TIMEOUT_SEC);
93+
if (bytes != quota) {
94+
if (bytes > 0)
95+
test_fail("server served: %zd", bytes);
96+
else
97+
test_fail("server returned: %zd", bytes);
98+
goto out;
99+
}
100+
101+
before_good = netstat_get_one("TCPAOGood", NULL);
102+
103+
synchronize_threads(); /* 3: restore the connection on another port */
104+
105+
test_enable_repair(sk);
106+
test_sock_checkpoint(sk, &img, &saddr);
107+
test_ao_checkpoint(sk, &ao_img);
108+
test_kill_sk(sk);
109+
#ifdef IPV6_TEST
110+
saddr.sin6_port = htons(ntohs(saddr.sin6_port) + 1);
111+
#else
112+
saddr.sin_port = htons(ntohs(saddr.sin_port) + 1);
113+
#endif
114+
test_adjust_seqs(&img, &ao_img, true);
115+
synchronize_threads(); /* 4: dump finished */
116+
sk = test_sk_restore(&img, &ao_img, &saddr, this_ip_dest,
117+
client_new_port, &ao1);
118+
119+
synchronize_threads(); /* 5: verify counters during SEQ-number rollover */
120+
bytes = test_server_run(sk, quota, TEST_TIMEOUT_SEC);
121+
if (bytes != quota) {
122+
if (bytes > 0)
123+
test_fail("server served: %zd", bytes);
124+
else
125+
test_fail("server returned: %zd", bytes);
126+
} else {
127+
test_ok("server alive");
128+
}
129+
130+
if (test_get_tcp_ao_counters(sk, &ao2))
131+
test_error("test_get_tcp_ao_counters()");
132+
after_good = netstat_get_one("TCPAOGood", NULL);
133+
134+
test_tcp_ao_counters_cmp(NULL, &ao1, &ao2, TEST_CNT_GOOD);
135+
136+
if (after_good <= before_good) {
137+
test_fail("TCPAOGood counter did not increase: %zu <= %zu",
138+
after_good, before_good);
139+
} else {
140+
test_ok("TCPAOGood counter increased %zu => %zu",
141+
before_good, after_good);
142+
}
143+
after_bad = netstat_get_one("TCPAOBad", NULL);
144+
if (after_bad)
145+
test_fail("TCPAOBad counter is non-zero: %zu", after_bad);
146+
else
147+
test_ok("TCPAOBad counter didn't increase");
148+
test_enable_repair(sk);
149+
test_ao_checkpoint(sk, &ao_img);
150+
if (ao_img.snd_sne && ao_img.rcv_sne) {
151+
test_ok("SEQ extension incremented: %u/%u",
152+
ao_img.snd_sne, ao_img.rcv_sne);
153+
} else {
154+
test_fail("SEQ extension was not incremented: %u/%u",
155+
ao_img.snd_sne, ao_img.rcv_sne);
156+
}
157+
158+
synchronize_threads(); /* 6: verified => closed */
159+
out:
160+
close(sk);
161+
return NULL;
162+
}
163+
164+
static void *client_fn(void *arg)
165+
{
166+
uint64_t before_good, after_good, after_bad;
167+
struct tcp_ao_counters ao1, ao2;
168+
struct tcp_sock_state img;
169+
struct tcp_ao_repair ao_img;
170+
sockaddr_af saddr;
171+
int sk;
172+
173+
sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
174+
if (sk < 0)
175+
test_error("socket()");
176+
177+
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100))
178+
test_error("setsockopt(TCP_AO_ADD_KEY)");
179+
180+
synchronize_threads(); /* 1: MKT added => connect() */
181+
if (test_connect_socket(sk, this_ip_dest, test_server_port) <= 0)
182+
test_error("failed to connect()");
183+
184+
synchronize_threads(); /* 2: accepted => send data */
185+
if (test_client_verify(sk, msg_len, nr_packets, TEST_TIMEOUT_SEC)) {
186+
test_fail("pre-migrate verify failed");
187+
return NULL;
188+
}
189+
190+
before_good = netstat_get_one("TCPAOGood", NULL);
191+
192+
synchronize_threads(); /* 3: restore the connection on another port */
193+
test_enable_repair(sk);
194+
test_sock_checkpoint(sk, &img, &saddr);
195+
test_ao_checkpoint(sk, &ao_img);
196+
test_kill_sk(sk);
197+
#ifdef IPV6_TEST
198+
client_new_port = ntohs(saddr.sin6_port) + 1;
199+
saddr.sin6_port = htons(ntohs(saddr.sin6_port) + 1);
200+
#else
201+
client_new_port = ntohs(saddr.sin_port) + 1;
202+
saddr.sin_port = htons(ntohs(saddr.sin_port) + 1);
203+
#endif
204+
test_adjust_seqs(&img, &ao_img, false);
205+
synchronize_threads(); /* 4: dump finished */
206+
sk = test_sk_restore(&img, &ao_img, &saddr, this_ip_dest,
207+
test_server_port + 1, &ao1);
208+
209+
synchronize_threads(); /* 5: verify counters during SEQ-number rollover */
210+
if (test_client_verify(sk, msg_len, nr_packets, TEST_TIMEOUT_SEC))
211+
test_fail("post-migrate verify failed");
212+
else
213+
test_ok("post-migrate connection alive");
214+
215+
if (test_get_tcp_ao_counters(sk, &ao2))
216+
test_error("test_get_tcp_ao_counters()");
217+
after_good = netstat_get_one("TCPAOGood", NULL);
218+
219+
test_tcp_ao_counters_cmp(NULL, &ao1, &ao2, TEST_CNT_GOOD);
220+
221+
if (after_good <= before_good) {
222+
test_fail("TCPAOGood counter did not increase: %zu <= %zu",
223+
after_good, before_good);
224+
} else {
225+
test_ok("TCPAOGood counter increased %zu => %zu",
226+
before_good, after_good);
227+
}
228+
after_bad = netstat_get_one("TCPAOBad", NULL);
229+
if (after_bad)
230+
test_fail("TCPAOBad counter is non-zero: %zu", after_bad);
231+
else
232+
test_ok("TCPAOBad counter didn't increase");
233+
234+
synchronize_threads(); /* 6: verified => closed */
235+
close(sk);
236+
237+
synchronize_threads(); /* don't race to exit: let server exit() */
238+
return NULL;
239+
}
240+
241+
int main(int argc, char *argv[])
242+
{
243+
test_init(7, server_fn, client_fn);
244+
return 0;
245+
}

0 commit comments

Comments
 (0)