/// @file dpf/net/link_log.hpp /// @brief Run-log records for listeners and established party links. /// @details `log_link_up` is called once per socket after the handshake and /// after the stream array has adopted and tuned it, so the socket /// options it reads back are the ones the kernel applied (Linux /// doubles `SO_SNDBUF`/`SO_RCVBUF` and clamps them to `wmem_max` and /// `rmem_max`). `TCP_INFO` at that point carries the kernel's RTT /// estimate from the connection setup and the handshake exchange. /// Encrypted links record the TLS version and cipher, how this side /// authenticated the peer (`auth=key` or `none`), the peer's key, and /// whether the peer authenticated this side. With encryption off the /// record says `auth=none encryption=none`, and the first plaintext /// link to an address off this host also raises one warning. #ifndef LIBDPF_INCLUDE_DPF_NET_LINK_LOG_HPP__ #define LIBDPF_INCLUDE_DPF_NET_LINK_LOG_HPP__ #include #include #include #include #include #include #include #include #include #include "dpf/log.hpp" #include "dpf/net/policy.hpp" #include "dpf/net/security.hpp" namespace dpf { namespace net { namespace detail { inline std::string sockaddr_text(const sockaddr_storage & ss) { char host[INET6_ADDRSTRLEN] = {}; if (ss.ss_family == AF_INET) { const auto & a = reinterpret_cast(ss); if (::inet_ntop(AF_INET, &a.sin_addr, host, sizeof(host)) == nullptr) return "unknown"; return std::string(host) + ":" + std::to_string(ntohs(a.sin_port)); } if (ss.ss_family == AF_INET6) { const auto & a = reinterpret_cast(ss); if (::inet_ntop(AF_INET6, &a.sin6_addr, host, sizeof(host)) == nullptr) return "unknown"; return "[" + std::string(host) + "]:" + std::to_string(ntohs(a.sin6_port)); } if (ss.ss_family == AF_UNIX) { const auto & a = reinterpret_cast(ss); return std::string("unix:") + (a.sun_path[0] != '\0' ? a.sun_path : "(unnamed)"); } return "unknown"; } inline bool loopback(const sockaddr_storage & ss) { if (ss.ss_family == AF_INET) return (ntohl(reinterpret_cast(ss).sin_addr.s_addr) >> 24) == 127u; if (ss.ss_family == AF_INET6) { const auto & a = reinterpret_cast(ss).sin6_addr; if (IN6_IS_ADDR_LOOPBACK(&a)) return true; return IN6_IS_ADDR_V4MAPPED(&a) && a.s6_addr[12] == 127; } return ss.ss_family == AF_UNIX; } inline int int_opt(int fd, int level, int name) { int v = -1; socklen_t len = sizeof(v); if (::getsockopt(fd, level, name, &v, &len) != 0) return -1; return v; } } // namespace detail /// @brief Record a listener: this process accepts any address on `port`. /// @param encrypted whether connections on it must complete TLS 1.3 first inline void log_listen(unsigned short port, bool sctp, bool encrypted = false) { DPF_LOG(info, "listen").kv("addr", "0.0.0.0").kv("port", port) .kv("tcp", true).kv("sctp", sctp) .kv("encryption", encrypted ? "tls1.3" : "none"); } /// @brief Record one established socket of a party link. /// @param how `accept` or `connect` /// @param peer the other end's role (`p1`, `dealer`, ...) /// @param lane which socket of a `parallel` link (0 otherwise) /// @param sec how the link was secured (null or unencrypted: plaintext) inline void log_link_up(const char * how, const std::string & peer, transport kind, std::size_t lanes, std::uint32_t lane, std::uint32_t epoch, int fd, const socket_options & requested, const link_security * sec = nullptr) { const bool encrypted = sec != nullptr && sec->encrypted; if (!log::enabled(log::level::info) || fd < 0) return; sockaddr_storage local{}; sockaddr_storage remote{}; socklen_t local_len = sizeof(local); socklen_t remote_len = sizeof(remote); const bool have_local = ::getsockname(fd, reinterpret_cast(&local), &local_len) == 0; const bool have_remote = ::getpeername(fd, reinterpret_cast(&remote), &remote_len) == 0; { log::record rec(log::level::info, "link.up"); rec.kv("how", how).kv("peer", peer).kv("transport", transport_name(kind)) .kv("lanes", lanes).kv("lane", lane).kv("epoch", epoch) .kv("local", have_local ? detail::sockaddr_text(local) : std::string("unknown")) .kv("remote", have_remote ? detail::sockaddr_text(remote) : std::string("unknown")) .kv("auth", encrypted ? sec->peer_auth : std::string("none")) .kv("encryption", encrypted ? sec->protocol + "/" + sec->cipher : std::string("none")); if (encrypted) rec.kv("peer_key", sec->peer_key ? sec->peer_key->base64() : std::string("none")) .kv("peer_verified_us", sec->peer_verified_us); if (kind != transport::sctp) { rec.kv("nodelay", detail::int_opt(fd, IPPROTO_TCP, TCP_NODELAY)) .kv("quickack_req", requested.quickack) .kv("keepalive", detail::int_opt(fd, SOL_SOCKET, SO_KEEPALIVE)) .kv("sndbuf_req", requested.send_buffer) .kv("sndbuf", detail::int_opt(fd, SOL_SOCKET, SO_SNDBUF)) .kv("rcvbuf_req", requested.recv_buffer) .kv("rcvbuf", detail::int_opt(fd, SOL_SOCKET, SO_RCVBUF)); #if defined(TCP_INFO) tcp_info ti{}; socklen_t ti_len = sizeof(ti); if (::getsockopt(fd, IPPROTO_TCP, TCP_INFO, &ti, &ti_len) == 0) rec.kv("rtt_us", ti.tcpi_rtt).kv("rttvar_us", ti.tcpi_rttvar) .kv("pmtu", ti.tcpi_pmtu).kv("snd_mss", ti.tcpi_snd_mss) .kv("snd_cwnd", ti.tcpi_snd_cwnd) .kv("retrans", ti.tcpi_total_retrans); #endif } } if (!encrypted && have_remote && !detail::loopback(remote) && log::first_time("net.plaintext_remote")) DPF_LOG(warning, "link.plaintext").kv("remote", detail::sockaddr_text(remote)) .kv("detail", "encryption is off: this party link is unauthenticated " "and unencrypted, and the handshake's party id is not verified"); } } // namespace net } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_NET_LINK_LOG_HPP__