[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 6/8] linux-user: setsockopt() SO_TIMESTAMPNS and SO_TIMESTAMPI
From: |
Shu-Chun Weng |
Subject: |
[PATCH v2 6/8] linux-user: setsockopt() SO_TIMESTAMPNS and SO_TIMESTAMPING |
Date: |
Tue, 11 Aug 2020 00:09:46 -0700 |
This change supports SO_TIMESTAMPNS_OLD/NEW and SO_TIMESTAMPING_OLD/NEW
for setsocketopt() with SOL_SOCKET. Based on the SO_TIMESTAMP_OLD/NEW
framework. The three pairs share the same flag `SOCK_TSTAMP_NEW` in
linux kernel for deciding if the old or the new format is used.
Signed-off-by: Shu-Chun Weng <scw@google.com>
---
v1 -> v2:
Only keep track of old/new format in a global state.
Fix style problems.
linux-user/alpha/sockbits.h | 13 +-
linux-user/generic/sockbits.h | 8 +
linux-user/hppa/sockbits.h | 12 +-
linux-user/mips/sockbits.h | 8 +
linux-user/sparc/sockbits.h | 13 +-
linux-user/strace.c | 12 +
linux-user/syscall.c | 149 ++++++++-
tests/tcg/multiarch/socket_timestamp.c | 442 +++++++++++++++++++------
8 files changed, 540 insertions(+), 117 deletions(-)
diff --git a/linux-user/alpha/sockbits.h b/linux-user/alpha/sockbits.h
index 40f0644df0..c2c88f432b 100644
--- a/linux-user/alpha/sockbits.h
+++ b/linux-user/alpha/sockbits.h
@@ -51,8 +51,6 @@
#define TARGET_SO_PEERSEC 30
#define TARGET_SO_PASSSEC 34
-#define TARGET_SO_TIMESTAMPNS 35
-#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS
/* Security levels - as per NRL IPv6 - don't actually do anything */
#define TARGET_SO_SECURITY_AUTHENTICATION 19
@@ -61,9 +59,6 @@
#define TARGET_SO_MARK 36
-#define TARGET_SO_TIMESTAMPING 37
-#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING
-
#define TARGET_SO_RXQ_OVFL 40
#define TARGET_SO_WIFI_STATUS 41
@@ -75,9 +70,17 @@
#define TARGET_SO_TIMESTAMP_OLD 29
#define TARGET_SCM_TIMESTAMP_OLD TARGET_SO_TIMESTAMP_OLD
+#define TARGET_SO_TIMESTAMPNS_OLD 35
+#define TARGET_SCM_TIMESTAMPNS_OLD TARGET_SO_TIMESTAMPNS_OLD
+#define TARGET_SO_TIMESTAMPING_OLD 37
+#define TARGET_SCM_TIMESTAMPING_OLD TARGET_SO_TIMESTAMPING_OLD
#define TARGET_SO_TIMESTAMP_NEW 63
#define TARGET_SCM_TIMESTAMP_NEW TARGET_SO_TIMESTAMP_NEW
+#define TARGET_SO_TIMESTAMPNS_NEW 64
+#define TARGET_SCM_TIMESTAMPNS_NEW TARGET_SO_TIMESTAMPNS_NEW
+#define TARGET_SO_TIMESTAMPING_NEW 65
+#define TARGET_SCM_TIMESTAMPING_NEW TARGET_SO_TIMESTAMPING_NEW
/* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we
* have to define SOCK_NONBLOCK to a different value here.
diff --git a/linux-user/generic/sockbits.h b/linux-user/generic/sockbits.h
index 532cf2d3dc..a0496d8751 100644
--- a/linux-user/generic/sockbits.h
+++ b/linux-user/generic/sockbits.h
@@ -56,8 +56,16 @@
#define TARGET_SO_TIMESTAMP_OLD 29
#define TARGET_SCM_TIMESTAMP_OLD TARGET_SO_TIMESTAMP_OLD
+#define TARGET_SO_TIMESTAMPNS_OLD 35
+#define TARGET_SCM_TIMESTAMPNS_OLD TARGET_SO_TIMESTAMPNS_OLD
+#define TARGET_SO_TIMESTAMPING_OLD 37
+#define TARGET_SCM_TIMESTAMPING_OLD TARGET_SO_TIMESTAMPING_OLD
#define TARGET_SO_TIMESTAMP_NEW 63
#define TARGET_SCM_TIMESTAMP_NEW TARGET_SO_TIMESTAMP_NEW
+#define TARGET_SO_TIMESTAMPNS_NEW 64
+#define TARGET_SCM_TIMESTAMPNS_NEW TARGET_SO_TIMESTAMPNS_NEW
+#define TARGET_SO_TIMESTAMPING_NEW 65
+#define TARGET_SCM_TIMESTAMPING_NEW TARGET_SO_TIMESTAMPING_NEW
#endif
diff --git a/linux-user/hppa/sockbits.h b/linux-user/hppa/sockbits.h
index 284a47e74e..d7e9aa340d 100644
--- a/linux-user/hppa/sockbits.h
+++ b/linux-user/hppa/sockbits.h
@@ -29,8 +29,6 @@
#define TARGET_SO_BSDCOMPAT 0x400e
#define TARGET_SO_PASSCRED 0x4010
#define TARGET_SO_PEERCRED 0x4011
-#define TARGET_SO_TIMESTAMPNS 0x4013
-#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS
#define TARGET_SO_SECURITY_AUTHENTICATION 0x4016
#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 0x4017
@@ -44,8 +42,6 @@
#define TARGET_SO_PEERSEC 0x401d
#define TARGET_SO_PASSSEC 0x401e
#define TARGET_SO_MARK 0x401f
-#define TARGET_SO_TIMESTAMPING 0x4020
-#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING
#define TARGET_SO_RXQ_OVFL 0x4021
#define TARGET_SO_WIFI_STATUS 0x4022
#define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS
@@ -67,9 +63,17 @@
#define TARGET_SO_TIMESTAMP_OLD 0x4012
#define TARGET_SCM_TIMESTAMP_OLD TARGET_SO_TIMESTAMP_OLD
+#define TARGET_SO_TIMESTAMPNS_OLD 0x4013
+#define TARGET_SCM_TIMESTAMPNS_OLD TARGET_SO_TIMESTAMPNS_OLD
+#define TARGET_SO_TIMESTAMPING_OLD 0x4020
+#define TARGET_SCM_TIMESTAMPING_OLD TARGET_SO_TIMESTAMPING_OLD
#define TARGET_SO_TIMESTAMP_NEW 0x4038
#define TARGET_SCM_TIMESTAMP_NEW TARGET_SO_TIMESTAMP_NEW
+#define TARGET_SO_TIMESTAMPNS_NEW 0x4039
+#define TARGET_SCM_TIMESTAMPNS_NEW TARGET_SO_TIMESTAMPNS_NEW
+#define TARGET_SO_TIMESTAMPING_NEW 0x403A
+#define TARGET_SCM_TIMESTAMPING_NEW TARGET_SO_TIMESTAMPING_NEW
/* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we
* have to define SOCK_NONBLOCK to a different value here.
diff --git a/linux-user/mips/sockbits.h b/linux-user/mips/sockbits.h
index b4c39d9588..49524e23ac 100644
--- a/linux-user/mips/sockbits.h
+++ b/linux-user/mips/sockbits.h
@@ -69,9 +69,17 @@
#define TARGET_SO_TIMESTAMP_OLD 29
#define TARGET_SCM_TIMESTAMP_OLD TARGET_SO_TIMESTAMP_OLD
+#define TARGET_SO_TIMESTAMPNS_OLD 35
+#define TARGET_SCM_TIMESTAMPNS_OLD TARGET_SO_TIMESTAMPNS_OLD
+#define TARGET_SO_TIMESTAMPING_OLD 37
+#define TARGET_SCM_TIMESTAMPING_OLD TARGET_SO_TIMESTAMPING_OLD
#define TARGET_SO_TIMESTAMP_NEW 63
#define TARGET_SCM_TIMESTAMP_NEW TARGET_SO_TIMESTAMP_NEW
+#define TARGET_SO_TIMESTAMPNS_NEW 64
+#define TARGET_SCM_TIMESTAMPNS_NEW TARGET_SO_TIMESTAMPNS_NEW
+#define TARGET_SO_TIMESTAMPING_NEW 65
+#define TARGET_SCM_TIMESTAMPING_NEW TARGET_SO_TIMESTAMPING_NEW
/** sock_type - Socket types
*
diff --git a/linux-user/sparc/sockbits.h b/linux-user/sparc/sockbits.h
index 07440efd14..c5fade3ad1 100644
--- a/linux-user/sparc/sockbits.h
+++ b/linux-user/sparc/sockbits.h
@@ -51,14 +51,9 @@
#define TARGET_SO_PEERSEC 0x001e
#define TARGET_SO_PASSSEC 0x001f
-#define TARGET_SO_TIMESTAMPNS 0x0021
-#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS
#define TARGET_SO_MARK 0x0022
-#define TARGET_SO_TIMESTAMPING 0x0023
-#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING
-
#define TARGET_SO_RXQ_OVFL 0x0024
#define TARGET_SO_WIFI_STATUS 0x0025
@@ -104,9 +99,17 @@
#define TARGET_SO_TIMESTAMP_OLD 0x001d
#define TARGET_SCM_TIMESTAMP_OLD TARGET_SO_TIMESTAMP_OLD
+#define TARGET_SO_TIMESTAMPNS_OLD 0x0021
+#define TARGET_SCM_TIMESTAMPNS_OLD TARGET_SO_TIMESTAMPNS_OLD
+#define TARGET_SO_TIMESTAMPING_OLD 0x0023
+#define TARGET_SCM_TIMESTAMPING_OLD TARGET_SO_TIMESTAMPING_OLD
#define TARGET_SO_TIMESTAMP_NEW 0x0046
#define TARGET_SCM_TIMESTAMP_NEW TARGET_SO_TIMESTAMP_NEW
+#define TARGET_SO_TIMESTAMPNS_NEW 0x0042
+#define TARGET_SCM_TIMESTAMPNS_NEW TARGET_SO_TIMESTAMPNS_NEW
+#define TARGET_SO_TIMESTAMPING_NEW 0x0043
+#define TARGET_SCM_TIMESTAMPING_NEW TARGET_SO_TIMESTAMPING_NEW
/* Security levels - as per NRL IPv6 - don't actually do anything */
#define TARGET_SO_SECURITY_AUTHENTICATION 0x5001
diff --git a/linux-user/strace.c b/linux-user/strace.c
index a11a5e9e86..7aabb3c972 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -2260,9 +2260,21 @@ print_optint:
case TARGET_SO_TIMESTAMP_OLD:
qemu_log("SO_TIMESTAMP_OLD,");
goto print_optint;
+ case TARGET_SO_TIMESTAMPNS_OLD:
+ qemu_log("SO_TIMESTAMPNS_OLD,");
+ goto print_optint;
+ case TARGET_SO_TIMESTAMPING_OLD:
+ qemu_log("SO_TIMESTAMPING_OLD,");
+ goto print_optint;
case TARGET_SO_TIMESTAMP_NEW:
qemu_log("SO_TIMESTAMP_NEW,");
goto print_optint;
+ case TARGET_SO_TIMESTAMPNS_NEW:
+ qemu_log("SO_TIMESTAMPNS_NEW,");
+ goto print_optint;
+ case TARGET_SO_TIMESTAMPING_NEW:
+ qemu_log("SO_TIMESTAMPING_NEW,");
+ goto print_optint;
case TARGET_SO_RCVLOWAT:
qemu_log("SO_RCVLOWAT,");
goto print_optint;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index e6b1a18cc0..bfc4219104 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -1771,6 +1771,34 @@ static inline abi_long host_to_target_cmsg(struct
target_msghdr *target_msgh,
break;
}
break;
+ case SCM_TIMESTAMPNS:
+ switch (target_expected_timestamp_version) {
+ case TARGET_TIMESTAMP_OLD:
+ tgt_len = sizeof(struct target_timespec);
+ target_cmsg->cmsg_type =
+ tswap32(TARGET_SCM_TIMESTAMPNS_OLD);
+ break;
+ case TARGET_TIMESTAMP_NEW:
+ tgt_len = sizeof(struct target__kernel_timespec);
+ target_cmsg->cmsg_type =
+ tswap32(TARGET_SCM_TIMESTAMPNS_NEW);
+ break;
+ }
+ break;
+ case SCM_TIMESTAMPING:
+ switch (target_expected_timestamp_version) {
+ case TARGET_TIMESTAMP_OLD:
+ tgt_len = sizeof(struct target_timespec[3]);
+ target_cmsg->cmsg_type =
+ tswap32(TARGET_SCM_TIMESTAMPING_OLD);
+ break;
+ case TARGET_TIMESTAMP_NEW:
+ tgt_len = sizeof(struct target__kernel_timespec[3]);
+ target_cmsg->cmsg_type =
+ tswap32(TARGET_SCM_TIMESTAMPING_NEW);
+ break;
+ }
+ break;
default:
break;
}
@@ -1838,6 +1866,81 @@ static inline abi_long host_to_target_cmsg(struct
target_msghdr *target_msgh,
}
break;
}
+ case SCM_TIMESTAMPNS:
+ {
+ struct timespec *ts = (struct timespec *)data;
+ if (len != sizeof(struct timespec)) {
+ goto unimplemented;
+ }
+
+ switch (target_expected_timestamp_version) {
+ case TARGET_TIMESTAMP_OLD:
+ {
+ struct target_timespec *target_ts =
+ (struct target_timespec *)target_data;
+ if (tgt_len != sizeof(struct target_timespec)) {
+ goto unimplemented;
+ }
+
+ __put_user(ts->tv_sec, &target_ts->tv_sec);
+ __put_user(ts->tv_nsec, &target_ts->tv_nsec);
+ break;
+ }
+ case TARGET_TIMESTAMP_NEW:
+ {
+ struct target__kernel_timespec *target_ts =
+ (struct target__kernel_timespec *)target_data;
+ if (tgt_len != sizeof(struct target__kernel_timespec)) {
+ goto unimplemented;
+ }
+
+ __put_user(ts->tv_sec, &target_ts->tv_sec);
+ __put_user(ts->tv_nsec, &target_ts->tv_nsec);
+ break;
+ }
+ }
+ break;
+ }
+ case SCM_TIMESTAMPING:
+ {
+ int i;
+ struct timespec *ts = (struct timespec *)data;
+ if (len != sizeof(struct timespec[3])) {
+ goto unimplemented;
+ }
+
+ switch (target_expected_timestamp_version) {
+ case TARGET_TIMESTAMP_OLD:
+ {
+ struct target_timespec *target_ts =
+ (struct target_timespec *)target_data;
+ if (tgt_len != sizeof(struct target_timespec[3])) {
+ goto unimplemented;
+ }
+
+ for (i = 0; i < 3; ++i) {
+ __put_user(ts[i].tv_sec, &target_ts[i].tv_sec);
+ __put_user(ts[i].tv_nsec, &target_ts[i].tv_nsec);
+ }
+ break;
+ }
+ case TARGET_TIMESTAMP_NEW:
+ {
+ struct target__kernel_timespec *target_ts =
+ (struct target__kernel_timespec *)target_data;
+ if (tgt_len != sizeof(struct target__kernel_timespec[3])) {
+ goto unimplemented;
+ }
+
+ for (i = 0; i < 3; ++i) {
+ __put_user(ts[i].tv_sec, &target_ts[i].tv_sec);
+ __put_user(ts[i].tv_nsec, &target_ts[i].tv_nsec);
+ }
+ break;
+ }
+ }
+ break;
+ }
case SCM_CREDENTIALS:
{
struct ucred *cred = (struct ucred *)data;
@@ -2381,6 +2484,22 @@ set_timeout:
target_timestamp_version = TARGET_TIMESTAMP_NEW;
optname = SO_TIMESTAMP;
break;
+ case TARGET_SO_TIMESTAMPNS_OLD:
+ target_timestamp_version = TARGET_TIMESTAMP_OLD;
+ optname = SO_TIMESTAMPNS;
+ break;
+ case TARGET_SO_TIMESTAMPNS_NEW:
+ target_timestamp_version = TARGET_TIMESTAMP_NEW;
+ optname = SO_TIMESTAMPNS;
+ break;
+ case TARGET_SO_TIMESTAMPING_OLD:
+ target_timestamp_version = TARGET_TIMESTAMP_OLD;
+ optname = SO_TIMESTAMPING;
+ break;
+ case TARGET_SO_TIMESTAMPING_NEW:
+ target_timestamp_version = TARGET_TIMESTAMP_NEW;
+ optname = SO_TIMESTAMPING;
+ break;
case TARGET_SO_RCVLOWAT:
optname = SO_RCVLOWAT;
break;
@@ -2393,7 +2512,9 @@ set_timeout:
if (get_user_u32(val, optval_addr))
return -TARGET_EFAULT;
ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val,
sizeof(val)));
- if (!is_error(ret) && optname == SO_TIMESTAMP) {
+ if (!is_error(ret) &&
+ (optname == SO_TIMESTAMP || optname == SO_TIMESTAMPNS ||
+ optname == SO_TIMESTAMPING)) {
target_expected_timestamp_version = target_timestamp_version;
}
break;
@@ -2637,6 +2758,26 @@ get_timeout:
(target_expected_timestamp_version == TARGET_TIMESTAMP_NEW);
optname = SO_TIMESTAMP;
goto int_case;
+ case TARGET_SO_TIMESTAMPNS_OLD:
+ timestamp_format_matches =
+ (target_expected_timestamp_version == TARGET_TIMESTAMP_OLD);
+ optname = SO_TIMESTAMPNS;
+ goto int_case;
+ case TARGET_SO_TIMESTAMPNS_NEW:
+ timestamp_format_matches =
+ (target_expected_timestamp_version == TARGET_TIMESTAMP_NEW);
+ optname = SO_TIMESTAMPNS;
+ goto int_case;
+ case TARGET_SO_TIMESTAMPING_OLD:
+ timestamp_format_matches =
+ (target_expected_timestamp_version == TARGET_TIMESTAMP_OLD);
+ optname = SO_TIMESTAMPING;
+ goto int_case;
+ case TARGET_SO_TIMESTAMPING_NEW:
+ timestamp_format_matches =
+ (target_expected_timestamp_version == TARGET_TIMESTAMP_NEW);
+ optname = SO_TIMESTAMPING;
+ goto int_case;
case TARGET_SO_RCVLOWAT:
optname = SO_RCVLOWAT;
goto int_case;
@@ -2661,9 +2802,9 @@ get_timeout:
return ret;
if (optname == SO_TYPE) {
val = host_to_target_sock_type(val);
- }
- if (optname == SO_TIMESTAMP) {
- val = val && timestamp_format_matches;
+ } else if ((optname == SO_TIMESTAMP || optname == SO_TIMESTAMPNS ||
+ optname == SO_TIMESTAMPING) && !timestamp_format_matches) {
+ val = 0;
}
if (len > lv)
len = lv;
diff --git a/tests/tcg/multiarch/socket_timestamp.c
b/tests/tcg/multiarch/socket_timestamp.c
index 71ab1845de..3ae833ad44 100644
--- a/tests/tcg/multiarch/socket_timestamp.c
+++ b/tests/tcg/multiarch/socket_timestamp.c
@@ -1,5 +1,6 @@
#include <assert.h>
#include <errno.h>
+#include <linux/net_tstamp.h>
#include <linux/types.h>
#include <netinet/in.h>
#include <stdint.h>
@@ -11,6 +12,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <time.h>
#include <unistd.h>
#ifdef __kernel_old_timeval
@@ -27,6 +29,33 @@ struct kernel_sock_timeval {
int64_t tv_usec;
};
+struct kernel_old_timespec {
+ __kernel_long_t tv_sec;
+ long tv_nsec;
+};
+
+struct kernel_timespec {
+ int64_t tv_sec;
+ long long tv_nsec;
+};
+
+struct scm_timestamping {
+ struct timespec ts[3];
+};
+
+struct scm_old_timestamping {
+ struct kernel_old_timespec ts[3];
+};
+
+struct scm_timestamping64 {
+ struct kernel_timespec ts[3];
+};
+
+const int so_timestamping_flags =
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE;
+
int create_udp_socket(struct sockaddr_in *sockaddr)
{
socklen_t sockaddr_len;
@@ -61,43 +90,47 @@ int create_udp_socket(struct sockaddr_in *sockaddr)
* Checks that the timestamp in the message is not after the reception
timestamp
* as well as the reception time is within 10 seconds of the message time.
*/
-void check_timestamp_difference(const struct timeval *msg_tv,
- const struct timeval *pkt_tv)
+void check_timestamp_difference(const struct timespec *msg_ts,
+ const struct timespec *pkt_ts)
{
- if (pkt_tv->tv_sec < msg_tv->tv_sec ||
- (pkt_tv->tv_sec == msg_tv->tv_sec && pkt_tv->tv_usec <
msg_tv->tv_usec))
+ if (pkt_ts->tv_sec < msg_ts->tv_sec ||
+ (pkt_ts->tv_sec == msg_ts->tv_sec && pkt_ts->tv_nsec <
msg_ts->tv_nsec))
{
fprintf(stderr,
- "Packet received before sent: %lld.%06lld < %lld.%06lld\n",
- (long long)pkt_tv->tv_sec, (long long)pkt_tv->tv_usec,
- (long long)msg_tv->tv_sec, (long long)msg_tv->tv_usec);
+ "Packet received before sent: %lld.%06lld < %lld.%09lld\n",
+ (long long)pkt_ts->tv_sec, (long long)pkt_ts->tv_nsec,
+ (long long)msg_ts->tv_sec, (long long)msg_ts->tv_nsec);
exit(-1);
}
- if (pkt_tv->tv_sec > msg_tv->tv_sec + 10 ||
- (pkt_tv->tv_sec == msg_tv->tv_sec + 10 &&
- pkt_tv->tv_usec > msg_tv->tv_usec)) {
+ if (pkt_ts->tv_sec > msg_ts->tv_sec + 10 ||
+ (pkt_ts->tv_sec == msg_ts->tv_sec + 10 &&
+ pkt_ts->tv_nsec > msg_ts->tv_nsec)) {
fprintf(stderr,
"Packet received more than 10 seconds after sent: "
- "%lld.%06lld > %lld.%06lld + 10\n",
- (long long)pkt_tv->tv_sec, (long long)pkt_tv->tv_usec,
- (long long)msg_tv->tv_sec, (long long)msg_tv->tv_usec);
+ "%lld.%06lld > %lld.%09lld + 10\n",
+ (long long)pkt_ts->tv_sec, (long long)pkt_ts->tv_nsec,
+ (long long)msg_ts->tv_sec, (long long)msg_ts->tv_nsec);
exit(-1);
}
}
void send_current_time(int sock, struct sockaddr_in server_sockaddr)
{
- struct timeval tv = {0, 0};
- gettimeofday(&tv, NULL);
- sendto(sock, &tv, sizeof(tv), 0, (struct sockaddr *)&server_sockaddr,
+ struct timespec ts = {0, 0};
+ clock_gettime(CLOCK_REALTIME, &ts);
+#ifdef MSG_CONFIRM
+ const int flags = MSG_CONFIRM;
+#else
+ const int flags = 0;
+#endif
+ sendto(sock, &ts, sizeof(ts), flags, (struct sockaddr *)&server_sockaddr,
sizeof(server_sockaddr));
}
-typedef void (*get_timeval_t)(const struct cmsghdr *cmsg, struct timeval *tv);
+typedef void (*get_timespec_t)(const struct cmsghdr *cmsg, struct timespec
*tv);
-
-void receive_packet(int sock, get_timeval_t get_timeval)
+void receive_packet(int sock, get_timespec_t get_timespec)
{
struct msghdr msg = {0};
@@ -113,7 +146,7 @@ void receive_packet(int sock, get_timeval_t get_timeval)
struct cmsghdr align;
} u;
struct cmsghdr *cmsg;
- struct timeval msg_tv, pkt_tv;
+ struct timespec msg_ts, pkt_ts;
int res;
@@ -134,31 +167,35 @@ void receive_packet(int sock, get_timeval_t get_timeval)
assert(res == sizeof(struct timeval));
assert(iov.iov_base == iobuf);
- memcpy(&msg_tv, iov.iov_base, sizeof(msg_tv));
- printf("Message timestamp: %lld.%06lld\n",
- (long long)msg_tv.tv_sec, (long long)msg_tv.tv_usec);
+ memcpy(&msg_ts, iov.iov_base, sizeof(msg_ts));
+ printf("Message timestamp: %lld.%09lld\n",
+ (long long)msg_ts.tv_sec, (long long)msg_ts.tv_nsec);
cmsg = CMSG_FIRSTHDR(&msg);
assert(cmsg);
- (*get_timeval)(cmsg, &pkt_tv);
- printf("Packet timestamp: %lld.%06lld\n",
- (long long)pkt_tv.tv_sec, (long long)pkt_tv.tv_usec);
+ (*get_timespec)(cmsg, &pkt_ts);
+ printf("Packet timestamp: %lld.%09lld\n",
+ (long long)pkt_ts.tv_sec, (long long)pkt_ts.tv_nsec);
- check_timestamp_difference(&msg_tv, &pkt_tv);
+ check_timestamp_difference(&msg_ts, &pkt_ts);
}
-void get_timeval_from_so_timestamp(const struct cmsghdr *cmsg,
- struct timeval *tv)
+void get_timespec_from_so_timestamp(const struct cmsghdr *cmsg,
+ struct timespec *ts)
{
+ struct timeval tv;
assert(cmsg->cmsg_level == SOL_SOCKET);
assert(cmsg->cmsg_type == SCM_TIMESTAMP);
- assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)));
- memcpy(tv, CMSG_DATA(cmsg), sizeof(*tv));
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(tv)));
+
+ memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000LL;
}
#ifdef SO_TIMESTAMP_OLD
-void get_timeval_from_so_timestamp_old(const struct cmsghdr *cmsg,
- struct timeval *tv)
+void get_timespec_from_so_timestamp_old(const struct cmsghdr *cmsg,
+ struct timespec *ts)
{
struct kernel_old_timeval old_tv;
assert(cmsg->cmsg_level == SOL_SOCKET);
@@ -166,13 +203,13 @@ void get_timeval_from_so_timestamp_old(const struct
cmsghdr *cmsg,
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(old_tv)));
memcpy(&old_tv, CMSG_DATA(cmsg), sizeof(old_tv));
- tv->tv_sec = old_tv.tv_sec;
- tv->tv_usec = old_tv.tv_usec;
+ ts->tv_sec = old_tv.tv_sec;
+ ts->tv_nsec = old_tv.tv_usec * 1000LL;
}
#ifdef SO_TIMESTAMP_NEW
-void get_timeval_from_so_timestamp_new(const struct cmsghdr *cmsg,
- struct timeval *tv)
+void get_timespec_from_so_timestamp_new(const struct cmsghdr *cmsg,
+ struct timespec *ts)
{
struct kernel_sock_timeval sock_tv;
assert(cmsg->cmsg_level == SOL_SOCKET);
@@ -180,42 +217,298 @@ void get_timeval_from_so_timestamp_new(const struct
cmsghdr *cmsg,
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(sock_tv)));
memcpy(&sock_tv, CMSG_DATA(cmsg), sizeof(sock_tv));
- tv->tv_sec = sock_tv.tv_sec;
- tv->tv_usec = sock_tv.tv_usec;
+ ts->tv_sec = sock_tv.tv_sec;
+ ts->tv_nsec = sock_tv.tv_usec * 1000LL;
}
#endif /* defined(SO_TIMESTAMP_NEW) */
#endif /* defined(SO_TIMESTAMP_OLD) */
-void set_socket_option(int sock, int sockopt, int on)
+void get_timespec_from_so_timestampns(const struct cmsghdr *cmsg,
+ struct timespec *ts)
+{
+ assert(cmsg->cmsg_level == SOL_SOCKET);
+ assert(cmsg->cmsg_type == SCM_TIMESTAMPNS);
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(*ts)));
+
+ memcpy(ts, CMSG_DATA(cmsg), sizeof(*ts));
+}
+
+#ifdef SO_TIMESTAMPNS_OLD
+void get_timespec_from_so_timestampns_old(const struct cmsghdr *cmsg,
+ struct timespec *ts)
+{
+ struct kernel_old_timespec old_ts;
+ assert(cmsg->cmsg_level == SOL_SOCKET);
+ assert(cmsg->cmsg_type == SO_TIMESTAMPNS_OLD);
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(old_ts)));
+
+ memcpy(&old_ts, CMSG_DATA(cmsg), sizeof(old_ts));
+ ts->tv_sec = old_ts.tv_sec;
+ ts->tv_nsec = old_ts.tv_nsec;
+}
+
+#ifdef SO_TIMESTAMPNS_NEW
+void get_timespec_from_so_timestampns_new(const struct cmsghdr *cmsg,
+ struct timespec *ts)
+{
+ struct kernel_timespec sock_ts;
+ assert(cmsg->cmsg_level == SOL_SOCKET);
+ assert(cmsg->cmsg_type == SO_TIMESTAMPNS_NEW);
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(sock_ts)));
+
+ memcpy(&sock_ts, CMSG_DATA(cmsg), sizeof(sock_ts));
+ ts->tv_sec = sock_ts.tv_sec;
+ ts->tv_nsec = sock_ts.tv_nsec;
+}
+#endif /* defined(SO_TIMESTAMPNS_NEW) */
+#endif /* defined(SO_TIMESTAMPNS_OLD) */
+
+void get_timespec_from_so_timestamping(const struct cmsghdr *cmsg,
+ struct timespec *ts)
+{
+ int i;
+ struct scm_timestamping tss;
+ assert(cmsg->cmsg_level == SOL_SOCKET);
+ assert(cmsg->cmsg_type == SCM_TIMESTAMPING);
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(tss)));
+
+ memcpy(&tss, CMSG_DATA(cmsg), sizeof(tss));
+
+ for (i = 0; i < 3; ++i) {
+ if (tss.ts[i].tv_sec || tss.ts[i].tv_nsec) {
+ *ts = tss.ts[i];
+ return;
+ }
+ }
+ assert(!"All three entries in scm_timestamping are empty");
+}
+
+#ifdef SO_TIMESTAMPING_OLD
+void get_timespec_from_so_timestamping_old(const struct cmsghdr *cmsg,
+ struct timespec *ts)
+{
+ int i;
+ struct scm_old_timestamping tss;
+ assert(cmsg->cmsg_level == SOL_SOCKET);
+ assert(cmsg->cmsg_type == SO_TIMESTAMPING_OLD);
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(tss)));
+
+ memcpy(&tss, CMSG_DATA(cmsg), sizeof(tss));
+
+ for (i = 0; i < 3; ++i) {
+ if (tss.ts[i].tv_sec || tss.ts[i].tv_nsec) {
+ ts->tv_sec = tss.ts[i].tv_sec;
+ ts->tv_nsec = tss.ts[i].tv_nsec;
+ return;
+ }
+ }
+ assert(!"All three entries in scm_old_timestamping are empty");
+}
+
+#ifdef SO_TIMESTAMPING_NEW
+void get_timespec_from_so_timestamping_new(const struct cmsghdr *cmsg,
+ struct timespec *ts)
+{
+ int i;
+ struct scm_timestamping64 tss;
+ assert(cmsg->cmsg_level == SOL_SOCKET);
+ assert(cmsg->cmsg_type == SO_TIMESTAMPING_NEW);
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(tss)));
+
+ memcpy(&tss, CMSG_DATA(cmsg), sizeof(tss));
+ for (i = 0; i < 3; ++i) {
+ if (tss.ts[i].tv_sec || tss.ts[i].tv_nsec) {
+ ts->tv_sec = tss.ts[i].tv_sec;
+ ts->tv_nsec = tss.ts[i].tv_nsec;
+ return;
+ }
+ }
+ assert(!"All three entries in scm_timestamp64 are empty");
+}
+#endif /* defined(SO_TIMESTAMPING_NEW) */
+#endif /* defined(SO_TIMESTAMPING_OLD) */
+
+void set_socket_option(int sock, int sockopt, int set_to)
{
socklen_t len;
- int val = on;
+ int val = set_to;
if (setsockopt(sock, SOL_SOCKET, sockopt, &val, sizeof(val)) < 0) {
int err = errno;
- fprintf(stderr, "Failed to setsockopt %d (%s): %s\n",
- sockopt, on ? "on" : "off", strerror(err));
+ fprintf(stderr, "Failed at setsockopt(%d, SOL_SOCKET, %d, %d): %s\n",
+ sock, sockopt, set_to, strerror(err));
exit(err);
}
+#ifdef SO_TIMESTAMPING_NEW
+ if (sockopt == SO_TIMESTAMPING_NEW) {
+ /*
+ * `getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING_NEW)` not implemented
+ * as of linux kernel v5.8-rc4.
+ */
+ return;
+ }
+#endif
+
len = sizeof(val);
val = -1;
if (getsockopt(sock, SOL_SOCKET, sockopt, &val, &len) < 0) {
int err = errno;
- fprintf(stderr, "Failed to getsockopt (%d): %s\n", sock,
strerror(err));
+ fprintf(stderr, "Failed at getsockopt(%d, SOL_SOCKET, %d): %s\n",
+ sock, sockopt, strerror(err));
exit(err);
}
assert(len == sizeof(val));
- assert(val == on);
+ assert(val == set_to);
+}
+
+void child_steps(int sock, struct sockaddr_in addr, int run_old)
+{
+ /* Test 1: SO_TIMESTAMP */
+ send_current_time(sock, addr);
+
+ /* Test 2: SO_TIMESTAMPNS */
+ printf("Test 2: SO_TIMESTAMPNS\n");
+ set_socket_option(sock, SO_TIMESTAMPNS, 1);
+ receive_packet(sock, &get_timespec_from_so_timestampns);
+ set_socket_option(sock, SO_TIMESTAMPNS, 0);
+
+ /* Test 3: SO_TIMESTAMPING */
+ send_current_time(sock, addr);
+
+ if (!run_old) {
+ return;
+ }
+
+#ifdef SO_TIMESTAMP_OLD
+ if (SO_TIMESTAMP_OLD != SO_TIMESTAMP) {
+ /* Test 4a: SO_TIMESTAMP_OLD */
+ printf("Test 4a: SO_TIMESTAMP_OLD\n");
+ set_socket_option(sock, SO_TIMESTAMP_OLD, 1);
+ receive_packet(sock, &get_timespec_from_so_timestamp_old);
+ set_socket_option(sock, SO_TIMESTAMP_OLD, 0);
+ }
+#ifdef SO_TIMESTAMP_NEW
+ else {
+ /* Test 4b: SO_TIMESTAMP_NEW */
+ printf("Test 4b: SO_TIMESTAMP_NEW\n");
+ set_socket_option(sock, SO_TIMESTAMP_NEW, 1);
+ receive_packet(sock, &get_timespec_from_so_timestamp_new);
+ set_socket_option(sock, SO_TIMESTAMP_NEW, 0);
+ }
+#endif /* defined(SO_TIMESTAMP_NEW) */
+#endif /* defined(SO_TIMESTAMP_OLD) */
+
+#ifdef SO_TIMESTAMPNS_OLD
+ if (SO_TIMESTAMPNS_OLD != SO_TIMESTAMPNS) {
+ /* Test 5a: SO_TIMESTAMPNS_OLD */
+ send_current_time(sock, addr);
+ }
+#ifdef SO_TIMESTAMPNS_NEW
+ else {
+ /* Test 5b: SO_TIMESTAMPNS_NEW */
+ send_current_time(sock, addr);
+ }
+#endif /* defined(SO_TIMESTAMPNS_NEW) */
+#endif /* defined(SO_TIMESTAMPNS_OLD) */
+
+#ifdef SO_TIMESTAMPING_OLD
+ if (SO_TIMESTAMPING_OLD != SO_TIMESTAMPING) {
+ /* Test 6a: SO_TIMESTAMPING_OLD */
+ printf("Test 6a: SO_TIMESTAMPING_OLD\n");
+ set_socket_option(sock, SO_TIMESTAMPING_OLD, so_timestamping_flags);
+ receive_packet(sock, &get_timespec_from_so_timestamping_old);
+ set_socket_option(sock, SO_TIMESTAMPING_OLD, 0);
+ }
+#ifdef SO_TIMESTAMPING_NEW
+ else {
+ /* Test 6b: SO_TIMESTAMPING_NEW */
+ printf("Test 6b: SO_TIMESTAMPING_NEW\n");
+ set_socket_option(sock, SO_TIMESTAMPING_NEW, so_timestamping_flags);
+ receive_packet(sock, &get_timespec_from_so_timestamping_new);
+ set_socket_option(sock, SO_TIMESTAMPING_NEW, 0);
+ }
+#endif /* defined(SO_TIMESTAMPING_NEW) */
+#endif /* defined(SO_TIMESTAMPING_OLD) */
+}
+
+void parent_steps(int sock, struct sockaddr_in addr, int run_old)
+{
+ /* Test 1: SO_TIMESTAMP */
+ printf("Test 1: SO_TIMESTAMP\n");
+ set_socket_option(sock, SO_TIMESTAMP, 1);
+ receive_packet(sock, &get_timespec_from_so_timestamp);
+ set_socket_option(sock, SO_TIMESTAMP, 0);
+
+ /* Test 2: SO_TIMESTAMPNS */
+ send_current_time(sock, addr);
+
+ /* Test 3: SO_TIMESTAMPING */
+ printf("Test 3: SO_TIMESTAMPING\n");
+ set_socket_option(sock, SO_TIMESTAMPING, so_timestamping_flags);
+ receive_packet(sock, &get_timespec_from_so_timestamping);
+ set_socket_option(sock, SO_TIMESTAMPING, 0);
+
+ if (!run_old) {
+ return;
+ }
+
+#ifdef SO_TIMESTAMP_OLD
+ if (SO_TIMESTAMP_OLD != SO_TIMESTAMP) {
+ /* Test 4a: SO_TIMESTAMP_OLD */
+ send_current_time(sock, addr);
+ }
+#ifdef SO_TIMESTAMP_NEW
+ else {
+ /* Test 4b: SO_TIMESTAMP_NEW */
+ send_current_time(sock, addr);
+ }
+#endif /* defined(SO_TIMESTAMP_NEW) */
+#endif /* defined(SO_TIMESTAMP_OLD) */
+
+#ifdef SO_TIMESTAMPNS_OLD
+ if (SO_TIMESTAMPNS_OLD != SO_TIMESTAMPNS) {
+ /* Test 5a: SO_TIMESTAMPNS_OLD */
+ printf("Test 5a: SO_TIMESTAMPNS_OLD\n");
+ set_socket_option(sock, SO_TIMESTAMPNS_OLD, 1);
+ receive_packet(sock, &get_timespec_from_so_timestampns_old);
+ set_socket_option(sock, SO_TIMESTAMPNS_OLD, 0);
+ }
+#ifdef SO_TIMESTAMPNS_NEW
+ else {
+ /* Test 5b: SO_TIMESTAMPNS_NEW */
+ printf("Test 5b: SO_TIMESTAMPNS_NEW\n");
+ set_socket_option(sock, SO_TIMESTAMPNS_NEW, 1);
+ receive_packet(sock, &get_timespec_from_so_timestampns_new);
+ set_socket_option(sock, SO_TIMESTAMPNS_NEW, 0);
+ }
+#endif /* defined(SO_TIMESTAMPNS_NEW) */
+#endif /* defined(SO_TIMESTAMPNS_OLD) */
+
+#ifdef SO_TIMESTAMPING_OLD
+ if (SO_TIMESTAMPING_OLD != SO_TIMESTAMPING) {
+ /* Test 6a: SO_TIMESTAMPING_OLD */
+ send_current_time(sock, addr);
+ }
+#ifdef SO_TIMESTAMPING_NEW
+ else {
+ /* Test 6b: SO_TIMESTAMPING_NEW */
+ send_current_time(sock, addr);
+ }
+#endif /* defined(SO_TIMESTAMPING_NEW) */
+#endif /* defined(SO_TIMESTAMPING_OLD) */
}
int main(int argc, char **argv)
{
int parent_sock, child_sock;
struct sockaddr_in parent_sockaddr, child_sockaddr;
- int pid;
+ int pid, run_old;
struct timeval tv = {0, 0};
gettimeofday(&tv, NULL);
+ /* Too close to y2038 old systems may not work. */
+ run_old = tv.tv_sec < 0x7fffff00;
+
parent_sock = create_udp_socket(&parent_sockaddr);
child_sock = create_udp_socket(&child_sockaddr);
@@ -226,64 +519,15 @@ int main(int argc, char **argv)
if (pid < 0) {
fprintf(stderr, "SKIPPED. Failed to fork: %s\n", strerror(errno));
} else if (pid == 0) {
- close(child_sock);
-
- /* Test 1: SO_TIMESTAMP */
- send_current_time(parent_sock, child_sockaddr);
-
- if (tv.tv_sec > 0x7fffff00) {
- /* Too close to y2038 problem, old system may not work. */
- close(parent_sock);
- return 0;
- }
-
-#ifdef SO_TIMESTAMP_OLD
- if (SO_TIMESTAMP_OLD != SO_TIMESTAMP) {
- /* Test 2a: SO_TIMESTAMP_OLD */
- set_socket_option(parent_sock, SO_TIMESTAMP_OLD, 1);
- receive_packet(parent_sock, &get_timeval_from_so_timestamp_old);
- set_socket_option(parent_sock, SO_TIMESTAMP_OLD, 0);
- }
-#ifdef SO_TIMESTAMP_NEW
- else {
- /* Test 2b: SO_TIMESTAMP_NEW */
- set_socket_option(parent_sock, SO_TIMESTAMP_NEW, 1);
- receive_packet(parent_sock, &get_timeval_from_so_timestamp_new);
- set_socket_option(parent_sock, SO_TIMESTAMP_NEW, 0);
- }
-#endif /* defined(SO_TIMESTAMP_NEW) */
-#endif /* defined(SO_TIMESTAMP_OLD) */
-
close(parent_sock);
+ child_steps(child_sock, parent_sockaddr, run_old);
+ close(child_sock);
} else {
int child_status;
- close(parent_sock);
-
- /* Test 1: SO_TIMESTAMP */
- set_socket_option(child_sock, SO_TIMESTAMP, 1);
- receive_packet(child_sock, &get_timeval_from_so_timestamp);
- set_socket_option(child_sock, SO_TIMESTAMP, 0);
-
- if (tv.tv_sec > 0x7fffff00) {
- /* Too close to y2038 problem, old system may not work. */
- close(child_sock);
- return 0;
- }
-
-#ifdef SO_TIMESTAMP_OLD
- if (SO_TIMESTAMP_OLD != SO_TIMESTAMP) {
- /* Test 2a: SO_TIMESTAMP_OLD */
- send_current_time(child_sock, parent_sockaddr);
- }
-#ifdef SO_TIMESTAMP_NEW
- else {
- /* Test 2b: SO_TIMESTAMP_NEW */
- send_current_time(child_sock, parent_sockaddr);
- }
-#endif /* defined(SO_TIMESTAMP_NEW) */
-#endif /* defined(SO_TIMESTAMP_OLD) */
close(child_sock);
+ parent_steps(parent_sock, child_sockaddr, run_old);
+ close(parent_sock);
if (waitpid(pid, &child_status, 0) < 0) {
int err = errno;
--
2.28.0.220.ged08abb693-goog
- [PATCH v2 1/8] linux-user: Support F_ADD_SEALS and F_GET_SEALS fcntls, (continued)
- [PATCH v2 1/8] linux-user: Support F_ADD_SEALS and F_GET_SEALS fcntls, Shu-Chun Weng, 2020/08/11
- [PATCH v2 2/8] linux-user: add missing UDP get/setsockopt option, Shu-Chun Weng, 2020/08/11
- [PATCH v2 3/8] linux-user: add missing IPv6 get/setsockopt option, Shu-Chun Weng, 2020/08/11
- [PATCH v2 4/8] linux-user: Add IPv6 options to do_print_sockopt(), Shu-Chun Weng, 2020/08/11
- [PATCH v2 5/8] linux-user: Update SO_TIMESTAMP to SO_TIMESTAMP_OLD/NEW, Shu-Chun Weng, 2020/08/11
- [PATCH v2 7/8] thunk: supports flexible arrays, Shu-Chun Weng, 2020/08/11
- [PATCH v2 6/8] linux-user: setsockopt() SO_TIMESTAMPNS and SO_TIMESTAMPING,
Shu-Chun Weng <=
- [PATCH v2 8/8] linux-user: Add support for SIOCETHTOOL ioctl, Shu-Chun Weng, 2020/08/11