From 47b3e98af8bfbf469391458fdb4e4b4783562b0c Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 16 Sep 2020 12:32:45 -0400 Subject: [PATCH] Kernel+LibC+UserspaceEmulator: Add SO_TIMESTAMP, and cmsg definitions When SO_TIMESTAMP is set as an option on a SOCK_DGRAM socket, then recvmsg() will return a SCM_TIMESTAMP control message that contains a struct timeval with the system time that was current when the socket was received. --- DevTools/UserspaceEmulator/Emulator.cpp | 4 +-- Kernel/Net/Socket.cpp | 20 +++++++++++++ Kernel/Net/Socket.h | 3 ++ Kernel/Syscalls/socket.cpp | 17 +++++++++++ Kernel/UnixTypes.h | 12 ++++++++ Libraries/LibC/sys/socket.h | 40 +++++++++++++++++++++++++ 6 files changed, 94 insertions(+), 2 deletions(-) diff --git a/DevTools/UserspaceEmulator/Emulator.cpp b/DevTools/UserspaceEmulator/Emulator.cpp index f523758f7c..42c6ed19e0 100644 --- a/DevTools/UserspaceEmulator/Emulator.cpp +++ b/DevTools/UserspaceEmulator/Emulator.cpp @@ -490,10 +490,10 @@ int Emulator::virt$setsockopt(FlatPtr params_addr) Syscall::SC_setsockopt_params params; mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - if (params.option == SO_RCVTIMEO) { + if (params.option == SO_RCVTIMEO || params.option == SO_TIMESTAMP) { auto host_value_buffer = ByteBuffer::create_zeroed(params.value_size); mmu().copy_from_vm(host_value_buffer.data(), (FlatPtr)params.value, params.value_size); - int rc = setsockopt(params.sockfd, params.level, SO_RCVTIMEO, host_value_buffer.data(), host_value_buffer.size()); + int rc = setsockopt(params.sockfd, params.level, params.option, host_value_buffer.data(), host_value_buffer.size()); if (rc < 0) return -errno; return rc; diff --git a/Kernel/Net/Socket.cpp b/Kernel/Net/Socket.cpp index 701155089b..75e4383d89 100644 --- a/Kernel/Net/Socket.cpp +++ b/Kernel/Net/Socket.cpp @@ -133,6 +133,17 @@ KResult Socket::setsockopt(int level, int option, Userspace user_va case SO_KEEPALIVE: // FIXME: Obviously, this is not a real keepalive. return KSuccess; + case SO_TIMESTAMP: + if (user_value_size != sizeof(int)) + return KResult(-EINVAL); + if (!copy_from_user(&m_timestamp, static_ptr_cast(user_value))) + return KResult(-EFAULT); + if (m_timestamp && (domain() != AF_INET || type() == SOCK_STREAM)) { + // FIXME: Support SO_TIMESTAMP for more protocols? + m_timestamp = 0; + return KResult(-ENOTSUP); + } + return KSuccess; default: dbg() << "setsockopt(" << option << ") at SOL_SOCKET not implemented."; return KResult(-ENOPROTOOPT); @@ -196,6 +207,15 @@ KResult Socket::getsockopt(FileDescription&, int level, int option, Userspace(value), &m_timestamp)) + return KResult(-EFAULT); + size = sizeof(int); + if (!copy_to_user(value_size, &size)) + return KResult(-EFAULT); + return KSuccess; default: dbg() << "getsockopt(" << option << ") at SOL_SOCKET not implemented."; return KResult(-ENOPROTOOPT); diff --git a/Kernel/Net/Socket.h b/Kernel/Net/Socket.h index 2b178a2ec9..4aa4a4920d 100644 --- a/Kernel/Net/Socket.h +++ b/Kernel/Net/Socket.h @@ -135,6 +135,8 @@ public: bool has_send_timeout() const { return m_send_timeout.tv_sec || m_send_timeout.tv_usec; } const timeval& send_timeout() const { return m_send_timeout; } + bool wants_timestamp() const { return m_timestamp; } + protected: Socket(int domain, int type, int protocol); @@ -172,6 +174,7 @@ private: timeval m_receive_timeout { 0, 0 }; timeval m_send_timeout { 0, 0 }; + int m_timestamp { 0 }; NonnullRefPtrVector m_pending; }; diff --git a/Kernel/Syscalls/socket.cpp b/Kernel/Syscalls/socket.cpp index 9d09d70e14..b54f6959e7 100644 --- a/Kernel/Syscalls/socket.cpp +++ b/Kernel/Syscalls/socket.cpp @@ -264,6 +264,23 @@ ssize_t Process::sys$recvmsg(int sockfd, Userspace user_msg, int msg_flags |= MSG_TRUNC; } + if (socket.wants_timestamp()) { + struct { + cmsghdr cmsg; + timeval timestamp; + } cmsg_timestamp; + socklen_t control_length = sizeof(cmsg_timestamp); + if (msg.msg_controllen < control_length) { + msg_flags |= MSG_CTRUNC; + } else { + cmsg_timestamp = { { control_length, SOL_SOCKET, SCM_TIMESTAMP }, timestamp }; + if (!copy_to_user(msg.msg_control, &cmsg_timestamp, control_length)) + return -EFAULT; + } + if (!copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_controllen, &control_length)) + return -EFAULT; + } + if (!copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_flags, &msg_flags)) return -EFAULT; diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index f6a7862532..f056d48304 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -458,6 +458,7 @@ struct pollfd { #define SHUT_RDWR 3 #define MSG_TRUNC 0x1 +#define MSG_CTRUNC 0x2 #define MSG_DONTWAIT 0x40 #define SOL_SOCKET 1 @@ -471,6 +472,11 @@ enum { SO_REUSEADDR, SO_BINDTODEVICE, SO_KEEPALIVE, + SO_TIMESTAMP, +}; + +enum { + SCM_TIMESTAMP, }; #define IPPROTO_IP 0 @@ -556,6 +562,12 @@ struct iovec { size_t iov_len; }; +struct cmsghdr { + socklen_t cmsg_len; + int cmsg_level; + int cmsg_type; +}; + struct msghdr { void* msg_name; socklen_t msg_namelen; diff --git a/Libraries/LibC/sys/socket.h b/Libraries/LibC/sys/socket.h index e63403c564..8a820e5213 100644 --- a/Libraries/LibC/sys/socket.h +++ b/Libraries/LibC/sys/socket.h @@ -62,10 +62,17 @@ __BEGIN_DECLS #define IPPROTO_UDP 17 #define MSG_TRUNC 0x1 +#define MSG_CTRUNC 0x2 #define MSG_DONTWAIT 0x40 typedef uint16_t sa_family_t; +struct cmsghdr { + socklen_t cmsg_len; + int cmsg_level; + int cmsg_type; +}; + struct msghdr { void* msg_name; socklen_t msg_namelen; @@ -99,6 +106,7 @@ enum { SO_REUSEADDR, SO_BINDTODEVICE, SO_KEEPALIVE, + SO_TIMESTAMP, }; #define SO_RCVTIMEO SO_RCVTIMEO #define SO_SNDTIMEO SO_SNDTIMEO @@ -108,6 +116,12 @@ enum { #define SO_REUSEADDR SO_REUSEADDR #define SO_BINDTODEVICE SO_BINDTODEVICE #define SO_KEEPALIVE SO_KEEPALIVE +#define SO_TIMESTAMP SO_TIMESTAMP + +enum { + SCM_TIMESTAMP, +}; +#define SCM_TIMESTAMP SCM_TIMESTAMP struct sockaddr_storage { union { @@ -135,4 +149,30 @@ int getpeername(int sockfd, struct sockaddr*, socklen_t*); int sendfd(int sockfd, int fd); int recvfd(int sockfd); +// These three are non-POSIX, but common: +#define CMSG_ALIGN(x) (((x) + sizeof(void*) - 1) & ~(sizeof(void*) - 1)) +#define CMSG_SPACE(x) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(x)) +#define CMSG_LEN(x) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (x)) + +static inline struct cmsghdr* CMSG_FIRSTHDR(struct msghdr* msg) +{ + if (msg->msg_controllen < sizeof(struct cmsghdr)) + return 0; + return (struct cmsghdr*)msg->msg_control; +} + +static inline struct cmsghdr* CMSG_NXTHDR(struct msghdr* msg, struct cmsghdr* cmsg) +{ + struct cmsghdr* next = (struct cmsghdr*)((char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len)); + unsigned offset = (char*)next - (char*)msg->msg_control; + if (msg->msg_controllen < offset + sizeof(struct cmsghdr)) + return NULL; + return next; +} + +static inline void* CMSG_DATA(struct cmsghdr* cmsg) +{ + return (void*)(cmsg + 1); +} + __END_DECLS