/// @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 #include #include "dpf/net/asio_ns.hpp" #if defined(__linux__) #include #include #include #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; sock.set_option(quickack(true), ec); #else (void)sock; #endif } namespace detail { template 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 struct is_layered_stream : std::false_type { }; template struct is_layered_stream> : std::true_type { }; } // namespace detail /// @brief The socket under `s`: itself, or the TCP socket under a TLS stream. template inline auto & base_socket(S & s) { if constexpr (detail::is_layered_stream::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 inline void tune_stream(Socket & sock, const socket_options & o = {}) { if constexpr (detail::is_layered_stream::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 inline void rearm_stream(Socket & sock, const socket_options & o) { if constexpr (detail::is_layered_stream::value) rearm_stream(base_socket(sock), o); else { (void)sock; (void)o; } } } // namespace net } // namespace dpf #endif