Введение
Столкнулся с ошибкой “kernel: TCP: request_sock_TCP: Possible SYN flooding on port 5005. Sending cookies. Check SNMP counters” на RHEL 7. Для понимания и закрепления информации изучил различные источники и задокументировал основные моменты.
SYN Queue
В целом нужно понимать, как работает TCP, у которого есть некий буфер очередей на кол-во соединений. И когда сервис не справляется с наплывом клиентов и перестает обрабатывать новые соединения, они накапливаются в backlog, т.е. в той самой очереди.
Тут-то в игру и вступают SYN Cookie, которые являются механизмом ядра от Red Hat. При заполнении очереди, ядро формирует куки с содержанием специального SYN+ACK, в котором порядковый номер TCP является функцией времени, MSS и IP-адресов клиента и сервера. И в итоге куки позволяют устанавливать TCP-соединения, когда очередь сокета переполнена по какой-либо причине (SYN flood атака или само приложение уже упёрлось в лимиты).
Для конечного пользователя это всё хорошо, т.к. его соединение не будет дропнуто. А вот для сервера, где такие сообщения об отправке кук встречаются очень часто, является нехорошим признаком того, что пора тюнить ядро.
Отключать куки не рекомендуется, поэтому необходимо выявить, где установлены лимиты, и увеличить их.
Ранее говорилось про бэклог, т.е. некую очередь. Так вот существует две очереди у каждого сокета. У этих очередей имеются различные названия, например, «reqsk_queue», «ACK backlog», «listen backlog» или даже «TCP backlog», но их корректные наименования представлены ниже:
- SYN queue, когда сервер получает SYN, шлет SYN-ACK и не получает ACK в ответ, т.е. получается полу-открытое соединение и ожидание ACK – последнего пакета для установления хендшейка. Повторная отправка SYN-ACK ограничена лимитом в net.ipv4.tcp_synack_retries. После получения ACK, ядро Linux удаляет элемент из очереди SYN и перемещает его в следующую очередь приёма. Когда таких соединений накапливается большое кол-во, формируется очередь, на размер которой влияет параметр tcp_max_syn_backlog;
- Accept queue содержит в себе уже полностью установленные соединения и их кол-во ограничивает net.core.somaxconn
В интернете гуляет множество ответов по запросу “kernel: TCP: request_sock_TCP: Possible SYN flooding”, где предлагается увеличить и net.ipv4.tcp_max_syn_backlog и net.core.somaxconn. В конечном итоге в статье cloudflare было сказано, что в настоящее время за размер и SYN очереди и Accept очереди отвечает один параметр net.core.somaxconn, т.е. тюнингу подлежит именно он, и у cloudflare его значение имеет следующее значение:
$ sysctl net.core.somaxconn
net.core.somaxconn = 16384
Про тюнинг ядра
Я не буду писать про то, в чём досконально не разбираюсь, а именно про тюнинг ядра. В интернете есть много примеров с описанием, в т.ч. даже в исходном коде. Поэтому кратко выписал основные параметры, которые в рамках данной статьи могут быть интересны:
а) net.ipv4.tcp_syncookies = 1 – включает куки (по дефолту включены)
б) net.core.somaxconn – максимальное кол-во открытых сокетов, ждущих соединения, по дефолту значение 128
в) net.ipv4.tcp_max_syn_backlog – максимальное кол-во запоминаемых запросов на соединения, для которых не было получено подтверждения от подключающегося клиента, по дефолту значение 128
г) net.ipv4.tcp_synack_retries – кол-во попыток отправки SYN-ACK пакетов, дефолтом 5.
И в конце возникает главный вопрос: какое же значение наиболее корректное? Ответ, как правило, звучит так – зависит от ситуации и обстоятельств. Да, тюнинг ядра такая вещь, что нет универсальных рецептов. Тем не менее можно взять за основу следующие утверждения:
- “Простым” серверам, которыми в большинстве случаев являются не сильно нагруженные проекты типа интернет-магазина или корпоративного портала, данный вид тюнинга вообще не требуется. А вот серверам, например, которые обслуживают клиентские чаты на часто посещаемом сайте, такой вид тюнинга точно понадобится с увеличением количества пользователей;
- Если большинство клиентов находятся физически далеко от сервера, т.е. имеют более высокие задержки, то значение очередей также можно подтюнить в большую сторону;
- Увеличение в большую сторону бездумно имеет негативные последствия и отразится на памяти – каждый слот в SYN-очереди будет занимать 256 байт (ядро 4.14) и при SYN Flood-атаке память может закончиться.
Средства диагностики
Как понять, что очередь заполнена и с этим имеются проблемы, кроме косвенных признаков медленного соединения? В команде ниже размер глобальных счётчиков TcpExtListenOverflows и TcpExtListenDrops будет активно расти, входящие пакеты SYN и ACK в SYN-очередь дропаются:
nstat -az | grep -i listen
TcpExtListenOverflows 3518352 0.0
TcpExtListenDrops 3518388 0.0
TcpExtTCPFastOpenListenOverflow 0 0.0
То, что пакеты дропаются, в целом нормальное поведение – клиент рано или поздно повторит отправку SYN или ACK. Но если такое происходит постоянно и из-за этого возникают проблемы, то необходимо заниматься ещё более тонкой настройкой ядра и приложения, что уже выходит за рамки данного материала.
Наконец, можно воочию посмотреть на кол-во соединений в очереди SYN, используя netstat или более современный аналог ss:
ss -n state syn-recv sport = :443 | wc -l
Если в очереди нет соединений, то в команде выше будет пустой результат, т.е. всё хорошо. Ещё более наглядный пример:
ss -plnt sport = :443 | cat
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 1024 *:6443 *:*
Цифры в Recv-Q показывают кол-во сокетов в Accept очереди, а Send-Q размер бэклог-лимита.
Заключение
Когда приложение не успевает по каким-либо причинам обработать все соединения, начинает накапливаться очередь и параметр somaxconn её регулирует. Если очередь мала, то клиент отвалится, т.е. будет разрыв соединения, а если очередь будет большого размера, то у клиента будут задержки в работе, но соединение не разорвётся. Так и получилось, что у меня пришло сообщение в логе про куки – лимиты заполнились, а nginx не успевал обрабатывать соединения и немного подтупливал.
Вообще тема тюнинга больше актуальна на больших проектах, о чем я уже говорил, но в какой-то момент возникает тонкая грань, по которой надо будет определять, когда проект постепенно начинает из среднего перерастать в большой. Для этого, как минимум, нужно настраивать мониторинг и наблюдать за динамикой собираемых метрик приложения и ОС.
UPD. Спустя какое-то время после написания данного материала вышла интересная статья на хабре, решил обновить пост и указать на неё ссылку, т.к. там неплохо рассказывается про сетевой стек в Linux и его настройку для хайлоада.
Используемые источники
- https://blog.cloudflare.com/syn-packet-handling-in-the-wild
- https://stackoverflow.com/questions/62641621/what-is-the-difference-between-tcp-max-syn-backlog-and-somaxconn
- https://serverfault.com/questions/875035/sane-value-for-net-ipv4-tcp-max-syn-backlog-in-sysctl-conf
- https://github.com/cloudflare/cloudflare-blog/blob/master/2018-01-syn-floods/resq.stp
- http://veithen.io/2014/01/01/how-tcp-backlog-works-in-linux.html
- https://access.redhat.com/solutions/30453
- https://www.nginx.com/blog/tuning-nginx/