Вопрос или проблема
Мне известны два основных способа отправки и получения сетевых пакетов с существующего сетевого интерфейса в Linux. Первый — это классический API сокетов, в котором ядро Linux отвечает за управление всей TCP/IP инкапсуляцией для данных, которые я отправляю и получаю. Второй способ — интерфейс AF_PACKET, в котором я читаю и записываю необработанные IP (или даже Ethernet) пакеты в интерфейс. Я отложу устройства TAP/TUN, пары veth, мосты и так далее, потому что в моем случае я не могу создавать новые сетевые интерфейсы, а только работать с существующими.
С подходом AF_PACKET, если я хочу как отправлять, так и получать данные и участвовать в «реальном» TCP соединении, кажется, мне нужно более или менее взять на себя управление всем интерфейсом, потому что если я отправлю, скажем, SYN пакет, и получу в ответ SYNACK пакет, то даже если я обработаю SYNACK пакет через декодирование пакетов на моем сокете AF_PACKET, ядро Linux также обработает SYNACK пакет, возможно, ответив каким-то образом или даже маршрутизируя его к существующему соединению на уровне API сокетов.
Что меня интересует: если я реализую собственный стек TCP, есть ли способ общаться через порты на моем хост-интерфейсе Linux, позволяя другим процессам по-прежнему использовать обычный API сокетов на том же хост-интерфейсе? Я хотел бы сказать ядру Linux: «Эй, пожалуйста, дай мне TCP порт, который ты не используешь, и с этого момента, пожалуйста, игнорируй пакеты на/с этого порта, если ты их получишь», а затем я использую свой сокет AF_PACKET для передачи TCP трафика, используя этот выделенный порт.
Конечно, я мог бы просто написать правило nftables, которое будет отклонять TCP трафик на определенный порт. Проблема в этом заключается в том, как мне найти порт, который не используется никаким другим процессом, и как надежно очистить правило, когда мой процесс завершится? Я выяснил, что правила nftables сложно настроить таким образом, чтобы они работали надежно на хостах, где уже есть сложные настройки nftables, и взаимодействовали с такими сервисами, как Docker, которые устанавливают свои собственные правила nftables.
Ответ или решение
На Linux вы можете реализовать собственный стек TCP, взаимодействуя с другими процессами на одном хосте, но это включает в себя несколько сложных деталей и ограничений. Давайте разберем, как вы можете этого добиться, исходя из вашего описания проблемы.
Использование AF_PACKET
Как вы правильно отметили, использование интерфейса AF_PACKET дает вам доступ к сырому уровню сети, что позволяет вам обрабатывать пакеты самостоятельно. Но это может вступить в конфликт с тем, как ядро Linux обрабатывает TCP-трафик. При непосредственной работе с AF_PACKET вам придется полностью контролировать TCP/IP стек, что действительно затрудняет взаимодействие с существующими процессами, использующими обычный API сокетов.
Изоляция вашего порта
Для того чтобы ваши приложения могли взаимодействовать через свой собственный TCP стек, при этом позволяя другим процессам продолжать работу с традиционным стеком, вам потребуется создать некоторую форму изоляции. Ваш подход с использованием правил nftables, действительно, имеет смысл, но он требует некоторой дополнительной работы.
-
Выбор порта: Вы можете написать функцию, которая сканирует диапазон портов (например, 1024-65535) и проверяет, занят ли порт, используя системные вызовы, такие как
getservbyport()
илиss
в Linux. Вот пример на C для проверки порта:#include <stdio.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> int is_port_in_use(int port) { char command[100]; snprintf(command, sizeof(command), "ss -tuln | grep ': %d '", port); return system(command) == 0; }
Цикл может выглядеть примерно так:
int port_to_use = 0; for (port_to_use = 1024; port_to_use <= 65535; port_to_use++) { if (!is_port_in_use(port_to_use)) { break; } }
-
Настройка правил nftables: После того, как вы выберете порт, вы действительно можете создать правило nftables, чтобы игнорировать пакеты для выбранного порта. Это может выглядеть примерно так:
nft add rule ip filter input tcp dport <ваш_порт> drop
-
Очистка правил: Для того чтобы удалять правила nftables при выходе вашего процесса, вы можете использовать обработчики сигналов. В вашем коде обработайте сигнал
SIGINT
илиSIGTERM
, чтобы выполнить команду для удаления правила:void cleanup() { system("nft delete rule ip filter input tcp dport <ваш_порт>"); } // Включите обработчик сигналов signal(SIGINT, cleanup); signal(SIGTERM, cleanup);
Альтернативный подход
Если настройка nftables выглядит слишком сложной или ненадежной для вашего конкретного случая, вы можете рассмотреть возможность использования сетевых пространств имен (network namespaces). Создание пространств имен позволит вам изолировать ваше приложение от других процессов, не мешая им. Однако, при этом вам понадобится разработать способ для взаимодействия вашего стека TCP с существующими процессами, что может значительно увеличить сложность.
Заключение
В итоге, взаимодействие с другими процессами на одном хосте при использовании собственного TCP стека возможно, однако требования к контролю за портами и правилам nftables требуют тщательного планирования и тестирования. В процессе реализации убедитесь, что все аспекты вашего решения протестированы на стабильность и производительность.