blob: c96b6e67d0d5d70d7faf11c1f0555edf6e2edc97 [file] [log] [blame]
// 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_