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 #endif +#define SO_TIMESTAMP_OLD 29 +#define SO_TIMESTAMPNS_OLD 35 +#define SO_TIMESTAMPING_OLD 37 +#define SCM_TIMESTAMP_OLD SO_TIMESTAMP_OLD +#define SCM_TIMESTAMPNS_OLD SO_TIMESTAMPNS_OLD +#define SCM_TIMESTAMPING_OLD SO_TIMESTAMPING_OLD + #ifndef SIOCGSTAMP_OLD #define SIOCGSTAMP_OLD 0x8906 #endif 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 #include #include 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; + case SO_TIMESTAMP: + case SO_TIMESTAMPNS: + if (SO_TIMESTAMP == SO_TIMESTAMP_OLD) 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 #include +#include +#include +#include #include "syscall.h" +static void convert_scm_timestamps(struct msghdr *msg, socklen_t csize) +{ + if (SCM_TIMESTAMP == SCM_TIMESTAMP_OLD) return; + 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) { + case SCM_TIMESTAMP_OLD: + have_tv = 1; + tv.tv_sec = *(long *)(CMSG_DATA(cmsg) + 0); + tv.tv_usec = *(long *)(CMSG_DATA(cmsg) + 4); + break; + case SCM_TIMESTAMPNS_OLD: + 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; #if LONG_MAX > INT_MAX struct msghdr h, *orig = msg; if (msg) { @@ -14,6 +66,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) } #endif r = socketcall_cp(recvmsg, fd, msg, flags, 0, 0, 0); + if (r >= 0) convert_scm_timestamps(msg, orig_controllen); #if LONG_MAX > INT_MAX if (orig) *orig = h; #endif 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; + case SO_TIMESTAMP: + case SO_TIMESTAMPNS: + if (SO_TIMESTAMP == SO_TIMESTAMP_OLD) 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);