Постановка задачи

Я поддерживаю удаленный сервер через SSH. Чтобы мое обслуживание оказало лишь незначительное влияние на скорость сети сервера, я ограничиваю исходящий трафик до 200 КБ / с, отбрасывая пакеты, используя следующее правило iptables:

# ip6tables -A INPUT -p tcp -m hashlimit --hashlimit-above 200kb/s -m tcp --destination 3ffe:ffff::dead:beef --dport 22 -j DROP

# ip6tables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp      anywhere             3ffe:ffff::dead:beef  limit: above 200kb/s tcp dpt:22

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Однако теперь, когда я насыщаю ссылку, сеансы SSH падают в предсказуемые 15-минутные интервалы времени:

$ for TRIAL in `seq 1 5`
> do
>     yes | dd status=progress if=/dev/stdin bs=1k count=$((500*1024)) 2>dd.$TRIAL.log |
>     ssh -vvv remotehost 'cat >/dev/null' 2>&1 |
>     while read LINE
>     do
>         printf '%s\t%s\n' `date +%H:%M:%S` "$LINE"
>     done | tee output.$TRIAL.log
> done

$ tail <output.1.log
20:51:53        debug2: channel 4: rcvd adjust 131072
20:51:54        debug2: channel 4: rcvd adjust 131072
20:51:55        debug2: channel 4: rcvd adjust 131072
20:51:55        debug2: channel 4: rcvd adjust 131072
20:51:56        debug2: channel 4: rcvd adjust 131072
20:51:56        debug2: channel 4: rcvd adjust 131072
20:51:57        debug2: channel 4: rcvd adjust 131072
20:51:58        debug2: channel 4: rcvd adjust 131072
20:51:58        debug3: send packet: type 1
20:51:58        packet_write_wait: Connection to 3ffe:ffff::dead:beef port 22: Broken pipe

$ for TRIAL in `seq 2 5`; do tail -n 1 <output.$TRIAL.log; done
21:07:34        packet_write_wait: Connection to 3ffe:ffff::dead:beef port 22: Broken pipe
21:23:11        packet_write_wait: Connection to 3ffe:ffff::dead:beef port 22: Broken pipe
21:38:47        packet_write_wait: Connection to 3ffe:ffff::dead:beef port 22: Broken pipe
21:54:24        packet_write_wait: Connection to 3ffe:ffff::dead:beef port 22: Broken pipe

$ for TRIAL in `seq 1 5`; do cat <dd.$TRIAL.log; echo; done
190336000 bytes (190 MB, 182 MiB) copied, 925.446 s, 206 kB/s
190317568 bytes (190 MB, 182 MiB) copied, 925.541 s, 206 kB/s
190258176 bytes (190 MB, 181 MiB) copied, 925.136 s, 206 kB/s
190503936 bytes (191 MB, 182 MiB) copied, 926.104 s, 206 kB/s
190619648 bytes (191 MB, 182 MiB) copied, 926.24 s, 206 kB/s

Как я пытался решить проблему

На удаленной стороне выполненные команды все еще зависают, поэтому сервер не обнаруживает, что сеанс сброшен. Это говорит мне, что это проблема клиента:

$ ssh remotehost ps ax | grep -F 'cat >/dev/null'
 6999 ?        Ss     0:00 bash -c cat >/dev/null
13084 ?        Ss     0:00 bash -c cat >/dev/null
13425 ?        Ss     0:00 bash -c cat >/dev/null
13593 ?        Ss     0:00 bash -c cat >/dev/null
13779 ?        Ss     0:00 bash -c cat >/dev/null

Если я ограничу скорость, с которой я передаю данные в SSH (обратите внимание на команду pv -q -L 200k ниже), сеансы SSH больше не сбрасываются, поэтому я использую это в качестве обходного пути, пока не найду решение:

# ip6tables -D INPUT -p tcp -m hashlimit --hashlimit-above 200kb/s -m tcp --destination 3ffe:ffff::dead:beef --dport 22 -j DROP

# ip6tables -A INPUT -p tcp -m hashlimit --hashlimit-above 300kb/s -m tcp --destination 3ffe:ffff::dead:beef --dport 22 -j DROP

$ while TRIAL in `seq 6 10`
> do
>     yes | dd status=progress if=/dev/stdin bs=1k count=$((500*1024)) 2>dd.$TRIAL.log |
>     pv -q -L 200k | ssh -vvv remotehost 'cat >/dev/null' 2>&1 |
>     while read LINE
>     do
>         printf '%s\t%s\n' `date +%H:%M:%S` "$LINE"
>     done | tee output.$TRIAL.log
> done

$ tail <output.6.log
22:48:14
22:48:14        debug1: channel 3: free: port listener, nchannels 1
22:48:14        debug3: channel 3: status: The following connections are open:
22:48:14
22:48:14        debug1: fd 0 clearing O_NONBLOCK
22:48:14        debug1: fd 1 clearing O_NONBLOCK
22:48:14        debug1: fd 2 clearing O_NONBLOCK
22:48:14        Transferred: sent 524986928, received 94512 bytes, in 2925.9 seconds
22:48:14        Bytes per second: sent 179429.8, received 32.3
22:48:14        debug1: Exit status 0

$ for TRIAL in `seq 6 10`; do tail -n 1 <dd.$TRIAL.log; done
524288000 bytes (524 MB, 500 MiB) copied, 2919.03 s, 180 kB/s
524288000 bytes (524 MB, 500 MiB) copied, 2559.03 s, 205 kB/s
524288000 bytes (524 MB, 500 MiB) copied, 2644.5 s, 198 kB/s
524288000 bytes (524 MB, 500 MiB) copied, 2559.03 s, 205 kB/s
524288000 bytes (524 MB, 500 MiB) copied, 2559.01 s, 205 kB/s

Это говорит мне о том, что отбрасывание сеансов SSH связано с тем, что контрольные сообщения SSH не проходят, но почему это так?

Пакеты поддержки активности TCP, вероятно, отброшены, но ядро не отправляет первый пакет поддержки активности, пока не пройдет два часа, после того, как мои сеансы SSH были сброшены через 15 минут, так что это вряд ли является причиной моей проблемы (/proc/sys/net/ipv4/tcp_keepalive_time применяется как к IPv6, так и к IPv4):

$ cat /proc/sys/net/ipv4/tcp_keepalive_time
7200

Клиент SSH настроен на тайм-аут, если он не получает ответ на три последовательных сообщения поддержки активности SSH, которые отправляются с 5-минутными интервалами. В зависимости от времени это кажется более вероятной причиной, но вышеприведенные выходные данные отладки SSH не указывают, что эти сообщения отправляются клиентом. В начале есть только куча сообщений (аутентификация, открытие канала и т. Д.), А затем через 15 минут сообщение об отключении, между которыми ничего нет:

$ grep 'send packet' <output.1.log
20:36:33        debug3: send packet: type 20
20:36:33        debug3: send packet: type 30
20:36:33        debug3: send packet: type 21
20:36:33        debug3: send packet: type 5
20:36:33        debug3: send packet: type 50
20:36:33        debug3: send packet: type 50
20:36:33        debug3: send packet: type 50
20:36:33        debug3: send packet: type 90
20:36:33        debug3: send packet: type 80
20:36:33        debug3: send packet: type 98
20:36:33        debug3: send packet: type 98
20:51:58        debug3: send packet: type 1

И не только это, но если я заменю удаленную команду cat>/dev/null на tee>/dev/null, вывод возвращается ко мне, но сеанс SSH по-прежнему прерывается, так что, похоже, проблема не возникает не в состоянии получать ответы от сервера. В обратном направлении сервер вообще не отправляет сообщения о поддержании SSH:

$ cat .ssh/config
Host remotehost
User username
Hostname 3ffe:ffff::dead:beef
ControlMaster auto
ControlPath /var/tmp/remotehost.socket
TCPKeepAlive yes
ServerAliveInterval 300
ServerAliveCountMax 3

$ ssh remotehost cat /etc/ssh/sshd_config
PasswordAuthentication no
TCPKeepAlive yes
ClientAliveInterval 0

0