restore.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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
  7. */
  8. #include <inttypes.h>
  9. #include "aolib.h"
  10. const size_t nr_packets = 20;
  11. const size_t msg_len = 100;
  12. const size_t quota = nr_packets * msg_len;
  13. #define fault(type) (inj == FAULT_ ## type)
  14. static void try_server_run(const char *tst_name, unsigned int port,
  15. fault_t inj, test_cnt cnt_expected)
  16. {
  17. test_cnt poll_cnt = (cnt_expected == TEST_CNT_GOOD) ? 0 : cnt_expected;
  18. const char *cnt_name = "TCPAOGood";
  19. struct tcp_counters cnt1, cnt2;
  20. uint64_t before_cnt, after_cnt;
  21. int sk, lsk, dummy;
  22. ssize_t bytes;
  23. if (fault(TIMEOUT))
  24. cnt_name = "TCPAOBad";
  25. lsk = test_listen_socket(this_ip_addr, port, 1);
  26. if (test_add_key(lsk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100))
  27. test_error("setsockopt(TCP_AO_ADD_KEY)");
  28. synchronize_threads(); /* 1: MKT added => connect() */
  29. if (test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0))
  30. test_error("test_wait_fd()");
  31. sk = accept(lsk, NULL, NULL);
  32. if (sk < 0)
  33. test_error("accept()");
  34. synchronize_threads(); /* 2: accepted => send data */
  35. close(lsk);
  36. bytes = test_server_run(sk, quota, TEST_TIMEOUT_SEC);
  37. if (bytes != quota) {
  38. test_fail("%s: server served: %zd", tst_name, bytes);
  39. goto out;
  40. }
  41. before_cnt = netstat_get_one(cnt_name, NULL);
  42. if (test_get_tcp_counters(sk, &cnt1))
  43. test_error("test_get_tcp_counters()");
  44. bytes = test_skpair_server(sk, quota, poll_cnt, &dummy);
  45. if (fault(TIMEOUT)) {
  46. if (bytes > 0)
  47. test_fail("%s: server served: %zd", tst_name, bytes);
  48. else
  49. test_ok("%s: server couldn't serve", tst_name);
  50. } else {
  51. if (bytes != quota)
  52. test_fail("%s: server served: %zd", tst_name, bytes);
  53. else
  54. test_ok("%s: server alive", tst_name);
  55. }
  56. synchronize_threads(); /* 3: counters checks */
  57. if (test_get_tcp_counters(sk, &cnt2))
  58. test_error("test_get_tcp_counters()");
  59. after_cnt = netstat_get_one(cnt_name, NULL);
  60. test_assert_counters(tst_name, &cnt1, &cnt2, cnt_expected);
  61. if (after_cnt <= before_cnt) {
  62. test_fail("%s(server): %s counter did not increase: %" PRIu64 " <= %" PRIu64,
  63. tst_name, cnt_name, after_cnt, before_cnt);
  64. } else {
  65. test_ok("%s(server): counter %s increased %" PRIu64 " => %" PRIu64,
  66. tst_name, cnt_name, before_cnt, after_cnt);
  67. }
  68. /*
  69. * Before close() as that will send FIN and move the peer in TCP_CLOSE
  70. * and that will prevent reading AO counters from the peer's socket.
  71. */
  72. synchronize_threads(); /* 4: verified => closed */
  73. out:
  74. close(sk);
  75. }
  76. static void *server_fn(void *arg)
  77. {
  78. unsigned int port = test_server_port;
  79. try_server_run("TCP-AO migrate to another socket (server)", port++,
  80. 0, TEST_CNT_GOOD);
  81. try_server_run("TCP-AO with wrong send ISN (server)", port++,
  82. FAULT_TIMEOUT, TEST_CNT_BAD);
  83. try_server_run("TCP-AO with wrong receive ISN (server)", port++,
  84. FAULT_TIMEOUT, TEST_CNT_BAD);
  85. try_server_run("TCP-AO with wrong send SEQ ext number (server)", port++,
  86. FAULT_TIMEOUT, TEST_CNT_BAD);
  87. try_server_run("TCP-AO with wrong receive SEQ ext number (server)",
  88. port++, FAULT_TIMEOUT, TEST_CNT_NS_BAD | TEST_CNT_GOOD);
  89. synchronize_threads(); /* don't race to exit: client exits */
  90. return NULL;
  91. }
  92. static void test_get_sk_checkpoint(unsigned int server_port, sockaddr_af *saddr,
  93. struct tcp_sock_state *img,
  94. struct tcp_ao_repair *ao_img)
  95. {
  96. int sk;
  97. sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
  98. if (sk < 0)
  99. test_error("socket()");
  100. if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100))
  101. test_error("setsockopt(TCP_AO_ADD_KEY)");
  102. synchronize_threads(); /* 1: MKT added => connect() */
  103. if (test_connect_socket(sk, this_ip_dest, server_port) <= 0)
  104. test_error("failed to connect()");
  105. synchronize_threads(); /* 2: accepted => send data */
  106. if (test_client_verify(sk, msg_len, nr_packets))
  107. test_fail("pre-migrate verify failed");
  108. test_enable_repair(sk);
  109. test_sock_checkpoint(sk, img, saddr);
  110. test_ao_checkpoint(sk, ao_img);
  111. test_kill_sk(sk);
  112. }
  113. static void test_sk_restore(const char *tst_name, unsigned int server_port,
  114. sockaddr_af *saddr, struct tcp_sock_state *img,
  115. struct tcp_ao_repair *ao_img,
  116. fault_t inj, test_cnt cnt_expected)
  117. {
  118. test_cnt poll_cnt = (cnt_expected == TEST_CNT_GOOD) ? 0 : cnt_expected;
  119. const char *cnt_name = "TCPAOGood";
  120. struct tcp_counters cnt1, cnt2;
  121. uint64_t before_cnt, after_cnt;
  122. int sk, dummy;
  123. if (fault(TIMEOUT))
  124. cnt_name = "TCPAOBad";
  125. before_cnt = netstat_get_one(cnt_name, NULL);
  126. sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
  127. if (sk < 0)
  128. test_error("socket()");
  129. test_enable_repair(sk);
  130. test_sock_restore(sk, img, saddr, this_ip_dest, server_port);
  131. if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0, this_ip_dest, -1, 100, 100))
  132. test_error("setsockopt(TCP_AO_ADD_KEY)");
  133. test_ao_restore(sk, ao_img);
  134. if (test_get_tcp_counters(sk, &cnt1))
  135. test_error("test_get_tcp_counters()");
  136. test_disable_repair(sk);
  137. test_sock_state_free(img);
  138. if (test_skpair_client(sk, msg_len, nr_packets, poll_cnt, &dummy)) {
  139. if (fault(TIMEOUT))
  140. test_ok("%s: post-migrate connection is broken", tst_name);
  141. else
  142. test_fail("%s: post-migrate connection is working", tst_name);
  143. } else {
  144. if (fault(TIMEOUT))
  145. test_fail("%s: post-migrate connection is working", tst_name);
  146. else
  147. test_ok("%s: post-migrate connection is alive", tst_name);
  148. }
  149. synchronize_threads(); /* 3: counters checks */
  150. if (test_get_tcp_counters(sk, &cnt2))
  151. test_error("test_get_tcp_counters()");
  152. after_cnt = netstat_get_one(cnt_name, NULL);
  153. test_assert_counters(tst_name, &cnt1, &cnt2, cnt_expected);
  154. if (after_cnt <= before_cnt) {
  155. test_fail("%s: %s counter did not increase: %" PRIu64 " <= %" PRIu64,
  156. tst_name, cnt_name, after_cnt, before_cnt);
  157. } else {
  158. test_ok("%s: counter %s increased %" PRIu64 " => %" PRIu64,
  159. tst_name, cnt_name, before_cnt, after_cnt);
  160. }
  161. synchronize_threads(); /* 4: verified => closed */
  162. close(sk);
  163. }
  164. static void *client_fn(void *arg)
  165. {
  166. unsigned int port = test_server_port;
  167. struct tcp_sock_state tcp_img;
  168. struct tcp_ao_repair ao_img;
  169. sockaddr_af saddr;
  170. test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);
  171. test_sk_restore("TCP-AO migrate to another socket (client)", port++,
  172. &saddr, &tcp_img, &ao_img, 0, TEST_CNT_GOOD);
  173. test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);
  174. ao_img.snt_isn += 1;
  175. trace_ao_event_expect(TCP_AO_MISMATCH, this_ip_addr, this_ip_dest,
  176. -1, port, 0, -1, -1, -1, -1, -1, 100, 100, -1);
  177. trace_ao_event_expect(TCP_AO_MISMATCH, this_ip_dest, this_ip_addr,
  178. port, -1, 0, -1, -1, -1, -1, -1, 100, 100, -1);
  179. test_sk_restore("TCP-AO with wrong send ISN (client)", port++,
  180. &saddr, &tcp_img, &ao_img, FAULT_TIMEOUT, TEST_CNT_BAD);
  181. test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);
  182. ao_img.rcv_isn += 1;
  183. trace_ao_event_expect(TCP_AO_MISMATCH, this_ip_addr, this_ip_dest,
  184. -1, port, 0, -1, -1, -1, -1, -1, 100, 100, -1);
  185. trace_ao_event_expect(TCP_AO_MISMATCH, this_ip_dest, this_ip_addr,
  186. port, -1, 0, -1, -1, -1, -1, -1, 100, 100, -1);
  187. test_sk_restore("TCP-AO with wrong receive ISN (client)", port++,
  188. &saddr, &tcp_img, &ao_img, FAULT_TIMEOUT, TEST_CNT_BAD);
  189. test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);
  190. ao_img.snd_sne += 1;
  191. trace_ao_event_expect(TCP_AO_MISMATCH, this_ip_addr, this_ip_dest,
  192. -1, port, 0, -1, -1, -1, -1, -1, 100, 100, -1);
  193. /* not expecting server => client mismatches as only snd sne is broken */
  194. test_sk_restore("TCP-AO with wrong send SEQ ext number (client)",
  195. port++, &saddr, &tcp_img, &ao_img, FAULT_TIMEOUT,
  196. TEST_CNT_NS_BAD | TEST_CNT_GOOD);
  197. test_get_sk_checkpoint(port, &saddr, &tcp_img, &ao_img);
  198. ao_img.rcv_sne += 1;
  199. /* not expecting client => server mismatches as only rcv sne is broken */
  200. trace_ao_event_expect(TCP_AO_MISMATCH, this_ip_dest, this_ip_addr,
  201. port, -1, 0, -1, -1, -1, -1, -1, 100, 100, -1);
  202. test_sk_restore("TCP-AO with wrong receive SEQ ext number (client)",
  203. port++, &saddr, &tcp_img, &ao_img, FAULT_TIMEOUT,
  204. TEST_CNT_NS_GOOD | TEST_CNT_BAD);
  205. return NULL;
  206. }
  207. int main(int argc, char *argv[])
  208. {
  209. test_init(21, server_fn, client_fn);
  210. return 0;
  211. }