1// SPDX-License-Identifier: GPL-2.0 2/* This is over-simplified TCP_REPAIR for TCP_ESTABLISHED sockets 3 * It tests that TCP-AO enabled connection can be restored. 4 * For the proper socket repair see: 5 * https://github.com/checkpoint-restore/criu/blob/criu-dev/soccr/soccr.h 6 */ 7#include <fcntl.h> 8#include <linux/sockios.h> 9#include <sys/ioctl.h> 10#include "aolib.h" 11 12#ifndef TCPOPT_MAXSEG 13# define TCPOPT_MAXSEG 2 14#endif 15#ifndef TCPOPT_WINDOW 16# define TCPOPT_WINDOW 3 17#endif 18#ifndef TCPOPT_SACK_PERMITTED 19# define TCPOPT_SACK_PERMITTED 4 20#endif 21#ifndef TCPOPT_TIMESTAMP 22# define TCPOPT_TIMESTAMP 8 23#endif 24 25enum { 26 TCP_ESTABLISHED = 1, 27 TCP_SYN_SENT, 28 TCP_SYN_RECV, 29 TCP_FIN_WAIT1, 30 TCP_FIN_WAIT2, 31 TCP_TIME_WAIT, 32 TCP_CLOSE, 33 TCP_CLOSE_WAIT, 34 TCP_LAST_ACK, 35 TCP_LISTEN, 36 TCP_CLOSING, /* Now a valid state */ 37 TCP_NEW_SYN_RECV, 38 39 TCP_MAX_STATES /* Leave at the end! */ 40}; 41 42static void test_sock_checkpoint_queue(int sk, int queue, int qlen, 43 struct tcp_sock_queue *q) 44{ 45 socklen_t len; 46 int ret; 47 48 if (setsockopt(sk, SOL_TCP, TCP_REPAIR_QUEUE, &queue, sizeof(queue))) 49 test_error("setsockopt(TCP_REPAIR_QUEUE)"); 50 51 len = sizeof(q->seq); 52 ret = getsockopt(sk, SOL_TCP, TCP_QUEUE_SEQ, &q->seq, &len); 53 if (ret || len != sizeof(q->seq)) 54 test_error("getsockopt(TCP_QUEUE_SEQ): %d", (int)len); 55 56 if (!qlen) { 57 q->buf = NULL; 58 return; 59 } 60 61 q->buf = malloc(qlen); 62 if (q->buf == NULL) 63 test_error("malloc()"); 64 ret = recv(sk, q->buf, qlen, MSG_PEEK | MSG_DONTWAIT); 65 if (ret != qlen) 66 test_error("recv(%d): %d", qlen, ret); 67} 68 69void __test_sock_checkpoint(int sk, struct tcp_sock_state *state, 70 void *addr, size_t addr_size) 71{ 72 socklen_t len = sizeof(state->info); 73 int ret; 74 75 memset(state, 0, sizeof(*state)); 76 77 ret = getsockopt(sk, SOL_TCP, TCP_INFO, &state->info, &len); 78 if (ret || len != sizeof(state->info)) 79 test_error("getsockopt(TCP_INFO): %d", (int)len); 80 81 len = addr_size; 82 if (getsockname(sk, addr, &len) || len != addr_size) 83 test_error("getsockname(): %d", (int)len); 84 85 len = sizeof(state->trw); 86 ret = getsockopt(sk, SOL_TCP, TCP_REPAIR_WINDOW, &state->trw, &len); 87 if (ret || len != sizeof(state->trw)) 88 test_error("getsockopt(TCP_REPAIR_WINDOW): %d", (int)len); 89 90 if (ioctl(sk, SIOCOUTQ, &state->outq_len)) 91 test_error("ioctl(SIOCOUTQ)"); 92 93 if (ioctl(sk, SIOCOUTQNSD, &state->outq_nsd_len)) 94 test_error("ioctl(SIOCOUTQNSD)"); 95 test_sock_checkpoint_queue(sk, TCP_SEND_QUEUE, state->outq_len, &state->out); 96 97 if (ioctl(sk, SIOCINQ, &state->inq_len)) 98 test_error("ioctl(SIOCINQ)"); 99 test_sock_checkpoint_queue(sk, TCP_RECV_QUEUE, state->inq_len, &state->in); 100 101 if (state->info.tcpi_state == TCP_CLOSE) 102 state->outq_len = state->outq_nsd_len = 0; 103 104 len = sizeof(state->mss); 105 ret = getsockopt(sk, SOL_TCP, TCP_MAXSEG, &state->mss, &len); 106 if (ret || len != sizeof(state->mss)) 107 test_error("getsockopt(TCP_MAXSEG): %d", (int)len); 108 109 len = sizeof(state->timestamp); 110 ret = getsockopt(sk, SOL_TCP, TCP_TIMESTAMP, &state->timestamp, &len); 111 if (ret || len != sizeof(state->timestamp)) 112 test_error("getsockopt(TCP_TIMESTAMP): %d", (int)len); 113} 114 115void test_ao_checkpoint(int sk, struct tcp_ao_repair *state) 116{ 117 socklen_t len = sizeof(*state); 118 int ret; 119 120 memset(state, 0, sizeof(*state)); 121 122 ret = getsockopt(sk, SOL_TCP, TCP_AO_REPAIR, state, &len); 123 if (ret || len != sizeof(*state)) 124 test_error("getsockopt(TCP_AO_REPAIR): %d", (int)len); 125} 126 127static void test_sock_restore_seq(int sk, int queue, uint32_t seq) 128{ 129 if (setsockopt(sk, SOL_TCP, TCP_REPAIR_QUEUE, &queue, sizeof(queue))) 130 test_error("setsockopt(TCP_REPAIR_QUEUE)"); 131 132 if (setsockopt(sk, SOL_TCP, TCP_QUEUE_SEQ, &seq, sizeof(seq))) 133 test_error("setsockopt(TCP_QUEUE_SEQ)"); 134} 135 136static void test_sock_restore_queue(int sk, int queue, void *buf, int len) 137{ 138 int chunk = len; 139 size_t off = 0; 140 141 if (len == 0) 142 return; 143 144 if (setsockopt(sk, SOL_TCP, TCP_REPAIR_QUEUE, &queue, sizeof(queue))) 145 test_error("setsockopt(TCP_REPAIR_QUEUE)"); 146 147 do { 148 int ret; 149 150 ret = send(sk, buf + off, chunk, 0); 151 if (ret <= 0) { 152 if (chunk > 1024) { 153 chunk >>= 1; 154 continue; 155 } 156 test_error("send()"); 157 } 158 off += ret; 159 len -= ret; 160 } while (len > 0); 161} 162 163void __test_sock_restore(int sk, const char *device, 164 struct tcp_sock_state *state, 165 void *saddr, void *daddr, size_t addr_size) 166{ 167 struct tcp_repair_opt opts[4]; 168 unsigned int opt_nr = 0; 169 long flags; 170 171 if (bind(sk, saddr, addr_size)) 172 test_error("bind()"); 173 174 flags = fcntl(sk, F_GETFL); 175 if ((flags < 0) || (fcntl(sk, F_SETFL, flags | O_NONBLOCK) < 0)) 176 test_error("fcntl()"); 177 178 test_sock_restore_seq(sk, TCP_RECV_QUEUE, state->in.seq - state->inq_len); 179 test_sock_restore_seq(sk, TCP_SEND_QUEUE, state->out.seq - state->outq_len); 180 181 if (device != NULL && setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, 182 device, strlen(device) + 1)) 183 test_error("setsockopt(SO_BINDTODEVICE, %s)", device); 184 185 if (connect(sk, daddr, addr_size)) 186 test_error("connect()"); 187 188 if (state->info.tcpi_options & TCPI_OPT_SACK) { 189 opts[opt_nr].opt_code = TCPOPT_SACK_PERMITTED; 190 opts[opt_nr].opt_val = 0; 191 opt_nr++; 192 } 193 if (state->info.tcpi_options & TCPI_OPT_WSCALE) { 194 opts[opt_nr].opt_code = TCPOPT_WINDOW; 195 opts[opt_nr].opt_val = state->info.tcpi_snd_wscale + 196 (state->info.tcpi_rcv_wscale << 16); 197 opt_nr++; 198 } 199 if (state->info.tcpi_options & TCPI_OPT_TIMESTAMPS) { 200 opts[opt_nr].opt_code = TCPOPT_TIMESTAMP; 201 opts[opt_nr].opt_val = 0; 202 opt_nr++; 203 } 204 opts[opt_nr].opt_code = TCPOPT_MAXSEG; 205 opts[opt_nr].opt_val = state->mss; 206 opt_nr++; 207 208 if (setsockopt(sk, SOL_TCP, TCP_REPAIR_OPTIONS, opts, opt_nr * sizeof(opts[0]))) 209 test_error("setsockopt(TCP_REPAIR_OPTIONS)"); 210 211 if (state->info.tcpi_options & TCPI_OPT_TIMESTAMPS) { 212 if (setsockopt(sk, SOL_TCP, TCP_TIMESTAMP, 213 &state->timestamp, opt_nr * sizeof(opts[0]))) 214 test_error("setsockopt(TCP_TIMESTAMP)"); 215 } 216 test_sock_restore_queue(sk, TCP_RECV_QUEUE, state->in.buf, state->inq_len); 217 test_sock_restore_queue(sk, TCP_SEND_QUEUE, state->out.buf, state->outq_len); 218 if (setsockopt(sk, SOL_TCP, TCP_REPAIR_WINDOW, &state->trw, sizeof(state->trw))) 219 test_error("setsockopt(TCP_REPAIR_WINDOW)"); 220} 221 222void test_ao_restore(int sk, struct tcp_ao_repair *state) 223{ 224 if (setsockopt(sk, SOL_TCP, TCP_AO_REPAIR, state, sizeof(*state))) 225 test_error("setsockopt(TCP_AO_REPAIR)"); 226} 227 228void test_sock_state_free(struct tcp_sock_state *state) 229{ 230 free(state->out.buf); 231 free(state->in.buf); 232} 233 234void test_enable_repair(int sk) 235{ 236 int val = TCP_REPAIR_ON; 237 238 if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val))) 239 test_error("setsockopt(TCP_REPAIR)"); 240} 241 242void test_disable_repair(int sk) 243{ 244 int val = TCP_REPAIR_OFF_NO_WP; 245 246 if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val))) 247 test_error("setsockopt(TCP_REPAIR)"); 248} 249 250void test_kill_sk(int sk) 251{ 252 test_enable_repair(sk); 253 close(sk); 254} 255