summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nakamura <upa@haeena.net>2023-09-09 14:32:15 +0900
committerRyo Nakamura <upa@haeena.net>2023-11-01 19:54:18 +0900
commitbf74aa095adf8d0a3b4144b29b2d7a48fe4e02b2 (patch)
treed3d58b31457af827cecf439b902e14b62d41c129
parenta88471fc43a8500f73cd55eeb87b3a990cb0b4f9 (diff)
add -g option to specify TCP cc algorithm
This commit introduce SSH_OPTIONS_CCALGO option to the libssh patch and add -g CONGESTION option to mscp.
-rw-r--r--include/mscp.h2
-rw-r--r--patch/libssh-0.10.4.patch151
-rw-r--r--src/main.c14
-rw-r--r--src/pymscp.c8
-rw-r--r--src/ssh.c6
-rw-r--r--test/test_e2e.py16
-rw-r--r--test/test_python.py1
7 files changed, 191 insertions, 7 deletions
diff --git a/include/mscp.h b/include/mscp.h
index 7062c48..35af518 100644
--- a/include/mscp.h
+++ b/include/mscp.h
@@ -56,6 +56,7 @@ struct mscp_opts {
#define MSCP_SSH_MAX_CIPHER_STR 32
#define MSCP_SSH_MAX_HMAC_STR 32
#define MSCP_SSH_MAX_COMP_STR 32 /* yes, no, zlib, zlib@openssh.com, none */
+#define MSCP_SSH_MAX_CCALGO_STR 16
#define MSCP_SSH_MAX_PASSWORD 128
#define MSCP_SSH_MAX_PASSPHRASE 128
@@ -72,6 +73,7 @@ struct mscp_ssh_opts {
char cipher[MSCP_SSH_MAX_CIPHER_STR]; /** cipher spec */
char hmac[MSCP_SSH_MAX_HMAC_STR]; /** hmacp spec */
char compress[MSCP_SSH_MAX_COMP_STR]; /** yes, no, zlib@openssh.com */
+ char ccalgo[MSCP_SSH_MAX_CCALGO_STR]; /** TCP cc algorithm */
char password[MSCP_SSH_MAX_PASSWORD]; /** password auth passowrd */
char passphrase[MSCP_SSH_MAX_PASSPHRASE]; /** passphrase for private key */
diff --git a/patch/libssh-0.10.4.patch b/patch/libssh-0.10.4.patch
index a8a9628..432cad0 100644
--- a/patch/libssh-0.10.4.patch
+++ b/patch/libssh-0.10.4.patch
@@ -1,3 +1,28 @@
+diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
+index 7103f303..c64eb39d 100644
+--- a/ConfigureChecks.cmake
++++ b/ConfigureChecks.cmake
+@@ -258,6 +258,7 @@ if (UNIX)
+ check_library_exists(util forkpty "" HAVE_LIBUTIL)
+ check_function_exists(cfmakeraw HAVE_CFMAKERAW)
+ check_function_exists(__strtoull HAVE___STRTOULL)
++ check_symbol_exists(TCP_CONGESTION "netinet/tcp.h" HAVE_TCP_CONGESTION)
+ endif (UNIX)
+
+ set(LIBSSH_REQUIRED_LIBRARIES ${_REQUIRED_LIBRARIES} CACHE INTERNAL "libssh required system libraries")
+diff --git a/config.h.cmake b/config.h.cmake
+index 1357615b..1e915ead 100644
+--- a/config.h.cmake
++++ b/config.h.cmake
+@@ -237,6 +237,8 @@
+
+ #cmakedefine HAVE_GCC_BOUNDED_ATTRIBUTE 1
+
++#cmakedefine HAVE_TCP_CONGESTION 1
++
+ /* Define to 1 if you want to enable GSSAPI */
+ #cmakedefine WITH_GSSAPI 1
+
diff --git a/include/libssh/buffer.h b/include/libssh/buffer.h
index a55a1b40..e34e075c 100644
--- a/include/libssh/buffer.h
@@ -12,10 +37,18 @@ index a55a1b40..e34e075c 100644
int ssh_buffer_validate_length(struct ssh_buffer_struct *buffer, size_t len);
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
-index 7857a77b..3eef7a16 100644
+index 7857a77b..6b4d481c 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
-@@ -833,6 +833,7 @@ LIBSSH_API const char* ssh_get_hmac_in(ssh_session session);
+@@ -402,6 +402,7 @@ enum ssh_options_e {
+ SSH_OPTIONS_GSSAPI_AUTH,
+ SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
+ SSH_OPTIONS_NODELAY,
++ SSH_OPTIONS_CCALGO,
+ SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+ SSH_OPTIONS_PROCESS_CONFIG,
+ SSH_OPTIONS_REKEY_DATA,
+@@ -833,6 +834,7 @@ LIBSSH_API const char* ssh_get_hmac_in(ssh_session session);
LIBSSH_API const char* ssh_get_hmac_out(ssh_session session);
LIBSSH_API ssh_buffer ssh_buffer_new(void);
@@ -23,7 +56,7 @@ index 7857a77b..3eef7a16 100644
LIBSSH_API void ssh_buffer_free(ssh_buffer buffer);
#define SSH_BUFFER_FREE(x) \
do { if ((x) != NULL) { ssh_buffer_free(x); x = NULL; } } while(0)
-@@ -843,6 +844,8 @@ LIBSSH_API void *ssh_buffer_get(ssh_buffer buffer);
+@@ -843,6 +845,8 @@ LIBSSH_API void *ssh_buffer_get(ssh_buffer buffer);
LIBSSH_API uint32_t ssh_buffer_get_len(ssh_buffer buffer);
LIBSSH_API int ssh_session_set_disconnect_message(ssh_session session, const char *message);
@@ -32,6 +65,18 @@ index 7857a77b..3eef7a16 100644
#ifndef LIBSSH_LEGACY_0_4
#include "libssh/legacy.h"
#endif
+diff --git a/include/libssh/session.h b/include/libssh/session.h
+index d3e5787c..15183d1b 100644
+--- a/include/libssh/session.h
++++ b/include/libssh/session.h
+@@ -232,6 +232,7 @@ struct ssh_session_struct {
+ int gss_delegate_creds;
+ int flags;
+ int nodelay;
++ char *ccalgo;
+ bool config_processed;
+ uint8_t options_seen[SOC_MAX];
+ uint64_t rekey_data;
diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h
index c855df8a..0fcdb9b8 100644
--- a/include/libssh/sftp.h
@@ -158,6 +203,106 @@ index e0068015..cc0caf35 100644
/**
* @brief Ensure the buffer has at least a certain preallocated size.
*
+diff --git a/src/connect.c b/src/connect.c
+index 57e37e63..c02397d5 100644
+--- a/src/connect.c
++++ b/src/connect.c
+@@ -156,6 +156,20 @@ static int set_tcp_nodelay(socket_t socket)
+ sizeof(opt));
+ }
+
++static int set_tcp_ccalgo(socket_t socket, const char *ccalgo)
++{
++#ifdef HAVE_TCP_CONGESTION
++ return setsockopt(socket,
++ IPPROTO_TCP,
++ TCP_CONGESTION,
++ (void *)ccalgo,
++ strlen(ccalgo));
++#else
++ errno = ENOTSUP;
++ return -1;
++#endif
++}
++
+ /**
+ * @internal
+ *
+@@ -256,6 +270,18 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
+ }
+ }
+
++ if (session->opts.ccalgo) {
++ rc = set_tcp_ccalgo(s, session->opts.ccalgo);
++ if (rc < 0) {
++ ssh_set_error(session, SSH_FATAL,
++ "Failed to set TCP_CONGESTION on socket: %s",
++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
++ ssh_connect_socket_close(s);
++ s = -1;
++ continue;
++ }
++ }
++
+ errno = 0;
+ rc = connect(s, itr->ai_addr, itr->ai_addrlen);
+ if (rc == -1 && (errno != 0) && (errno != EINPROGRESS)) {
+diff --git a/src/options.c b/src/options.c
+index 49aaefa2..9f7360c3 100644
+--- a/src/options.c
++++ b/src/options.c
+@@ -210,6 +210,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
+ new->opts.gss_delegate_creds = src->opts.gss_delegate_creds;
+ new->opts.flags = src->opts.flags;
+ new->opts.nodelay = src->opts.nodelay;
++ new->opts.ccalgo = src->opts.ccalgo;
+ new->opts.config_processed = src->opts.config_processed;
+ new->common.log_verbosity = src->common.log_verbosity;
+ new->common.callbacks = src->common.callbacks;
+@@ -450,6 +451,10 @@ int ssh_options_set_algo(ssh_session session,
+ * Set it to disable Nagle's Algorithm (TCP_NODELAY) on the
+ * session socket. (int, 0=false)
+ *
++ * - SSH_OPTIONS_CCALGO
++ * Set it to specify TCP congestion control algorithm on the
++ * session socket (Linux only). (int, 0=false)
++ *
+ * - SSH_OPTIONS_PROCESS_CONFIG
+ * Set it to false to disable automatic processing of per-user
+ * and system-wide OpenSSH configuration files. LibSSH
+@@ -1013,6 +1018,20 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
+ session->opts.nodelay = (*x & 0xff) > 0 ? 1 : 0;
+ }
+ break;
++ case SSH_OPTIONS_CCALGO:
++ v = value;
++ if (v == NULL || v[0] == '\0') {
++ ssh_set_error_invalid(session);
++ return -1;
++ } else {
++ SAFE_FREE(session->opts.ccalgo);
++ session->opts.ccalgo = strdup(v);
++ if (session->opts.ccalgo == NULL) {
++ ssh_set_error_oom(session);
++ return -1;
++ }
++ }
++ break;
+ case SSH_OPTIONS_PROCESS_CONFIG:
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+diff --git a/src/session.c b/src/session.c
+index 6025c133..6b197526 100644
+--- a/src/session.c
++++ b/src/session.c
+@@ -108,6 +108,7 @@ ssh_session ssh_new(void)
+ session->opts.fd = -1;
+ session->opts.compressionlevel = 7;
+ session->opts.nodelay = 0;
++ session->opts.ccalgo = NULL;
+
+ session->opts.flags = SSH_OPT_FLAG_PASSWORD_AUTH |
+ SSH_OPT_FLAG_PUBKEY_AUTH |
diff --git a/src/sftp.c b/src/sftp.c
index e01012a8..702623a0 100644
--- a/src/sftp.c
diff --git a/src/main.c b/src/main.c
index cbd2574..8a229dd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -21,7 +21,8 @@ void usage(bool print_help) {
"Usage: mscp [vqDHdNh] [-n nr_conns] [-m coremask] [-u max_startups]\n"
" [-s min_chunk_sz] [-S max_chunk_sz] [-a nr_ahead] [-b buf_sz]\n"
" [-l login_name] [-p port] [-F ssh_config] [-i identity_file]\n"
- " [-c cipher_spec] [-M hmac_spec] [-C compress] source ... target\n"
+ " [-c cipher_spec] [-M hmac_spec] [-C compress] [-g congestion]\n"
+ " source ... target\n"
"\n");
if (!print_help)
@@ -51,6 +52,7 @@ void usage(bool print_help) {
" -M HMAC hmac spec\n"
" -C COMPRESS enable compression: "
"yes, no, zlib, zlib@openssh.com\n"
+ " -g CONGESTION specify TCP congestion control algorithm\n"
" -H disable hostkey check\n"
" -d increment ssh debug output level\n"
" -N enable Nagle's algorithm (default disabled)\n"
@@ -202,7 +204,8 @@ int main(int argc, char **argv)
memset(&o, 0, sizeof(o));
o.severity = MSCP_SEVERITY_WARN;
- while ((ch = getopt(argc, argv, "n:m:u:s:S:a:b:vqDrl:p:i:F:c:M:C:HdNh")) != -1) {
+ while ((ch = getopt(argc, argv,
+ "n:m:u:s:S:a:b:vqDrl:p:i:F:c:M:C:g:HdNh")) != -1) {
switch (ch) {
case 'n':
o.nr_threads = atoi(optarg);
@@ -287,6 +290,13 @@ int main(int argc, char **argv)
}
strncpy(s.compress, optarg, MSCP_SSH_MAX_COMP_STR);
break;
+ case 'g':
+ if (strlen(optarg) > MSCP_SSH_MAX_CCALGO_STR - 1) {
+ fprintf(stderr, "long ccalgo string: %s\n", optarg);
+ return -1;
+ }
+ strncpy(s.ccalgo, optarg, MSCP_SSH_MAX_CCALGO_STR);
+ break;
case 'H':
s.no_hostkey_check = true;
break;
diff --git a/src/pymscp.c b/src/pymscp.c
index c36c662..6861ad8 100644
--- a/src/pymscp.c
+++ b/src/pymscp.c
@@ -109,6 +109,7 @@ static PyObject *wrap_mscp_init(PyObject *self, PyObject *args, PyObject *kw)
"cipher", /* const char * */
"hmac", /* const char * */
"compress", /* const char * */
+ "ccalgo", /* const char * */
"password", /* const char * */
"passphrase", /* const char * */
@@ -117,10 +118,10 @@ static PyObject *wrap_mscp_init(PyObject *self, PyObject *args, PyObject *kw)
"enable_nagle", /* bool */
NULL,
};
- const char *fmt = "si" "|" "ii" "kkk" "s" "iii" "ssss" "sssss" "ipp";
+ const char *fmt = "si" "|" "ii" "kkk" "s" "iii" "ssss" "ssssss" "ipp";
char *coremask = NULL;
char *login_name = NULL, *port = NULL, *config = NULL, *identity = NULL;
- char *cipher = NULL, *hmac = NULL, *compress = NULL;
+ char *cipher = NULL, *hmac = NULL, *compress = NULL, *ccalgo = NULL;
char *password = NULL, *passphrase = NULL;
struct instance *i;
@@ -154,6 +155,7 @@ static PyObject *wrap_mscp_init(PyObject *self, PyObject *args, PyObject *kw)
&cipher,
&hmac,
&compress,
+ &ccalgo,
&password,
&passphrase,
&i->so.debug_level,
@@ -179,6 +181,8 @@ static PyObject *wrap_mscp_init(PyObject *self, PyObject *args, PyObject *kw)
strncpy(i->so.hmac, hmac, MSCP_SSH_MAX_HMAC_STR - 1);
if (compress)
strncpy(i->so.compress, compress, MSCP_SSH_MAX_COMP_STR - 1);
+ if (ccalgo)
+ strncpy(i->so.ccalgo, ccalgo, MSCP_SSH_MAX_CCALGO_STR - 1);
if (password)
strncpy(i->so.password, password, MSCP_SSH_MAX_PASSWORD - 1);
if (passphrase)
diff --git a/src/ssh.c b/src/ssh.c
index 29f9623..eceb71c 100644
--- a/src/ssh.c
+++ b/src/ssh.c
@@ -64,6 +64,12 @@ static int ssh_set_opts(ssh_session ssh, struct mscp_ssh_opts *opts)
return -1;
}
+ if (is_specified(opts->ccalgo) &&
+ ssh_options_set(ssh, SSH_OPTIONS_CCALGO, opts->ccalgo) < 0) {
+ mscp_set_error("failed to set cclago");
+ return -1;
+ }
+
/* if NOT specified to enable Nagle's algorithm, disable it (set TCP_NODELAY) */
if (!opts->enable_nagle) {
int v = 1;
diff --git a/test/test_e2e.py b/test/test_e2e.py
index 108ae71..c46141b 100644
--- a/test/test_e2e.py
+++ b/test/test_e2e.py
@@ -3,6 +3,7 @@
test_e2e.py: End-to-End test for mscp executable.
"""
+import platform
import pytest
import os
@@ -268,6 +269,21 @@ def test_compression(mscp, src_prefix, dst_prefix, compress):
src.cleanup()
dst.cleanup()
+@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
+def test_ccalgo(mscp, src_prefix, dst_prefix):
+ src = File("src", size = 1024 * 1024).make()
+ dst = File("dst").make()
+ if platform.system() == "Darwin":
+ # Darwin does not support TCP_CONGESTION
+ algo = "cubic"
+ run = run2ng
+ elif platform.system() == "Linux":
+ # Linux supports TCP_CONGESTION
+ with open("/proc/sys/net/ipv4/tcp_allowed_congestion_control", r) as f:
+ algo = f.read().strip().split().pop()
+ run = run2ok
+ run([mscp, "-H", "-vvv", "-g", algo, src_prefix + src.path, dst_prefix + "dst"])
+
testhost = "mscptestlocalhost"
testhost_prefix = "{}:{}/".format(testhost, os.getcwd()) # use current dir
diff --git a/test/test_python.py b/test/test_python.py
index a6b2787..22d778a 100644
--- a/test/test_python.py
+++ b/test/test_python.py
@@ -121,6 +121,7 @@ param_invalid_kwargs = [
{ "cipher": "invalid" },
{ "hmac": "invalid"},
{ "compress": "invalid"},
+ { "ccalgo": "invalid"},
]
@pytest.mark.parametrize("kw", param_invalid_kwargs)