zdtm: add test for SO_SNDTIMEO and SO_RCVTIMEO

CRIU dumps and restores the SO_SNDTIMEO and SO_RCVTIMEO socket
send/receive timeout options in dump_socket_opts()/restore_socket_opts()
(criu/sockets.c), but the ZDTM suite has no dedicated coverage for them:
SO_SNDTIMEO is not exercised by any test, and SO_RCVTIMEO is only used
incidentally as a receive timeout in socket_icmp, never asserted across
checkpoint/restore.

Add socket-timeo, which sets distinct send and receive timeouts on a
socket, performs checkpoint/restore, and verifies both values survive.
Whole-second timeouts are used because the kernel stores these options
with jiffy granularity: setsockopt() may round a sub-second value to the
nearest tick depending on HZ, so a literal sub-second expectation could
fail to match the stored value regardless of checkpoint/restore. The
send and receive values differ so a mix-up between the two options is
caught.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Rocker Zhang <zhang.rocker.liyuan@gmail.com>
This commit is contained in:
Rocker Zhang 2026-06-05 00:51:31 +08:00 committed by Radostin Stoyanov
parent cba1c94b60
commit fc29bfef9d
2 changed files with 76 additions and 0 deletions

View file

@ -137,6 +137,7 @@ TST_NOFILE := \
socket-tcp-skip-in-flight \
socket-tcp-keepalive \
socket-linger \
socket-timeo \
sock_opts00 \
sock_opts01 \
sock_opts02 \

View file

@ -0,0 +1,75 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include "zdtmtst.h"
const char *test_doc = "Check SO_SNDTIMEO and SO_RCVTIMEO socket options";
const char *test_author = "Rocker Zhang <zhang.rocker.liyuan@gmail.com>";
/*
* Whole-second timeouts are used on purpose. The kernel stores
* SO_SNDTIMEO/SO_RCVTIMEO with jiffy granularity, so setsockopt() may
* round a sub-second tv_usec to the nearest tick depending on HZ. Such a
* value would still survive checkpoint/restore (CRIU saves and restores
* whatever the kernel reports), but a literal sub-second expectation in
* this test could fail to match the rounded value. Whole seconds avoid
* that ambiguity. Distinct send/receive values catch a possible mix-up
* between the two options.
*/
static int check_timeo(int sk, int opt, const char *name, const struct timeval *want)
{
struct timeval got = { 0, 0 };
socklen_t optlen = sizeof(got);
if (getsockopt(sk, SOL_SOCKET, opt, &got, &optlen) < 0) {
pr_perror("getsockopt %s", name);
return -1;
}
if (got.tv_sec != want->tv_sec || got.tv_usec != want->tv_usec) {
fail("%s has incorrect value (%ld.%06ld != %ld.%06ld)", name, (long)got.tv_sec, (long)got.tv_usec,
(long)want->tv_sec, (long)want->tv_usec);
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
int sk;
struct timeval sndtimeo = { 7, 0 }, rcvtimeo = { 11, 0 };
test_init(argc, argv);
sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sk < 0) {
pr_perror("Can't create socket");
return 1;
}
if (setsockopt(sk, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(sndtimeo)) < 0) {
pr_perror("setsockopt SO_SNDTIMEO");
return 1;
}
if (setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(rcvtimeo)) < 0) {
pr_perror("setsockopt SO_RCVTIMEO");
return 1;
}
test_daemon();
test_waitsig();
if (check_timeo(sk, SO_SNDTIMEO, "SO_SNDTIMEO", &sndtimeo))
return 1;
if (check_timeo(sk, SO_RCVTIMEO, "SO_RCVTIMEO", &rcvtimeo))
return 1;
pass();
return 0;
}