libdpf/include/dpf/net/socket_tune.hpp

136 lines
3.3 KiB
C++
Raw Permalink Normal View History

/// @file dpf/net/socket_tune.hpp
/// @brief Apply `socket_options` to TCP and unix-domain sockets.
#ifndef LIBDPF_INCLUDE_DPF_NET_SOCKET_TUNE_HPP__
#define LIBDPF_INCLUDE_DPF_NET_SOCKET_TUNE_HPP__
#include <system_error>
#include <type_traits>
#include "dpf/net/asio_ns.hpp"
#if defined(__linux__)
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#endif
#include "dpf/net/policy.hpp"
namespace dpf
{
namespace net
{
/// @brief Linux clears `TCP_QUICKACK` after one ACK. Call again after I/O.
inline void rearm_quickack(asio::ip::tcp::socket & sock)
{
#if defined(TCP_QUICKACK)
std::error_code ec;
using quickack =
asio::detail::socket_option::boolean<IPPROTO_TCP, TCP_QUICKACK>;
sock.set_option(quickack(true), ec);
#else
(void)sock;
#endif
}
namespace detail
{
template <typename Socket>
inline void apply_buffers(Socket & sock, const socket_options & o)
{
std::error_code ec;
if (o.send_buffer > 0)
sock.set_option(asio::socket_base::send_buffer_size(o.send_buffer), ec);
if (o.recv_buffer > 0)
sock.set_option(asio::socket_base::receive_buffer_size(o.recv_buffer),
ec);
}
} // namespace detail
inline void tune_tcp(asio::ip::tcp::socket & sock, const socket_options & o = {})
{
std::error_code ec;
sock.set_option(asio::ip::tcp::no_delay(o.no_delay), ec);
sock.set_option(asio::socket_base::keep_alive(o.keepalive), ec);
#if defined(__linux__)
const int fd = sock.native_handle();
if (o.keepalive && o.keepalive_idle_s > 0)
(void)::setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &o.keepalive_idle_s,
sizeof(o.keepalive_idle_s));
if (o.keepalive && o.keepalive_interval_s > 0)
(void)::setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
&o.keepalive_interval_s, sizeof(o.keepalive_interval_s));
if (o.keepalive && o.keepalive_count > 0)
(void)::setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &o.keepalive_count,
sizeof(o.keepalive_count));
#endif
detail::apply_buffers(sock, o);
if (o.quickack)
rearm_quickack(sock);
}
namespace detail
{
template <typename S, typename = void>
struct is_layered_stream : std::false_type
{
};
template <typename S>
struct is_layered_stream<S, std::void_t<typename S::next_layer_type>> : std::true_type
{
};
} // namespace detail
/// @brief The socket under `s`: itself, or the TCP socket under a TLS stream.
template <typename S>
inline auto & base_socket(S & s)
{
if constexpr (detail::is_layered_stream<S>::value)
return base_socket(s.next_layer());
else
return s;
}
inline void tune_stream(asio::ip::tcp::socket & sock, const socket_options & o = {})
{
tune_tcp(sock, o);
}
template <typename Socket>
inline void tune_stream(Socket & sock, const socket_options & o = {})
{
if constexpr (detail::is_layered_stream<Socket>::value)
tune_stream(base_socket(sock), o);
else
detail::apply_buffers(sock, o);
}
inline void rearm_stream(asio::ip::tcp::socket & sock, const socket_options & o)
{
if (o.quickack)
rearm_quickack(sock);
}
template <typename Socket>
inline void rearm_stream(Socket & sock, const socket_options & o)
{
if constexpr (detail::is_layered_stream<Socket>::value)
rearm_stream(base_socket(sock), o);
else
{
(void)sock;
(void)o;
}
}
} // namespace net
} // namespace dpf
#endif