136 lines
3.3 KiB
C++
136 lines
3.3 KiB
C++
|
|
/// @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
|