Необычно медленное TCP-соединение в Linux - PullRequest
6 голосов
/ 16 января 2020

Я написал клиент-сервер в пользовательском режиме приложение на основе сокетов Беркли, которые взаимодействуют через какую-то частную сеть.
Ситуация определенно странная. Иногда связь становится очень медленной при некоторых неопределенных обстоятельствах. Обычный обмен данными по TCP в моем случае составляет около 10-25 Кбайт полезной нагрузки на сегмент, но иногда он становится примерно ~ 200-500 байт на сегмент.

После некоторого устранения неполадок я понял, что эта проблема не воспроизводится для другие сетевые сервисы, таким образом, похоже, что мой сервис виноват. Но я не могу понять, что не так. Он хорошо работал на ядре 3.10 Linux, но странное поведение на 4.4. Могли ли быть какие-то внутренние изменения в ядре, которые вызвали такую ​​проблему?

Я пытался поиграть с настройками Linux sysctl:

net.ipv4.tcp_congestion_control
net.ipv4.tcp_sack
net.ipv4.route.flush

, но это не помогло help.

Кажется, что проблема появляется на стороне прослушивающего сокета . В tcpdump размер окна TCP в порядке при рукопожатии. Но после того, как размер окна первого входящего пакета уменьшается (со стороны слушателя).

UPD
Вот мой фрагмент кода на стороне сервера:

 serv_fd = socket(AF_INET, SOCK_STREAM, 0); 
 if (serv_fd == -1) {
      perror("socket");
      return;
 }   

 server.sin_family = AF_INET;
 server.sin_port = htons(LISTEN_PORT);
 server.sin_addr.s_addr = htonl(INADDR_ANY);

 #ifdef SET_BUF
 if (setsockopt(serv_fd, SOL_SOCKET, SO_RCVBUF, &buflen, sizeof(int)) == -1) {
      perror ("setsockopt");
      return;
 }   
 if (setsockopt(serv_fd, SOL_SOCKET, SO_SNDBUF, &buflen, sizeof(int)) == -1) {
      perror ("setsockopt");
      return;
 }   
 #endif // SET_BUF

 if (bind(serv_fd, (struct sockaddr *) &server, sizeof(server)) == -1) {
      perror("bind");
      return;
 }   

 if (listen(serv_fd, 3)) {
      perror("listen");
      return;
 }   

 printf("Server is listening on %u\n", LISTEN_PORT);

Может кто-нибудь пролить свет на мою проблему ? Буду очень признателен!
Может ли это быть связано с некоторыми недавними Linux модификациями ядра? Нужно ли настраивать некоторые настройки ядра Linux или проверять некоторые настройки пользовательского режима (например, параметры сокета или что-то еще)?

PS Проблема нестабильна.

UPD:

Вывод tcpdump:

IP 10.0.0.34.31334 > 10.0.0.99.12345: Flags [S], seq 426261790, win 43690, options [mss 65495,sackOK,TS val 799180610 ecr 0,nop,wscale 7], length 0
IP 10.0.0.99.12345 > 10.0.0.34.31334: Flags [S.], seq 803872704, ack 426261791, win 65483, options [mss 65495,sackOK,TS val 799180567 ecr 799180610,nop,wscale 0], length 0
IP 10.0.0.34.31334 > 10.0.0.99.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 799180610 ecr 799180567], length 0
IP 10.0.0.34.31334 > 10.0.0.99.12345: Flags [P.], seq 1:1301, ack 1, win 342, options [nop,nop,TS val 799180610 ecr 799180567], length 1300
IP 10.0.0.34.31334 > 10.0.0.99.12345: Flags [P.], seq 1301:1804, ack 1, win 342, options [nop,nop,TS val 799181412 ecr 799180610], length 503
IP 10.0.0.99.12345 > 10.0.0.34.31334: Flags [.], ack 1804, win 512, options [nop,nop,TS val 799181412 ecr 799181412], length 0

10.0.0.34.31334 - это клиент, 10.0.0.99.12345 - это сервер. Обратите внимание на неожиданное win 512 в последней строке.

UPD2: Я видел несколько сообщений о SYN-куки в dmesg, например:

possible SYN flooding on port 12345. Sending cookies.

Но они не так уж связаны с медленным передачи.

1 Ответ

8 голосов
/ 24 января 2020

Я не совсем уверен, что это именно ваш случай, но выглядит похоже. Кажется, это известная проблема .

Причины

Ряд обстоятельств может привести к такому Linux поведению ядра:

  • Специфика обработки соединения с ядром в контексте SYN-куки с соединениями, имеющими нулевую оконную шкалу (или если WS изменен каким-либо другим способом).
  • нулевая оконная шкала вы спровоцировали setsockopt() с SO_RCVBUF (см. tcp_select_initial_window())
  • Чрезвычайно маленький backlog

Объяснение

О «медленной» передаче :
Параметр Windows Масштабирование рассчитывается на этапе [ SYN - SYN + ACK ] обоими хостами. Грубо говоря, узел A говорит «подразумевать размер моего окна TCP на N во время будущего обмена» (SYN), а затем узел B говорит «подразумевать размер моего окна TCP на M во время будущего обмена» (SYN + ACK) - здесь N и M могут быть одинаковыми , Таким образом, в обычной ситуации эти коэффициенты сохраняются и в конечном итоге используются во время обмена данными.
Но TCP SYN-cookie метод подразумевает забвение об этапе [ SYN - SYN + ACK ] соединения (некоторые указанные опции, включая WS, будут потеряны после SYN + ACK). В этом случае Linux kernel пересчитывает значение WS , когда ACK приходит (если ACK прибыл, то необходимо создать обычное соединение). Но этот второй пересчет может быть немного другим, потому что setsockopt() не влияет на него (по некоторым объективным причинам). Здесь вы сталкиваетесь с ситуацией, когда ваш сервер отправляет нулевую опцию Window Scale с помощью SYN + ACK, затем забывает об этом, затем повторно порождает соединение (когда приходит ACK), как это было с некоторым стандартным Window Scale (например, 7) и использует небольшое окно. подразумевается, что клиент умножит его на 128. Но клиент не забывает, что WS равен 0 и обрабатывает небольшой размер окна как реальный - следовательно, он отправляет небольшие порции данных - тем самым ваше «медленное» соединение выходит на сцену.

О SYN-флуде:
Когда у вас такой небольшой резерв, простые 3 SYN-ретрансляции могут спровоцировать SYN-куки (то есть заполнить вашу очередь невыполненных работ). Кстати, вы видите повторные передачи в tcpdump?
From ip-sysctl.txt :

Note, that syncookies is fallback facility.
It MUST NOT be used to help highly loaded servers to stand
against legal connection rate. If you see SYN flood warnings
in your logs, but investigation shows that they occur
because of overload with legal connections, you should tune
another parameters until this warning disappear.
See: tcp_max_syn_backlog, tcp_synack_retries, tcp_abort_on_overflow.

syncookies seriously violate TCP protocol, do not allow
to use TCP extensions, can result in serious degradation
of some services (f.e. SMTP relaying), visible not by you,
but your clients and relays, contacting you. While you see
SYN flood warnings in logs not being really flooded, your server
is seriously misconfigured.

Так что если в вашей локальной сети нет атак SYN-flood - ваш сервер серьезно неправильно. SYN-куки должны выполнять свою работу только при наличии атаки SYN-флуд.


Решение

В заключение, могут быть предприняты некоторые действия для устранения проблемы:

  1. Если в вашей сети есть настоящий SYN-поток - файлы SYN-cookie частично решают эту проблему информационной безопасности. При настоящей атаке нет времени думать о медленных соединениях. Это чрезвычайная ситуация.
  2. Если нет, то есть некоторые SYN-ретрансляции вызвали SYN-куки:
    • вдумчиво увеличить отставание для устранения таких условий;
    • не делать setsockopt() с SO_RCVBUF на сокете прослушивания . Это не имеет особого смысла. Без выполнения setsockopt() вы можете уменьшить вероятность различных вычислений WS ядром в указанном сценарии. Кстати, вы можете установить SO_RCVBUF на принятом сокете, если это необходимо.

Repro

Я воспроизвел вашу проблему с простым клиентом и сервером с hping3 при приблизительных условиях. Таким образом, вы можете заполнить backlog очередь сервера:

hping3 -c 3 -S -p 12345 --fast 10.0.0.99

, а затем инициировать соединение с клиента - соединение будет открыто в так называемом "контексте SYN-cookies" хотя бы на ядре 4.4. Вы также можете проверить это на ядре 3.10, увеличив -c с 3 до X до успешного воспроизведения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...