1 // SPDX-License-Identifier: GPL-2.0
2 /* Author: Dmitry Safonov <dima@arista.com> */
3 /* This is over-simplified TCP_REPAIR for TCP_ESTABLISHED sockets
4 * It tests that TCP-AO enabled connection can be restored.
5 * For the proper socket repair see:
6 * https://github.com/checkpoint-restore/criu/blob/criu-dev/soccr/soccr.h
11 const size_t nr_packets
= 20;
12 const size_t msg_len
= 100;
13 const size_t quota
= nr_packets
* msg_len
;
14 #define fault(type) (inj == FAULT_ ## type)
16 static void try_server_run(const char *tst_name
, unsigned int port
,
17 fault_t inj
, test_cnt cnt_expected
)
19 const char *cnt_name
= "TCPAOGood";
20 struct tcp_ao_counters ao1
, ao2
;
21 uint64_t before_cnt
, after_cnt
;
27 cnt_name
= "TCPAOBad";
28 lsk
= test_listen_socket(this_ip_addr
, port
, 1);
30 if (test_add_key(lsk
, DEFAULT_TEST_PASSWORD
, this_ip_dest
, -1, 100, 100))
31 test_error("setsockopt(TCP_AO_ADD_KEY)");
32 synchronize_threads(); /* 1: MKT added => connect() */
34 if (test_wait_fd(lsk
, TEST_TIMEOUT_SEC
, 0))
35 test_error("test_wait_fd()");
37 sk
= accept(lsk
, NULL
, NULL
);
39 test_error("accept()");
41 synchronize_threads(); /* 2: accepted => send data */
44 bytes
= test_server_run(sk
, quota
, TEST_TIMEOUT_SEC
);
46 test_fail("%s: server served: %zd", tst_name
, bytes
);
50 before_cnt
= netstat_get_one(cnt_name
, NULL
);
51 if (test_get_tcp_ao_counters(sk
, &ao1
))
52 test_error("test_get_tcp_ao_counters()");
54 timeout
= fault(TIMEOUT
) ? TEST_RETRANSMIT_SEC
: TEST_TIMEOUT_SEC
;
55 bytes
= test_server_run(sk
, quota
, timeout
);
58 test_fail("%s: server served: %zd", tst_name
, bytes
);
60 test_ok("%s: server couldn't serve", tst_name
);
63 test_fail("%s: server served: %zd", tst_name
, bytes
);
65 test_ok("%s: server alive", tst_name
);
67 synchronize_threads(); /* 3: counters checks */
68 if (test_get_tcp_ao_counters(sk
, &ao2
))
69 test_error("test_get_tcp_ao_counters()");
70 after_cnt
= netstat_get_one(cnt_name
, NULL
);
72 test_tcp_ao_counters_cmp(tst_name
, &ao1
, &ao2
, cnt_expected
);
74 if (after_cnt
<= before_cnt
) {
75 test_fail("%s: %s counter did not increase: %" PRIu64
" <= %" PRIu64
,
76 tst_name
, cnt_name
, after_cnt
, before_cnt
);
78 test_ok("%s: counter %s increased %" PRIu64
" => %" PRIu64
,
79 tst_name
, cnt_name
, before_cnt
, after_cnt
);
83 * Before close() as that will send FIN and move the peer in TCP_CLOSE
84 * and that will prevent reading AO counters from the peer's socket.
86 synchronize_threads(); /* 4: verified => closed */
91 static void *server_fn(void *arg
)
93 unsigned int port
= test_server_port
;
95 try_server_run("TCP-AO migrate to another socket", port
++,
97 try_server_run("TCP-AO with wrong send ISN", port
++,
98 FAULT_TIMEOUT
, TEST_CNT_BAD
);
99 try_server_run("TCP-AO with wrong receive ISN", port
++,
100 FAULT_TIMEOUT
, TEST_CNT_BAD
);
101 try_server_run("TCP-AO with wrong send SEQ ext number", port
++,
102 FAULT_TIMEOUT
, TEST_CNT_BAD
);
103 try_server_run("TCP-AO with wrong receive SEQ ext number", port
++,
104 FAULT_TIMEOUT
, TEST_CNT_NS_BAD
| TEST_CNT_GOOD
);
106 synchronize_threads(); /* don't race to exit: client exits */
110 static void test_get_sk_checkpoint(unsigned int server_port
, sockaddr_af
*saddr
,
111 struct tcp_sock_state
*img
,
112 struct tcp_ao_repair
*ao_img
)
116 sk
= socket(test_family
, SOCK_STREAM
, IPPROTO_TCP
);
118 test_error("socket()");
120 if (test_add_key(sk
, DEFAULT_TEST_PASSWORD
, this_ip_dest
, -1, 100, 100))
121 test_error("setsockopt(TCP_AO_ADD_KEY)");
123 synchronize_threads(); /* 1: MKT added => connect() */
124 if (test_connect_socket(sk
, this_ip_dest
, server_port
) <= 0)
125 test_error("failed to connect()");
127 synchronize_threads(); /* 2: accepted => send data */
128 if (test_client_verify(sk
, msg_len
, nr_packets
, TEST_TIMEOUT_SEC
))
129 test_fail("pre-migrate verify failed");
131 test_enable_repair(sk
);
132 test_sock_checkpoint(sk
, img
, saddr
);
133 test_ao_checkpoint(sk
, ao_img
);
137 static void test_sk_restore(const char *tst_name
, unsigned int server_port
,
138 sockaddr_af
*saddr
, struct tcp_sock_state
*img
,
139 struct tcp_ao_repair
*ao_img
,
140 fault_t inj
, test_cnt cnt_expected
)
142 const char *cnt_name
= "TCPAOGood";
143 struct tcp_ao_counters ao1
, ao2
;
144 uint64_t before_cnt
, after_cnt
;
149 cnt_name
= "TCPAOBad";
151 before_cnt
= netstat_get_one(cnt_name
, NULL
);
152 sk
= socket(test_family
, SOCK_STREAM
, IPPROTO_TCP
);
154 test_error("socket()");
156 test_enable_repair(sk
);
157 test_sock_restore(sk
, img
, saddr
, this_ip_dest
, server_port
);
158 if (test_add_repaired_key(sk
, DEFAULT_TEST_PASSWORD
, 0, this_ip_dest
, -1, 100, 100))
159 test_error("setsockopt(TCP_AO_ADD_KEY)");
160 test_ao_restore(sk
, ao_img
);
162 if (test_get_tcp_ao_counters(sk
, &ao1
))
163 test_error("test_get_tcp_ao_counters()");
165 test_disable_repair(sk
);
166 test_sock_state_free(img
);
168 timeout
= fault(TIMEOUT
) ? TEST_RETRANSMIT_SEC
: TEST_TIMEOUT_SEC
;
169 if (test_client_verify(sk
, msg_len
, nr_packets
, timeout
)) {
171 test_ok("%s: post-migrate connection is broken", tst_name
);
173 test_fail("%s: post-migrate connection is working", tst_name
);
176 test_fail("%s: post-migrate connection still working", tst_name
);
178 test_ok("%s: post-migrate connection is alive", tst_name
);
180 synchronize_threads(); /* 3: counters checks */
181 if (test_get_tcp_ao_counters(sk
, &ao2
))
182 test_error("test_get_tcp_ao_counters()");
183 after_cnt
= netstat_get_one(cnt_name
, NULL
);
185 test_tcp_ao_counters_cmp(tst_name
, &ao1
, &ao2
, cnt_expected
);
187 if (after_cnt
<= before_cnt
) {
188 test_fail("%s: %s counter did not increase: %" PRIu64
" <= %" PRIu64
,
189 tst_name
, cnt_name
, after_cnt
, before_cnt
);
191 test_ok("%s: counter %s increased %" PRIu64
" => %" PRIu64
,
192 tst_name
, cnt_name
, before_cnt
, after_cnt
);
194 synchronize_threads(); /* 4: verified => closed */
198 static void *client_fn(void *arg
)
200 unsigned int port
= test_server_port
;
201 struct tcp_sock_state tcp_img
;
202 struct tcp_ao_repair ao_img
;
205 test_get_sk_checkpoint(port
, &saddr
, &tcp_img
, &ao_img
);
206 test_sk_restore("TCP-AO migrate to another socket", port
++,
207 &saddr
, &tcp_img
, &ao_img
, 0, TEST_CNT_GOOD
);
209 test_get_sk_checkpoint(port
, &saddr
, &tcp_img
, &ao_img
);
211 trace_ao_event_expect(TCP_AO_MISMATCH
, this_ip_addr
, this_ip_dest
,
212 -1, port
, 0, -1, -1, -1, -1, -1, 100, 100, -1);
213 trace_ao_event_expect(TCP_AO_MISMATCH
, this_ip_dest
, this_ip_addr
,
214 port
, -1, 0, -1, -1, -1, -1, -1, 100, 100, -1);
215 test_sk_restore("TCP-AO with wrong send ISN", port
++,
216 &saddr
, &tcp_img
, &ao_img
, FAULT_TIMEOUT
, TEST_CNT_BAD
);
218 test_get_sk_checkpoint(port
, &saddr
, &tcp_img
, &ao_img
);
220 trace_ao_event_expect(TCP_AO_MISMATCH
, this_ip_addr
, this_ip_dest
,
221 -1, port
, 0, -1, -1, -1, -1, -1, 100, 100, -1);
222 trace_ao_event_expect(TCP_AO_MISMATCH
, this_ip_dest
, this_ip_addr
,
223 port
, -1, 0, -1, -1, -1, -1, -1, 100, 100, -1);
224 test_sk_restore("TCP-AO with wrong receive ISN", port
++,
225 &saddr
, &tcp_img
, &ao_img
, FAULT_TIMEOUT
, TEST_CNT_BAD
);
227 test_get_sk_checkpoint(port
, &saddr
, &tcp_img
, &ao_img
);
229 trace_ao_event_expect(TCP_AO_MISMATCH
, this_ip_addr
, this_ip_dest
,
230 -1, port
, 0, -1, -1, -1, -1, -1, 100, 100, -1);
231 /* not expecting server => client mismatches as only snd sne is broken */
232 test_sk_restore("TCP-AO with wrong send SEQ ext number", port
++,
233 &saddr
, &tcp_img
, &ao_img
, FAULT_TIMEOUT
,
234 TEST_CNT_NS_BAD
| TEST_CNT_GOOD
);
236 test_get_sk_checkpoint(port
, &saddr
, &tcp_img
, &ao_img
);
238 /* not expecting client => server mismatches as only rcv sne is broken */
239 trace_ao_event_expect(TCP_AO_MISMATCH
, this_ip_dest
, this_ip_addr
,
240 port
, -1, 0, -1, -1, -1, -1, -1, 100, 100, -1);
241 test_sk_restore("TCP-AO with wrong receive SEQ ext number", port
++,
242 &saddr
, &tcp_img
, &ao_img
, FAULT_TIMEOUT
,
243 TEST_CNT_NS_GOOD
| TEST_CNT_BAD
);
248 int main(int argc
, char *argv
[])
250 test_init(21, server_fn
, client_fn
);