mailing list of musl libc
 help / color / Atom feed
* probably need to emulate SO_TIMESTAMP
@ 2019-12-17  5:08 Rich Felker
  0 siblings, 0 replies; only message in thread
From: Rich Felker @ 2019-12-17  5:08 UTC (permalink / raw)
  To: musl

[-- Attachment #1: Type: text/plain, Size: 2661 bytes --]

It's come to my attention that a good deal of software uses
SO_TIMESTAMP (the socket option for receiving timestamps as control
messages attached to datagrams), whose definition changes for time64.
Without any further measures, it fails with ENOPROTOOPT on pre-5.1
kernels. Some programs handle this gracefully; others treat it as a
hard error. Apparently it's old enough, and widely available enough on
non-Linux, that it's fairly "reasonable" to just assume it works.

Since otherwise this looks like it's going to be a big breaking part
of the time64 release, I'm strongly leaning towards emulating
SO_TIMESTAMP (and SO_TIMESTAMPNS) for pre-5.1 kernels. This involves
converting the setsockopt/getsockopt calls to use the old option as a
fallback, but more importantly, having recvmsg scan and translate
received control messages. A draft of the patch is attached.

Since recvmsg can't distinguish between being called by a time32
binary (that intentionally requested SO_TIMESTAMP_OLD) and being
called by a time64 binary (that got SO_TIMESTAMP_OLD as a fallback),
it doesn't replace the old control message (this couldn't be done
in-place anyway, since the new one is larger) but appends it. Programs
running on pre-5.1 kernels, and all legacy time32 callers, will then
see both SCM_TIMESTAMP_OLD and SCM_TIMESTAMP[_NEW] control messages,
assuming both fit (if not, perhaps we should set MSG_CTRUNC flag like
the kernel would).

The related SO_TIMESTAMPING option is significantly harder to emulate
in a feature-complete manner, since it allows SCM_TIMESTAMPING to be
sent as a control message on a per-sendmsg basis, to control send
timestamp generation. A feature-incomplete version could be done just
like the other two, but it's not clear how beneficial that would be;
from my research so far (Debian Code Search, mostly, after some
initial reports of problems) it seems that SO_TIMESTAMPING is pretty
much always treated as a feature that might or might not be present or
work, and that absence is non-fatal. This makes sense because it's a
lot newer and seems Linux-specific.

One issue I've not mentioned yet is recvmmsg, which is an utter mess.
The O(1)-space emulation approach that works for recvmsg becomes O(n)
with recvmmsg, since there's nowhere to keep the original
msg_controllen for each msghdr. This means we need to limit vlen,
emulate it with recvmsg in a loop, or limit vlen in each loop
iteration but loop until the whole requested vlen is completed or
other exit conditions occur. My leaning would be just calling recvmmsg
in a loop since this whole function is such a mess.

Thoughts? Any potential problems with the attached?


[-- Attachment #2: emulate_so_timestamp.diff --]
[-- Type: text/plain, Size: 4429 bytes --]

diff --git a/src/internal/syscall.h b/src/internal/syscall.h
index 9f2784db..d768fb64 100644
--- a/src/internal/syscall.h
+++ b/src/internal/syscall.h
@@ -306,6 +306,13 @@ hidden long __syscall_ret(unsigned long),
 #define SO_SNDTIMEO_OLD  21
+#define SO_TIMESTAMP_OLD    29
 #define SIOCGSTAMP_OLD 0x8906
diff --git a/src/mman/mmap.c b/src/mman/mmap.c
index eff88d82..2da11b87 100644
--- a/src/mman/mmap.c
+++ b/src/mman/mmap.c
@@ -1,3 +1,4 @@
+#define SYSCALL_NO_TLS 1
 #include <unistd.h>
 #include <sys/mman.h>
 #include <errno.h>
diff --git a/src/network/getsockopt.c b/src/network/getsockopt.c
index e871d624..d3640d9c 100644
--- a/src/network/getsockopt.c
+++ b/src/network/getsockopt.c
@@ -26,6 +26,15 @@ int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t
 			tv->tv_sec = tv32[0];
 			tv->tv_usec = tv32[1];
 			*optlen = sizeof *tv;
+			break;
+			if (optname==SO_TIMESTAMP) optname=SO_TIMESTAMP_OLD;
+			if (optname==SO_TIMESTAMPNS) optname=SO_TIMESTAMPNS_OLD;
+			r = __socketcall(getsockopt, fd, level,
+				optname, optval, optlen, 0);
+			break;
 	return __syscall_ret(r);
diff --git a/src/network/recvmsg.c b/src/network/recvmsg.c
index 4ca7da8b..50ea082d 100644
--- a/src/network/recvmsg.c
+++ b/src/network/recvmsg.c
@@ -1,10 +1,62 @@
 #include <sys/socket.h>
 #include <limits.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
 #include "syscall.h"
+static void convert_scm_timestamps(struct msghdr *msg, socklen_t csize)
+	if (!msg->msg_control || !msg->msg_controllen) return;
+	int have_tv=0, have_ts=0;
+	struct timeval tv;
+	struct timespec ts;
+	struct cmsghdr *cmsg, *last=0;
+	socklen_t clen = msg->msg_controllen;
+	for (cmsg=CMSG_FIRSTHDR(msg); cmsg; cmsg=CMSG_NXTHDR(msg, cmsg)) {
+		if (cmsg->cmsg_level==SOL_SOCKET) switch (cmsg->cmsg_type) {
+			have_tv = 1;
+			tv.tv_sec = *(long *)(CMSG_DATA(cmsg) + 0);
+			tv.tv_usec = *(long *)(CMSG_DATA(cmsg) + 4);
+			break;
+			have_ts = 1;
+			ts.tv_sec = *(long *)(CMSG_DATA(cmsg) + 0);
+			ts.tv_nsec = *(long *)(CMSG_DATA(cmsg) + 4);
+			break;
+		}
+		last = cmsg;
+	}
+	if (!last) return;
+	msg->msg_controllen = csize;
+	cmsg = CMSG_NXTHDR(msg, last);
+	if (cmsg && have_ts && CMSG_SPACE(sizeof ts)<=csize-clen) {
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_TIMESTAMPNS;
+		cmsg->cmsg_len = CMSG_LEN(sizeof ts);
+		memcpy(CMSG_DATA(cmsg), &ts, sizeof ts);
+		cmsg = CMSG_NXTHDR(msg, cmsg);
+		clen += CMSG_SPACE(sizeof ts);
+	}
+	if (cmsg && have_tv && CMSG_SPACE(sizeof tv)<=csize-clen) {
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_TIMESTAMP;
+		cmsg->cmsg_len = CMSG_LEN(sizeof tv);
+		memcpy(CMSG_DATA(cmsg), &tv, sizeof tv);
+		cmsg = CMSG_NXTHDR(msg, cmsg);
+		clen += CMSG_SPACE(sizeof tv);
+	}
+	msg->msg_controllen = clen;
 ssize_t recvmsg(int fd, struct msghdr *msg, int flags)
 	ssize_t r;
+	socklen_t orig_controllen = msg->msg_controllen;
 	struct msghdr h, *orig = msg;
 	if (msg) {
@@ -14,6 +66,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags)
 	r = socketcall_cp(recvmsg, fd, msg, flags, 0, 0, 0);
+	if (r >= 0) convert_scm_timestamps(msg, orig_controllen);
 	if (orig) *orig = h;
diff --git a/src/network/setsockopt.c b/src/network/setsockopt.c
index 2c188a96..612a1947 100644
--- a/src/network/setsockopt.c
+++ b/src/network/setsockopt.c
@@ -31,6 +31,15 @@ int setsockopt(int fd, int level, int optname, const void *optval, socklen_t opt
 			r = __socketcall(setsockopt, fd, level, optname,
 				((long[]){s, CLAMP(us)}), 2*sizeof(long), 0);
+			break;
+			if (optname==SO_TIMESTAMP) optname=SO_TIMESTAMP_OLD;
+			if (optname==SO_TIMESTAMPNS) optname=SO_TIMESTAMPNS_OLD;
+			r = __socketcall(setsockopt, fd, level,
+				optname, optval, optlen, 0);
+			break;
 	return __syscall_ret(r);

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, back to index

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-17  5:08 probably need to emulate SO_TIMESTAMP Rich Felker

mailing list of musl libc

Archives are clonable: git clone --mirror

Example config snippet for mirrors

Newsgroup available over NNTP:

AGPL code for this site: git clone