Пстач сетевой, а существует ли какой-то способ добиться следующего, не калеча код tcp/ip стека ядра?
1) При получении tcp-пакета с определенным признаком в заголовке (опции там, или установленные зарезервированные биты, или похуй чо) на определенный порт система должна автоматически установить коннект с отправляющим клиентом (т. е. сделать вид что получила от него SYN и ответила SYN+ACK, на самом деле этого не делая; пнуть слушающий на этом порту сокет, и использовать установленные в заголовке пакета seq и ack_seq в качестве начальных значений).
2) При наличии другого признака в заголовке пакета принять его, не отправляя в ответ подтверждение (ack);
3) Для непомеченных пакетов вести себя как обычно.
Clarification A: Да, проще всего вбить эту логику, поправив код сетевой подсистемы ядра, но такая реализация мне не нравится как минимум по двум причинам — из-за необходимости, собственно, пересобирать его на каждой ноде, и из-за необходимости мержить этот стремный патч при каждом его обновлении. А поскольку "stable api is nonsense", мне жаль потомков.
Clarification B: Я знаю про возможность сделать модуль, устанавливающий netfilter hook, который дропал бы нужные пакеты. Эта идея мне не нравится из-за второго пункта из предыдущего абзаца, бросающейся в глаза костыльности и неприемлемого оверхеда при реализации.
Clarification C: Да, я знаю, что такое в школе не проходят и я спросил не там, но вдруг.
Cthulhu
19.04.2012 07:30 work
Recommended by:
@snakehoney
Do you really want to delete ?
очевидно, нельзя. если очень хочется можно попытаться закостылировать каким-либо методом отправляя пакеты прямо в userspace, минуя TCP (но тогда проблема с сокетами).
Олсо, зачем нужна такая поебень?
Для сервера, работающего в юзерспейсе, все это шаманство должно быть прозрачно, потому в эту сторону костылять нету смысла.
А нужно, ну... Есть access server, на котором развернут перепиленный lvs (он единственный из всего кластера доступен из внешней сети). Он принимает соединения от клиентов, парсит запрос, и в зависимости от его параметров выбирает из кластера ноду, которая будет этот запрос выполнять, и передает этой ноде заботу об открытом подключении (т. е. пакеты от клиента к ноде будут идти все еще через access server, а от ноды к клиенту уже напрямую). И, повторюсь, все это должно быть прозрачно как для клиента, так и для сервера на ноде.
а зачем для этого какие-то флаги в пакетах и зачем калечить сетевой стек? Почему нельзя просто на все ноды кластера повесить ІР на который обращается клиент, тогда стек каждой ноды будет без всяких изъёбств отправлять пакеты нужному приложению. От access server в такой позе требуется только выставлять правильный L2-адрес для каждого пакета.
а, ну да, это же линукс...
Ну смотри. Предположим, что в качестве сервера на нодах у нас работает http. Аксес сервер должен работать в качестве L7 роутера (т. е. определять ноду в зависимости от параметров GET-запроса). На него прилетает SYN от клиента, он отвечает SYN+ACK, клиент кидает ему ACK и пакет с запросом. Он парсит запрос, определяет нужную ноду, и пересылает ей этот же запрос (а у нас его еще и поправить чуть придется — размер изменится, отсюда необходимость не отправлять в некоторых случаях ack — этим будет заниматься access, который знает оригинальный размер пакета; но на это пока можно забить) от имени клиента (т. е. нода должна начать слать клиенту данные сразу, без всяких там хендшейков). Потому что если она пошлет ему SYN+ACK — он охуеет и сбросит подключение.
какое-то скрещивание ужа с ежом у тебя получается: тут мы L7-роутер, а тут L3-балансировщик. Наиболее корректный вариант — устанавливать самые обычные подключения между access server и нодами и пересылать данные по ним, но тогда ноды не видят реального адреса клиента (если конечно ты не передашь его отдельным заголовком). Второй вариант — сделать чтобы access server эмулировал подключение от имени клиента к ноде.
Вот на этом месте у меня начало возникать подозрение что ты пытаешься навелосипедить nginx в ядре.
Ну в первую очередь мы все же л7-роутер, да.
Если я установлю обычное подключение с нодой — она будет слать пакеты мне. Я могу, конечно, сначала установить его, а потом каким-то образом сказать ей поменять destination у сокета на клиентский, но это плохая идея — лишний оверхед на установление подключения, которое уже какбэ установлено, как минимум.
Да, я хочу эмулировать подключение от имени клиента к ноде, с минимальным оверхедом. Собственно, затем весь этот изврат и был придуман.
destination у сокета ты не поменяешь без насилования сетевого стека ноды. Ну и да, кто тебе мешает сэмулировать несколько пакетиков от имени клиента, а потом пересылать пакеты от клиента ноде?
Олсо, чем таки nginx в качестве http-балансировщика не угодил?
Эмулировать пакеты никто не мешает, но.
Вот шлю я ноде SYN от имени клиента — она ему отвечает SYN+ACK. Он охуевает и ресетит подключение, потому что уже получил SYN+ACK от аксеса раньше. Следовательно, первая проблема — сказать ноде "не шли SYN+ACK". Ну или как-то перехватить его на пути к клиенту.
Дальше нода ждет от клиента ack для завершения хендшейка — окей, его тоже эмулируем. Уже имеем 2 (3) лишних пакета на каждое соединение.
Дальше мы пересылаем ноде гет-запрос от клиента. Поскольку мы его фиксили — размер изменился. И, когда она ответит клиенту ack, значение ack_seq в пакете не будет соответствовать оригинальному размеру. Следовательно, мы либо должны как-то сказать ноде "отвечай вот так", либо вообще отправить первый ack самостоятельно, сказав ноде молчать в ответ на этот пакет. Это вторая проблема.
А, ну еще seq в пакетах от ноды должен соответствовать тому, что мы отправили клиенту в первом пакете от аксеса при хендшейке — тобишь мы должны ей подсказать начальный номер последовательности.
В итоге пришли к тому, что и было в оп-посте )
Или все же есть менее костыльное решение для всего этого?
А про nginx — длинная история. Он не умеет часть того, что нам в итоге понадобится, ну и наша реализация будет быстрее в итоге )
без подставляния костылей в стек ноды можно извратиться только с чем-то-вроде-NAT-но-не-NAT, которое таки будет через себя пропускать и ответы от ноды к клиенту, меняя в них seq/ack. Если именно nginx не подходит, то 1) почему его нельзя допилить до нужной кондиции? 2) почему не подходит схема с обычными бекендами, общающимися с балансировщиком по обычному HTTP/TCP ? Stateful forwarding на фронтенде всё равно придётся городить, как не крути, а в такой позе насиловать придётся только код балансировщика, не трогая ноды.
В Шаолине уже открыли отделение Computer Science?
Причём тут cs?
Это прям как "мы навелосипедим свой бинарный XMPP и будет он в сто раз быстрее работать". И где они все?
По теме, если nginx не устраивает, то возьми HA Proxy и не парься. Проверенный софт гораздо лучше собственных велосипедов в большинстве случаев, а это как-раз такой.