[PATCH 5/5] Add a escape key to show packet statistics.

Sebastian Andrzej Siewior
2020-09-05T15:50:05Z

The raw/compressed bytes sent/received are only shown with the verbose
option enabled once the connection is closed.
Add a escape key `S' which shows the statistics on demand.
The statistics is extended by the amount of bytes sent/recevied which
includes cryptographic overhead (like MAC and block size).

Signed-off-by: Sebastian Andrzej Siewior <sebastian at breakpoint.cc>
---
 clientloop.c | 13 +++++++-
 packet.c     | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 packet.h     |  1 +
 3 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/clientloop.c b/clientloop.c
index c82b084da1bb0..7a62af9db4907 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -897,6 +897,7 @@ static struct escape_help_text esc_txt[] = {
     {"B",  "send a BREAK to the remote system",
SUPPRESS_NEVER},
     {"C",  "open a command line", SUPPRESS_MUXCLIENT},
     {"R",  "request rekey", SUPPRESS_NEVER},
+    {"S",  "Show statistics", SUPPRESS_NEVER},
     {"V/v",  "decrease/increase verbosity (LogLevel)",
SUPPRESS_MUXCLIENT},
     {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT},
     {"#",  "list forwarded connections", SUPPRESS_NEVER},
@@ -1039,7 +1040,17 @@ process_escapes(struct ssh *ssh, Channel *c,
 				else
 					need_rekeying = 1;
 				continue;
-
+			case 'S':
+				if ((r = sshbuf_putf(berr, "%c#\r\n",
+				    efc->escape_char)) != 0)
+					fatal("%s: buffer error: %s",
+					    __func__, ssh_err(r));
+				s = sshpkt_get_stats(ssh);
+				if ((r = sshbuf_put(berr, s, strlen(s))) != 0)
+					fatal("%s: buffer error: %s",
+					    __func__, ssh_err(r));
+				free(s);
+				continue;
 			case 'V':
 				/* FALLTHROUGH */
 			case 'v':
diff --git a/packet.c b/packet.c
index d49c9594cf60d..eef82766425f8 100644
--- a/packet.c
+++ b/packet.c
@@ -690,6 +690,91 @@ ssh_packet_close_internal(struct ssh *ssh, int do_close)
 	}
 }

+char *
+sshpkt_get_stats(struct ssh *ssh)
+{
+	struct session_state *state = ssh->state;
+	u_int64_t ibytes, obytes;
+	struct sshbuf *buf;
+	char *ret;
+	int r;
+
+	if ((buf = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new", __func__);
+
+	if ((r = sshbuf_putf(buf, "Connection statistics ")) != 0)
+		fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+
+	if (state->compression_buffer) {
+#ifdef WITH_ZLIB
+		if (state->compression_out_started == COMP_ZLIB) {
+			if ((r = sshbuf_putf(buf, "zlib compressed:\r\n")) != 0)
+				fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+			z_streamp stream = &state->compression_out_stream;
+			r = sshbuf_putf(buf, "compress outgoing: "
+			    "raw data %llu, compressed %llu, factor %.2f\r\n",
+				(unsigned long long)stream->total_in,
+				(unsigned long long)stream->total_out,
+				stream->total_in == 0 ? 0.0 :
+				(double) stream->total_out / stream->total_in);
+			if (r)
+				fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+		}
+		if (state->compression_in_started == COMP_ZLIB) {
+			z_streamp stream = &state->compression_in_stream;
+			r = sshbuf_putf(buf, "compress incoming: "
+			    "raw data %llu, compressed %llu, factor %.2f\r\n",
+			    (unsigned long long)stream->total_out,
+			    (unsigned long long)stream->total_in,
+			    stream->total_out == 0 ? 0.0 :
+			    (double) stream->total_in / stream->total_out);
+			if (r)
+				fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+		}
+#endif /* WITH_ZLIB */
+#ifdef HAVE_LIBZSTD
+		if (state->compression_out_started == COMP_ZSTD) {
+			if ((r = sshbuf_putf(buf, "zstd compressed:\r\n")) != 0)
+				fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+			r = sshbuf_putf(buf, "compress outgoing: "
+			      "raw data %llu, compressed %llu, factor %.2f\r\n",
+			      (unsigned long long)state->compress_zstd_out_raw,
+			      (unsigned long long)state->compress_zstd_out_comp,
+			      state->compress_zstd_out_raw == 0 ? 0.0 :
+			      (double) state->compress_zstd_out_comp /
+			      state->compress_zstd_out_raw);
+			if (r)
+				fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+		}
+		if (state->compression_in_started == COMP_ZSTD) {
+			r = sshbuf_putf(buf, "compress incoming: "
+			      "raw data %llu, compressed %llu, factor %.2f\r\n",
+			      (unsigned long long)state->compress_zstd_in_raw,
+			      (unsigned long long)state->compress_zstd_in_comp,
+			      state->compress_zstd_in_raw == 0 ? 0.0 :
+			      (double) state->compress_zstd_in_comp /
+			      state->compress_zstd_in_raw);
+			if (r)
+				fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+		}
+#endif /* HAVE_LIBZSTD */
+	} else {
+
+		if ((r = sshbuf_putf(buf, "not compressed:\r\n")) != 0)
+			fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+	}
+	ssh_packet_get_bytes(ssh, &ibytes, &obytes);
+	r = sshbuf_putf(buf, "Total sent %llu, received %llu bytes.\r\n",
+			(unsigned long long)obytes, (unsigned long long)ibytes);
+	if (r)
+		fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+
+	if ((ret = sshbuf_dup_string(buf)) == NULL)
+		fatal("%s: sshbuf_dup_string", __func__);
+	sshbuf_free(buf);
+	return ret;
+}
+
 void
 ssh_packet_close(struct ssh *ssh)
 {
diff --git a/packet.h b/packet.h
index c2544bd966078..9487ba9c21a54 100644
--- a/packet.h
+++ b/packet.h
@@ -171,6 +171,7 @@ void	*ssh_packet_get_input(struct ssh *);
 void	*ssh_packet_get_output(struct ssh *);

 /* new API */
+char   *sshpkt_get_stats(struct ssh *ssh);
 int	sshpkt_start(struct ssh *ssh, u_char type);
 int	sshpkt_send(struct ssh *ssh);
 int     sshpkt_disconnect(struct ssh *, const char *fmt, ...)
--
2.28.0