Muito obrigado às pessoas que enviaram ideias nos comentários. Eu passei por todos eles:
Gravando pacotes com o tcpdump e comparando o conteúdo no WireShark
# tcpdump -i wlan0 -w good.ssh & \
cat signature | ssh -o "ProxyCommand nc %h %p" \
[email protected] 'cat | md5sum' ; \
killall tcpdump
# tcpdump -i wlan0 -w bad.ssh & \
cat signature | ssh [email protected] 'cat | md5sum' ; \
killall tcpdump
Não houve diferença de importância nos pacotes gravados.
Verificando a modelagem de tráfego
Não tinha ideia sobre isso, mas depois de analisar a página "tc", consegui verificar que
-
tc filter show
não retorna nada -
tc class show
não retorna nada -
tc qdisc show
... retorna estes:
qdisc noqueue 0: dev lo root refcnt 2
qdisc noqueue 0: dev docker0 root refcnt 2
qdisc fq_codel 0: dev wlan0 root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
... que não parecem diferenciar entre "ssh" e "nc" - na verdade, eu nem tenho certeza se o traffic shaping pode operar no nível do processo (eu esperaria que funcionasse em endereços / Portas / Serviços diferenciados no cabeçalho IP).
Debian Chroot, para evitar potencial "esperteza" no cliente SSH Arch Linux
Não, os mesmos resultados.
Finalmente - Nagle
Executando uma strace no remetente ...
pv data | strace -T -ttt -f ssh 192.168.1.150 'cat | md5sum' 2>bad.log
... e observando o que exatamente acontece no soquete que transmite os dados, notei essa "configuração" antes do início da transmissão:
1522665534.007805 getsockopt(3, SOL_TCP, TCP_NODELAY, [0], [4]) = 0 <0.000025>
1522665534.007899 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000021>
Isso configura o soquete SSH para desabilitar o algoritmo de Nagle. Você pode pesquisar no Google e ler tudo sobre ele - mas o que significa é que o SSH está dando prioridade à capacidade de resposta sobre a largura de banda - instrui o kernel a transmitir qualquer coisa escrita neste soquete imediatamente e não "atrasar" aguardando confirmações do controle remoto. / p>
O que isto significa, em termos simples, é que na sua configuração padrão, o SSH NÃO é uma boa maneira de transportar dados - não quando o link usado é lento (o que é o caso de muitos links WiFi). Se estamos enviando pacotes pelo ar que são "principalmente cabeçalhos", a largura de banda é desperdiçada!
Para provar que esse foi realmente o culpado, usei LD_PRELOAD para "descartar" este syscall específico:
$ cat force_nagle.c
#include <stdio.h>
#include <dlfcn.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
int (*osetsockopt) (int socket, int level, int option_name,
const void *option_value, socklen_t option_len) = NULL;
int setsockopt(int socket, int level, int option_name,
const void *option_value, socklen_t option_len)
{
int ret;
if (!osetsockopt) {
osetsockopt = dlsym(RTLD_NEXT, "setsockopt");
}
if (option_name == TCP_NODELAY) {
puts("No, Mr Nagle stays.");
return 0;
}
ret = osetsockopt(socket, level, option_name, option_value, option_len);
return ret;
}
$ gcc -fPIC -D_GNU_SOURCE -shared -o force_nagle.so force_nagle.c -ldl
$ pv /dev/shm/data | LD_PRELOAD=./force_nagle.so ssh [email protected] 'cat >/dev/null'
No, Mr Nagle stays.
No, Mr Nagle stays.
100MiB 0:00:29 [3.38MiB/s] [3.38MiB/s] [================================>] 100%
Há - velocidade perfeita (bem, tão rápido quanto iperf3).
Moral da história
Nunca desista: -)
E se você usar ferramentas como rsync
ou borgbackup
que transportam seus dados por SSH, e seu link for lento, tente parar o SSH de desativar o Nagle (como mostrado acima) - ou usar ProxyCommand
para mude o SSH para conectar via nc
. Isso pode ser automatizado em seu $ HOME / .ssh / config:
$ cat .ssh/config
...
Host orangepi
Hostname 192.168.1.150
User root
Port 22
# Compression no
# Cipher None
ProxyCommand nc %h %p
...
... para que todos os usos futuros de "orangepi" como host de destino em ssh / rsync / borgbackup usem a partir de agora nc
para se conectar (e, portanto, deixar Nagle em paz).