Вопрос касается реализации логики tcp proxy в tpws.
tcp proxy означает двустороннюю переброску данных между двумя tcp соединениями (local и remote leg).
Важно корректно закрывать соединения без потери неотосланных кусков данных, когда какой-то конец инициирует закрытие.
Но закрытие может быть разным. Это может быть close через FIN, close через RST и half-close - shutdown(SHUT_WR).
Есть ли способ средствами epoll (да и не только ими) отличить состояние half close от close ?
Если закрывают с RST, приходит EPOLLERR, тут все ясно. connection reset by peer.
half close возникает, когда другой конец выполнил shutdown(SHUT_WR). В этом случае от него поступает tcp пакет с FIN, что не означает полное закрытие соединения. Ему еще можно досылать остаток данных.
В этом случае приходит EPOLLRDHUP.
Но он же и приходит, когда другой конец закрыл сокет без неотосланных данных, без RST.
В первом случае еще имеет смысл ожидать от другого конца прокси каких-то данных, во втором надо скорее уже их обоих кидать, как только все неотосланное будет отправлено.
То, что сделано сейчас, при отсутствии ответа FIN с другой стороны приводит к подвисанию обоих концов - одного в состоянии FIN_WAIT, другого в состоянии CLOSE_WAIT до истечения fin-wait timeout.
Это не смертельно, но если бы можно было закрывать поскорее, было бы лучше.
Как вариант настроить параметры conntrack_tcp_timeout уменьшить значения
conntrack_generic_timeout=60 по умолчанию 600
conntrack_tcp_timeout_close_wait=30 по умолчанию 60
conntrack_tcp_timeout_established=180 по умолчанию 7440
conntrack_tcp_timeout_fin_wait=30 по умолчанию 120
conntrack_tcp_timeout_syn_recv=30 по умолчанию 60
conntrack_tcp_timeout_syn_sent=60 по умолчанию 120
conntrack_tcp_timeout_time_wait=30 по умолчанию 120
conntrack_udp_timeout_stream=60 по умолчанию 120
меньше ставить значение я не пробовал, но думаю некоторые можно ещё уменьшить
это глобальные настройки для всех соединений, настраиваются через nftables или iptables
Самых 2 жестких параметра conntrack_generic_timeout и conntrack_tcp_timeout_established они жрут ресурсы и удерживают неактивное соединение
Эти sysctl относятся к netfilter и conntrack, не к системе
conntrack есть следилка за соединениями, позволяющая применять stateful действия к проходящему, исходящему и входящему трафику
Эти значения таймаутов не должны относиться к tcpip реализации системы.
Система вполне может работать, если выгрузить nf_conntrack, и логика tcp states, прописанная в rfc, от этого никуда не уйдет. Иначе tcpip вообще не будет работать.
Хотя это и не решает задачу напрямую, но достаточно полезная штука в том числе и для смягчения этой проблемы.
Жаль, только на linux и macos работает.
Реализовал.
Перечитал начальный пост. На сокете, на который уже сделали shutdown(SHUT_WR), но в котором ещё есть непрочитанные вами данные, epoll должен вернуть EPOLLIN | EPOLLRDHUP, а не голый EPOLLRDHUP. Неужели это не так?
Проблема не в этом. На том конце, которому делают SHUT_WR, как раз все как ожидается.
Проблема на конце, где мне делают SHUT_WR. Я получают оттуда EPOLLRDHUP. Мне тут без разницы есть ли EPOLLIN, так как всегда при этом происходит полная вычитка сокета до recv<=0.
Я не могу на этом конце отличить сделали мне SHUT_WR или закрыли сокет через close().
В первом случае мне еще есть смысл ожидать, когда мне будет что передать этому концу. Во втором случае надо сразу закрывать его сокет и как можно быстрее заканчивать с другим концом, тк ждать смысла нет. Если тот конец висит, то ждать можно до FIN_WAIT timeout.
И получается у меня пара FIN_WAIT_1 + CLOSE_WAIT.
В нормальной ситуации я делаю второму концу SHUT_WR, и если у него ничего нет , - он мне быстренько шлет ACK на мой FIN, на что я получаю EPOLLHUP, и все хорошо.
Но он может повиснуть. 3-way handshake проходит, дальше мертво. Ситуация cdn, когда backend мертв