diff options
author | Ryo Nakamura <upa@haeena.net> | 2023-09-09 14:32:15 +0900 |
---|---|---|
committer | Ryo Nakamura <upa@haeena.net> | 2023-11-01 19:54:18 +0900 |
commit | bf74aa095adf8d0a3b4144b29b2d7a48fe4e02b2 (patch) | |
tree | d3d58b31457af827cecf439b902e14b62d41c129 | |
parent | a88471fc43a8500f73cd55eeb87b3a990cb0b4f9 (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.h | 2 | ||||
-rw-r--r-- | patch/libssh-0.10.4.patch | 151 | ||||
-rw-r--r-- | src/main.c | 14 | ||||
-rw-r--r-- | src/pymscp.c | 8 | ||||
-rw-r--r-- | src/ssh.c | 6 | ||||
-rw-r--r-- | test/test_e2e.py | 16 | ||||
-rw-r--r-- | test/test_python.py | 1 |
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 @@ -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) @@ -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) |