xref: /linux/tools/testing/selftests/net/tcp_ao/seq-ext.c (revision eb01fe7abbe2d0b38824d2a93fdb4cc3eaf2ccc1)
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 }
246