Сбросить открытое TCP-соединение без отправки RST
Глядя на nginx: игнорировать некоторые запросы без правильного заголовка Host, я подумал, что на самом деле это невозможно close(2)
TCP-соединение без операционной системы должным образом завершает основное TCP-соединение, отправляя RST
(И / или FIN
) на другой конец.
Одним из обходных путей было бы использовать что-то вроде tcpdrop(8)
Однако, как видно из usr.sbin/tcpdrop/tcpdrop.c
в OpenBSD и FreeBSD он реализован через интерфейс на основе sysctl и может иметь проблемы с переносимостью за пределами BSD. (На самом деле, похоже, что даже реализация на основе sysctl может быть достаточно разной в OpenBSD и FreeBSD, чтобы требовать уровня переноса - OpenBSD использует tcp_ident_mapping
структура (которая, следовательно, содержит два sockaddr_storage
элементы, плюс некоторая другая информация), тогда как FreeBSD, DragonFly и NetBSD используют массив из двух sockaddr_storage
элементы напрямую.) Оказывается, что OpenBSD tcpdrop
действительно, чтобы отправить R
пакет согласно tcpdump(8)
и может быть подтверждено, посмотрев на /sys/netinet/tcp_subr.c :: tcp_drop()
, который вызывает tcp_close()
в конце (и tcp_close()
подтверждено отправить RST
в другом месте на SO), так что, похоже, это даже не сработает.
Если я сам устанавливаю соединение через C, есть ли способ впоследствии отбросить его без подтверждения другой стороне, например, без инициации RST
?
2 ответа
Обман злоумышленника таким образом может быть хорошей идеей. Конечно, в этом случае вы уже резервируете ресурсы сервера для установленного соединения. В самом основном режиме вы можете использовать netfilter для удаления любого исходящего сегмента TCP с установленными флагами RST или FIN. Это правило iptables может быть примером:
sudo iptables -A OUTPUT -p tcp --tcp-flags FIN,RST SYN -j DROP
Конечно, это правило повлияет на все ваши TCP-соединения. Я написал это просто, чтобы показать, как вы можете это сделать. Перейдите на https://www.netfilter.org/ чтобы получить больше идей для работы над вашим решением. В принципе вы должны быть в состоянии сделать то же самое только для выбранных соединений.
Из-за того, как работает TCP, если вы сможете реализовать его, клиент (или злоумышленник) будет держать соединение открытым в течение длительного времени. Чтобы понять, как это повлияет на клиента, прочитайте здесь: TCP, функция recv зависает, несмотря на KEEPALIVE, где я предоставляю результаты теста, в котором другая сторона не возвращает ни одного сегмента TCP (даже ACK). В моей конфигурации сокету требуется 13 минут для перехода в состояние ошибки (это зависит от параметров Linux, таких как tcp_retries1 и tcp_retries2).
Просто учтите, что атака DoS обычно подразумевает подключения от тысяч различных устройств, а не обязательно от нескольких подключений к одному устройству. Это очень легко обнаружить и заблокировать в брандмауэре. Таким образом, очень маловероятно, что вы будете генерировать исчерпание ресурсов в клиенте. Также это решение не будет работать в случаях полуоткрытой атаки соединения.
Если я сам устанавливаю соединение через C, есть ли способ впоследствии отбросить его без подтверждения на другую сторону, например, без инициирования RST?
Нет. Даже если бы это было так, если бы коллега впоследствии отправил что-нибудь, на него бы ответил RST.
NB Обычное завершение TCP использует FIN, а не RST.