]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
common: Add utility to fetch TCP_INFO for socket
authorMarcel Lauhoff <marcel.lauhoff@clyso.com>
Wed, 21 Aug 2024 13:38:30 +0000 (15:38 +0200)
committerMarcel Lauhoff <marcel.lauhoff@clyso.com>
Mon, 17 Feb 2025 17:41:18 +0000 (18:41 +0100)
Add utility to fetch TCP_INFO for a socket and a variant that pushes
the data to a Formatter.

Signed-off-by: Marcel Lauhoff <marcel.lauhoff@clyso.com>
src/common/CMakeLists.txt
src/common/tcp_info.cc [new file with mode: 0644]
src/common/tcp_info.h [new file with mode: 0644]

index c607839a8d2598020ad66ec2de21e481555ec9c3..ae34d330813f36b92f71831bd6a39f4f0463d7f3 100644 (file)
@@ -104,7 +104,8 @@ set(common_srcs
   pretty_binary.cc
   utf8.c
   util.cc
-  version.cc)
+  version.cc
+  tcp_info.cc)
 
 if(WITH_SYSTEMD)
   list(APPEND common_srcs
diff --git a/src/common/tcp_info.cc b/src/common/tcp_info.cc
new file mode 100644 (file)
index 0000000..c0a0f65
--- /dev/null
@@ -0,0 +1,121 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2024 Clyso GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#include "common/tcp_info.h"
+
+#include "common/Formatter.h"
+
+namespace ceph {
+
+#ifdef _WIN32
+struct tcp_info {};
+
+bool tcp_info(int fd, struct tcp_info& info) {
+  return false;
+}
+bool dump_tcp_info(int fd, Formatter* f) {
+  return false;
+}
+
+#else
+
+bool tcp_info(int fd, struct tcp_info& info) {
+  socklen_t info_len = sizeof(info);
+  return (getsockopt(fd, SOL_TCP, TCP_INFO, &info, &info_len) == 0);
+}
+
+static const char* get_tcpi_state_name(uint8_t state) {
+  switch (state) {
+    case TCP_ESTABLISHED:
+      return "established";
+    case TCP_SYN_SENT:
+      return "syn sent";
+    case TCP_SYN_RECV:
+      return "syn recv";
+    case TCP_FIN_WAIT1:
+      return "fin wait1";
+    case TCP_FIN_WAIT2:
+      return "fin wait2";
+    case TCP_TIME_WAIT:
+      return "time wait";
+    case TCP_CLOSE:
+      return "close";
+    case TCP_CLOSE_WAIT:
+      return "close wait";
+    case TCP_LAST_ACK:
+      return "last ack";
+    case TCP_LISTEN:
+      return "listen";
+    case TCP_CLOSING:
+      return "closing";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+bool dump_tcp_info(int fd, Formatter* f) {
+  struct tcp_info info;
+  if (!tcp_info(fd, info)) {
+    return false;
+  }
+
+  f->open_object_section("tcp_info");
+  f->dump_string("tcpi_state", get_tcpi_state_name(info.tcpi_state));
+  f->dump_unsigned("tcpi_retransmits", info.tcpi_retransmits);
+  f->dump_unsigned("tcpi_probes", info.tcpi_probes);
+  f->dump_unsigned("tcpi_backoff", info.tcpi_backoff);
+  f->dump_unsigned("tcpi_rto_us", info.tcpi_rto);
+  f->dump_unsigned("tcpi_ato_us", info.tcpi_ato);
+  f->dump_unsigned("tcpi_snd_mss", info.tcpi_snd_mss);
+  f->dump_unsigned("tcpi_rcv_mss", info.tcpi_rcv_mss);
+  f->dump_unsigned("tcpi_unacked", info.tcpi_unacked);
+  f->dump_unsigned("tcpi_lost", info.tcpi_lost);
+  f->dump_unsigned("tcpi_retrans", info.tcpi_retrans);
+  f->dump_unsigned("tcpi_pmtu", info.tcpi_pmtu);
+  f->dump_unsigned("tcpi_rtt_us", info.tcpi_rtt);
+  f->dump_unsigned("tcpi_rttvar_us", info.tcpi_rttvar);
+  f->dump_unsigned("tcpi_total_retrans", info.tcpi_total_retrans);
+  f->dump_unsigned("tcpi_last_data_sent_ms", info.tcpi_last_data_sent);
+  f->dump_unsigned("tcpi_last_ack_sent_ms", info.tcpi_last_ack_sent);
+  f->dump_unsigned("tcpi_last_data_recv_ms", info.tcpi_last_data_recv);
+  f->dump_unsigned("tcpi_last_ack_recv_ms", info.tcpi_last_ack_recv);
+
+  f->open_array_section("tcpi_options");
+  if (info.tcpi_options & TCPI_OPT_TIMESTAMPS) {
+    f->dump_string("option", "timestamps");
+  }
+  if (info.tcpi_options & TCPI_OPT_SACK) {
+    f->dump_string("option", "sack");
+  }
+  if (info.tcpi_options & TCPI_OPT_WSCALE) {
+    f->dump_string("option", "wscale");
+  }
+  if (info.tcpi_options & TCPI_OPT_ECN) {
+    f->dump_string("option", "ecn");
+  }
+  if (info.tcpi_options & TCPI_OPT_ECN_SEEN) {
+    f->dump_string("option", "ecn seen");
+  }
+  if (info.tcpi_options & TCPI_OPT_SYN_DATA) {
+    f->dump_string("option", "syn data");
+  }
+  f->close_section();
+
+  f->close_section();
+  return true;
+}
+
+#endif
+
+}  // namespace ceph
diff --git a/src/common/tcp_info.h b/src/common/tcp_info.h
new file mode 100644 (file)
index 0000000..d911289
--- /dev/null
@@ -0,0 +1,30 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2024 Clyso GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#pragma once
+
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+
+#include "Formatter.h"
+
+namespace ceph {
+
+/// Return TCP_INFO socket stats (see tcp(7)). Return true on success.
+bool tcp_info(int fd, struct tcp_info& info);
+/// Dump TCP_INFO socket stats to formatter. Use struct tcp_info variables
+/// names as keys. Returns true on success.
+bool dump_tcp_info(int fd, Formatter* f);
+
+}  // namespace ceph