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
8 #include <linux/sockios.h>
13 # define TCPOPT_MAXSEG 2
16 # define TCPOPT_WINDOW 3
18 #ifndef TCPOPT_SACK_PERMITTED
19 # define TCPOPT_SACK_PERMITTED 4
21 #ifndef TCPOPT_TIMESTAMP
22 # define TCPOPT_TIMESTAMP 8
36 TCP_CLOSING
, /* Now a valid state */
39 TCP_MAX_STATES
/* Leave at the end! */
42 static void test_sock_checkpoint_queue(int sk
, int queue
, int qlen
,
43 struct tcp_sock_queue
*q
)
48 if (setsockopt(sk
, SOL_TCP
, TCP_REPAIR_QUEUE
, &queue
, sizeof(queue
)))
49 test_error("setsockopt(TCP_REPAIR_QUEUE)");
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
);
61 q
->buf
= malloc(qlen
);
63 test_error("malloc()");
64 ret
= recv(sk
, q
->buf
, qlen
, MSG_PEEK
| MSG_DONTWAIT
);
66 test_error("recv(%d): %d", qlen
, ret
);
69 void __test_sock_checkpoint(int sk
, struct tcp_sock_state
*state
,
70 void *addr
, size_t addr_size
)
72 socklen_t len
= sizeof(state
->info
);
75 memset(state
, 0, sizeof(*state
));
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
);
82 if (getsockname(sk
, addr
, &len
) || len
!= addr_size
)
83 test_error("getsockname(): %d", (int)len
);
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
);
90 if (ioctl(sk
, SIOCOUTQ
, &state
->outq_len
))
91 test_error("ioctl(SIOCOUTQ)");
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
);
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
);
101 if (state
->info
.tcpi_state
== TCP_CLOSE
)
102 state
->outq_len
= state
->outq_nsd_len
= 0;
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
);
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
);
115 void test_ao_checkpoint(int sk
, struct tcp_ao_repair
*state
)
117 socklen_t len
= sizeof(*state
);
120 memset(state
, 0, sizeof(*state
));
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
);
127 static void test_sock_restore_seq(int sk
, int queue
, uint32_t seq
)
129 if (setsockopt(sk
, SOL_TCP
, TCP_REPAIR_QUEUE
, &queue
, sizeof(queue
)))
130 test_error("setsockopt(TCP_REPAIR_QUEUE)");
132 if (setsockopt(sk
, SOL_TCP
, TCP_QUEUE_SEQ
, &seq
, sizeof(seq
)))
133 test_error("setsockopt(TCP_QUEUE_SEQ)");
136 static void test_sock_restore_queue(int sk
, int queue
, void *buf
, int len
)
144 if (setsockopt(sk
, SOL_TCP
, TCP_REPAIR_QUEUE
, &queue
, sizeof(queue
)))
145 test_error("setsockopt(TCP_REPAIR_QUEUE)");
150 ret
= send(sk
, buf
+ off
, chunk
, 0);
156 test_error("send()");
163 void __test_sock_restore(int sk
, const char *device
,
164 struct tcp_sock_state
*state
,
165 void *saddr
, void *daddr
, size_t addr_size
)
167 struct tcp_repair_opt opts
[4];
168 unsigned int opt_nr
= 0;
171 if (bind(sk
, saddr
, addr_size
))
172 test_error("bind()");
174 flags
= fcntl(sk
, F_GETFL
);
175 if ((flags
< 0) || (fcntl(sk
, F_SETFL
, flags
| O_NONBLOCK
) < 0))
176 test_error("fcntl()");
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
);
181 if (device
!= NULL
&& setsockopt(sk
, SOL_SOCKET
, SO_BINDTODEVICE
,
182 device
, strlen(device
) + 1))
183 test_error("setsockopt(SO_BINDTODEVICE, %s)", device
);
185 if (connect(sk
, daddr
, addr_size
))
186 test_error("connect()");
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;
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);
199 if (state
->info
.tcpi_options
& TCPI_OPT_TIMESTAMPS
) {
200 opts
[opt_nr
].opt_code
= TCPOPT_TIMESTAMP
;
201 opts
[opt_nr
].opt_val
= 0;
204 opts
[opt_nr
].opt_code
= TCPOPT_MAXSEG
;
205 opts
[opt_nr
].opt_val
= state
->mss
;
208 if (setsockopt(sk
, SOL_TCP
, TCP_REPAIR_OPTIONS
, opts
, opt_nr
* sizeof(opts
[0])))
209 test_error("setsockopt(TCP_REPAIR_OPTIONS)");
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)");
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)");
222 void test_ao_restore(int sk
, struct tcp_ao_repair
*state
)
224 if (setsockopt(sk
, SOL_TCP
, TCP_AO_REPAIR
, state
, sizeof(*state
)))
225 test_error("setsockopt(TCP_AO_REPAIR)");
228 void test_sock_state_free(struct tcp_sock_state
*state
)
230 free(state
->out
.buf
);
234 void test_enable_repair(int sk
)
236 int val
= TCP_REPAIR_ON
;
238 if (setsockopt(sk
, SOL_TCP
, TCP_REPAIR
, &val
, sizeof(val
)))
239 test_error("setsockopt(TCP_REPAIR)");
242 void test_disable_repair(int sk
)
244 int val
= TCP_REPAIR_OFF_NO_WP
;
246 if (setsockopt(sk
, SOL_TCP
, TCP_REPAIR
, &val
, sizeof(val
)))
247 test_error("setsockopt(TCP_REPAIR)");
250 void test_kill_sk(int sk
)
252 test_enable_repair(sk
);