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
13const unsigned int nr_packets = 1000;
14const unsigned int msg_len = 1000;
15const unsigned int quota = nr_packets * msg_len;
16unsigned int client_new_port;
17
18/* Move them closer to roll-over */
19static 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
40static 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
65static 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 */
159out:
160	close(sk);
161	return NULL;
162}
163
164static 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
241int main(int argc, char *argv[])
242{
243	test_init(7, server_fn, client_fn);
244	return 0;
245}
246