253 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Author: Dmitry Safonov <dima@arista.com> */3/* This is over-simplified TCP_REPAIR for TCP_ESTABLISHED sockets4 * 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.h7 */8#include <inttypes.h>9#include "aolib.h"10 11const size_t nr_packets = 20;12const size_t msg_len = 100;13const size_t quota = nr_packets * msg_len;14#define fault(type) (inj == FAULT_ ## type)15 16static void try_server_run(const char *tst_name, unsigned int port,17 fault_t inj, test_cnt cnt_expected)18{19 const char *cnt_name = "TCPAOGood";20 struct tcp_ao_counters ao1, ao2;21 uint64_t before_cnt, after_cnt;22 int sk, lsk;23 time_t timeout;24 ssize_t bytes;25 26 if (fault(TIMEOUT))27 cnt_name = "TCPAOBad";28 lsk = test_listen_socket(this_ip_addr, port, 1);29 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() */33 34 if (test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0))35 test_error("test_wait_fd()");36 37 sk = accept(lsk, NULL, NULL);38 if (sk < 0)39 test_error("accept()");40 41 synchronize_threads(); /* 2: accepted => send data */42 close(lsk);43 44 bytes = test_server_run(sk, quota, TEST_TIMEOUT_SEC);45 if (bytes != quota) {46 test_fail("%s: server served: %zd", tst_name, bytes);47 goto out;48 }49 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()");53 54 timeout = fault(TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;55 bytes = test_server_run(sk, quota, timeout);56 if (fault(TIMEOUT)) {57 if (bytes > 0)58 test_fail("%s: server served: %zd", tst_name, bytes);59 else60 test_ok("%s: server couldn't serve", tst_name);61 } else {62 if (bytes != quota)63 test_fail("%s: server served: %zd", tst_name, bytes);64 else65 test_ok("%s: server alive", tst_name);66 }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);71 72 test_tcp_ao_counters_cmp(tst_name, &ao1, &ao2, cnt_expected);73 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);77 } else {78 test_ok("%s: counter %s increased %" PRIu64 " => %" PRIu64,79 tst_name, cnt_name, before_cnt, after_cnt);80 }81 82 /*83 * Before close() as that will send FIN and move the peer in TCP_CLOSE84 * and that will prevent reading AO counters from the peer's socket.85 */86 synchronize_threads(); /* 4: verified => closed */87out:88 close(sk);89}90 91static void *server_fn(void *arg)92{93 unsigned int port = test_server_port;94 95 try_server_run("TCP-AO migrate to another socket", port++,96 0, TEST_CNT_GOOD);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);105 106 synchronize_threads(); /* don't race to exit: client exits */107 return NULL;108}109 110static 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)113{114 int sk;115 116 sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);117 if (sk < 0)118 test_error("socket()");119 120 if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100))121 test_error("setsockopt(TCP_AO_ADD_KEY)");122 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()");126 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");130 131 test_enable_repair(sk);132 test_sock_checkpoint(sk, img, saddr);133 test_ao_checkpoint(sk, ao_img);134 test_kill_sk(sk);135}136 137static 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)141{142 const char *cnt_name = "TCPAOGood";143 struct tcp_ao_counters ao1, ao2;144 uint64_t before_cnt, after_cnt;145 time_t timeout;146 int sk;147 148 if (fault(TIMEOUT))149 cnt_name = "TCPAOBad";150 151 before_cnt = netstat_get_one(cnt_name, NULL);152 sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);153 if (sk < 0)154 test_error("socket()");155 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);161 162 if (test_get_tcp_ao_counters(sk, &ao1))163 test_error("test_get_tcp_ao_counters()");164 165 test_disable_repair(sk);166 test_sock_state_free(img);167 168 timeout = fault(TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;169 if (test_client_verify(sk, msg_len, nr_packets, timeout)) {170 if (fault(TIMEOUT))171 test_ok("%s: post-migrate connection is broken", tst_name);172 else173 test_fail("%s: post-migrate connection is working", tst_name);174 } else {175 if (fault(TIMEOUT))176 test_fail("%s: post-migrate connection still working", tst_name);177 else178 test_ok("%s: post-migrate connection is alive", tst_name);179 }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);184 185 test_tcp_ao_counters_cmp(tst_name, &ao1, &ao2, cnt_expected);186 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);190 } else {191 test_ok("%s: counter %s increased %" PRIu64 " => %" PRIu64,192 tst_name, cnt_name, before_cnt, after_cnt);193 }194 synchronize_threads(); /* 4: verified => closed */195 close(sk);196}197 198static void *client_fn(void *arg)199{200 unsigned int port = test_server_port;201 struct tcp_sock_state tcp_img;202 struct tcp_ao_repair ao_img;203 sockaddr_af saddr;204 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);208 209 test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);210 ao_img.snt_isn += 1;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);217 218 test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);219 ao_img.rcv_isn += 1;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);226 227 test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);228 ao_img.snd_sne += 1;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);235 236 test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);237 ao_img.rcv_sne += 1;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);244 245 return NULL;246}247 248int main(int argc, char *argv[])249{250 test_init(21, server_fn, client_fn);251 return 0;252}253