| // Copyright (c) 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef QUICHE_QUIC_QBONE_PLATFORM_KERNEL_INTERFACE_H_ |
| #define QUICHE_QUIC_QBONE_PLATFORM_KERNEL_INTERFACE_H_ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace quic { |
| |
| // A wrapper for making syscalls to the kernel, so that syscalls can be |
| // mocked during testing. |
| class KernelInterface { |
| public: |
| virtual ~KernelInterface() {} |
| virtual int bind(int fd, const struct sockaddr* addr, socklen_t addr_len) = 0; |
| virtual int close(int fd) = 0; |
| virtual int ioctl(int fd, int request, void* argp) = 0; |
| virtual int open(const char* pathname, int flags) = 0; |
| virtual ssize_t read(int fd, void* buf, size_t count) = 0; |
| virtual ssize_t recvfrom(int sockfd, |
| void* buf, |
| size_t len, |
| int flags, |
| struct sockaddr* src_addr, |
| socklen_t* addrlen) = 0; |
| virtual ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) = 0; |
| virtual ssize_t sendto(int sockfd, |
| const void* buf, |
| size_t len, |
| int flags, |
| const struct sockaddr* dest_addr, |
| socklen_t addrlen) = 0; |
| virtual int socket(int domain, int type, int protocol) = 0; |
| virtual int setsockopt(int fd, |
| int level, |
| int optname, |
| const void* optval, |
| socklen_t optlen) = 0; |
| virtual ssize_t write(int fd, const void* buf, size_t count) = 0; |
| }; |
| |
| // It is unfortunate to have R here, but std::result_of cannot be used. |
| template <typename F, typename R, typename... Params> |
| auto SyscallRetryOnError(R r, F f, Params&&... params) |
| -> decltype(f(std::forward<Params>(params)...)) { |
| static_assert( |
| std::is_same<decltype(f(std::forward<Params>(params)...)), R>::value, |
| "Return type does not match"); |
| decltype(f(std::forward<Params>(params)...)) result; |
| do { |
| result = f(std::forward<Params>(params)...); |
| } while (result == r && errno == EINTR); |
| return result; |
| } |
| |
| template <typename F, typename... Params> |
| auto SyscallRetry(F f, Params&&... params) |
| -> decltype(f(std::forward<Params>(params)...)) { |
| return SyscallRetryOnError(-1, f, std::forward<Params>(params)...); |
| } |
| |
| template <typename Runner> |
| class ParametrizedKernel final : public KernelInterface { |
| public: |
| static_assert(std::is_trivially_destructible<Runner>::value, |
| "Runner is used as static, must be trivially destructible"); |
| |
| ~ParametrizedKernel() override {} |
| |
| int bind(int fd, const struct sockaddr* addr, socklen_t addr_len) override { |
| static Runner syscall("bind"); |
| return syscall.Retry(&::bind, fd, addr, addr_len); |
| } |
| int close(int fd) override { |
| static Runner syscall("close"); |
| return syscall.Retry(&::close, fd); |
| } |
| int ioctl(int fd, int request, void* argp) override { |
| static Runner syscall("ioctl"); |
| return syscall.Retry(&::ioctl, fd, request, argp); |
| } |
| int open(const char* pathname, int flags) override { |
| static Runner syscall("open"); |
| return syscall.Retry(&::open, pathname, flags); |
| } |
| ssize_t read(int fd, void* buf, size_t count) override { |
| static Runner syscall("read"); |
| return syscall.Run(&::read, fd, buf, count); |
| } |
| ssize_t recvfrom(int sockfd, |
| void* buf, |
| size_t len, |
| int flags, |
| struct sockaddr* src_addr, |
| socklen_t* addrlen) override { |
| static Runner syscall("recvfrom"); |
| return syscall.RetryOnError(&::recvfrom, static_cast<ssize_t>(-1), sockfd, |
| buf, len, flags, src_addr, addrlen); |
| } |
| ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) override { |
| static Runner syscall("sendmsg"); |
| return syscall.RetryOnError(&::sendmsg, static_cast<ssize_t>(-1), sockfd, |
| msg, flags); |
| } |
| ssize_t sendto(int sockfd, |
| const void* buf, |
| size_t len, |
| int flags, |
| const struct sockaddr* dest_addr, |
| socklen_t addrlen) override { |
| static Runner syscall("sendto"); |
| return syscall.RetryOnError(&::sendto, static_cast<ssize_t>(-1), sockfd, |
| buf, len, flags, dest_addr, addrlen); |
| } |
| int socket(int domain, int type, int protocol) override { |
| static Runner syscall("socket"); |
| return syscall.Retry(&::socket, domain, type, protocol); |
| } |
| int setsockopt(int fd, |
| int level, |
| int optname, |
| const void* optval, |
| socklen_t optlen) override { |
| static Runner syscall("setsockopt"); |
| return syscall.Retry(&::setsockopt, fd, level, optname, optval, optlen); |
| } |
| ssize_t write(int fd, const void* buf, size_t count) override { |
| static Runner syscall("write"); |
| return syscall.Run(&::write, fd, buf, count); |
| } |
| }; |
| |
| class DefaultKernelRunner { |
| public: |
| explicit DefaultKernelRunner(const char* name) {} |
| |
| template <typename F, typename R, typename... Params> |
| static auto RetryOnError(F f, R r, Params&&... params) |
| -> decltype(f(std::forward<Params>(params)...)) { |
| return SyscallRetryOnError(r, f, std::forward<Params>(params)...); |
| } |
| |
| template <typename F, typename... Params> |
| static auto Retry(F f, Params&&... params) |
| -> decltype(f(std::forward<Params>(params)...)) { |
| return SyscallRetry(f, std::forward<Params>(params)...); |
| } |
| |
| template <typename F, typename... Params> |
| static auto Run(F f, Params&&... params) |
| -> decltype(f(std::forward<Params>(params)...)) { |
| return f(std::forward<Params>(params)...); |
| } |
| }; |
| |
| using Kernel = ParametrizedKernel<DefaultKernelRunner>; |
| |
| } // namespace quic |
| |
| #endif // QUICHE_QUIC_QBONE_PLATFORM_KERNEL_INTERFACE_H_ |