Как принудительно перенаправить пакет после возврата в netfilter из NFQUEUE

У меня есть кеширующее приложение, которое работает в пользовательском пространстве. Обычно он использует DPDK для взаимодействия с сетевыми адаптерами напрямую без взаимодействия с ядром, перехватывая пакеты из сети и либо пропуская их, генерируя ответ из кэша, либо, в некоторых случаях, блокируя их. Он прозрачен для конечных точек и написан для работы с необработанными пакетными данными прямо с сетевой карты. Примечательно, что здесь я не занимаюсь обычным программированием сокетов — все обрабатывается на уровне пакетов, без взаимодействия со стеком TCP/IP.

По долгим и скучным причинам я хотел добавить некоторые функции NAT. В качестве доказательства концепции я надеялся создать интерфейс для своего приложения с помощью iptables/netfilter. Итак, я сделал части NAT с помощью таких команд:

sudo iptables --table nat --append PREROUTING --in-interface seg1a  -j DNAT --to-destination $SERVERIP
sudo iptables --table nat --append POSTROUTING --out-interface seg1b -j MASQUERADE
sudo route add $SERVERIP seg1b

Это хорошо работает для моих целей. Теперь клиенты подключаются к интерфейсу в моей системе, но их трафик перенаправляется на сервер. Я уверен, что их трафик проходит через мою систему, так что все в порядке.

Но чтобы быть полезным в качестве кэша, мне нужно иметь возможность отвечать на некоторые запросы, которые я получаю от клиентов. Я думал, что смогу использовать для этого NFQUEUE с небольшим приложением, которое читает из очереди сетевого фильтра и передает пакеты в мое приложение и из него через IPC. Я использовал такое правило iptables:

sudo iptables --table mangle --append FORWARD -j NFQUEUE

Это работает нормально, пока мое приложение ни на что не отвечает. Но когда мой кеш пытается ответить на что-то с одной из конечных точек, все идет не так. Кэш переворачивает все заголовки L2-4, управляет порядковыми номерами и номерами подтверждения и т. д. Но пакеты не возвращаются клиенту.

Я думаю, что происходит то, что решения о маршрутизации пакета были приняты до того, как он был отправлен в NFQUEUE. Таким образом, даже если кеш возвращает что-то, чьи IP-адреса источника и получателя поменялись местами, это не имеет значения для маршрутизации пакета.

Я пробовал несколько вещей, чтобы изменить маршрутизацию моих ответных пакетов, но, похоже, ничего не работает. Как можно изменить маршрутизацию пакета, прочитанного из очередей сетевого фильтра? В противном случае есть ли хороший способ просто вводить пакеты в сеть из очередей сетевого фильтра? Если бы это было выполнимо, я мог бы заблокировать первоначальный запрос, а затем отправить ответ кеша как совершенно новый.

Спасибо заранее за любые предложения.


person SynAckRst    schedule 16.09.2020    source источник
comment
не могли бы вы понять I need to be able to respond to some requests that I get from clients and the packets don't get back to the client.. Клиент работает на вашем сервере, используя сетевую карту, отличную от dpdk? Требуется ли вам отвечать на запрос клиента, поступающий на сетевую карту, отличную от DPDK? Если вы знаете подпись клиентского пакета, на который вам нужно ответить, почему бы не использовать eBPF или XDP для перенаправления в ваше приложение кэширования DPDK, а затем внедрить его обратно в ядро ​​​​через интерфейс DPDK-TAP?   -  person Vipin Varghese    schedule 17.09.2020
comment
Клиенты находятся в другом месте в сети. Я перехватываю сетевой трафик и отвечаю на него. Я не использую какие-либо интерфейсы DPDK, потому что я хотел бы иметь возможность использовать существующие функции NAT iptables, а не писать свои собственные для этого быстрого доказательства концепции, поэтому на самом деле нет интерфейса DPDK-TAP для использования.   -  person SynAckRst    schedule 17.09.2020
comment
Таким образом, ваше текущее решение не использует интерфейс NIC DPDK. Непонятно, как приложение кэша DPDK пользовательского пространства получает пакеты, если не DPDK-TAP. Это ДПДК-КНИ? Второй вопрос: если реальное намерение состоит в том, чтобы отправить ответ на конкретный запрос, XDP eBPF был бы лучшим выбором, поскольку он может получить доступ к таблицам и FIB из ядра. Извините за продолжение, так как я не могу четко понять настройку.   -  person Vipin Varghese    schedule 17.09.2020
comment
пожалуйста, проигнорируйте мой предыдущий комментарий, я перечитал несколько раз, чтобы понять проблему. Ваша текущая настройка: NIC <==> iptables/netfilter <= application reads from NFQUEUE and forwards via IPC => Caching userspace application. Вопрос касается packets not getting forwarded after getting modified from caching application by reversing L2-4 headers, manages sequence and ack. numbers. Как уже говорилось, любой интерфейс tap из пользовательского пространства с правильным dst-ip должен маршрутизировать пакеты. Но вы подозреваете routing is not considered. Это правильно?   -  person Vipin Varghese    schedule 18.09.2020
comment
Если вы выполняете инъекцию обратно с помощью самого IPC, не могли бы вы попробовать внедрить измененный пакет через интерфейс TAP. Это должно позволить получить доступ к таблице маршрутизации FIB в Linux и отправить пакет на интерфейс, верно?   -  person Vipin Varghese    schedule 18.09.2020


Ответы (1)


Лучший ответ, который я придумал для этой проблемы, заключался в том, чтобы открыть необработанный сокет и передать ему мои предварительно созданные пакеты. Крайне важно, вы должны создать правило iptables, которое соответствует вашим сгенерированным пакетам и сообщает netfilter не отслеживать соединения. В противном случае netfilter видит внедренные пакеты и считает, что произошла коллизия соединений. Затем он выполняет переадресацию портов, поэтому пакеты выходят с портом, отличным от того, который вы указали!

Я сделал сокет следующим образом:

    sid = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);

Затем я включил пару опций (для краткости оставив обработку ошибок):

int one = 1;
const int *val = &one;
retval = setsockopt(sid, IPPROTO_IP, IP_HDRINCL, val, sizeof(one));    
retval = setsockopt(sid, SOL_SOCKET, SO_MARK, val, sizeof(one));

Первый из них сообщает ядру, что у меня уже есть заголовки, поэтому нет необходимости пытаться их создавать. Второй сообщает ядру ставить значение метки 1 для каждого пакета, отправленного через сокет, что я могу сопоставить с правилами маршрутизации или iptables.

Позже я создал адрес назначения и использовал sendto() для отправки пакета:

retval = sendto(sid, pL3, len, 0, (struct sockaddr *)&daddr, sizeof(daddr));

Наконец, я установил правило, запрещающее netfilter отслеживать соединения для TCP-пакетов, помеченных цифрой 1. Поскольку выше я добавил sockopt, чтобы все мои сгенерированные пакеты имели эту метку, это сработало хорошо:

 sudo iptables -t raw -I OUTPUT -p tcp -m mark --mark 1 -j CT --notrack
person SynAckRst    schedule 22.09.2020