Эмулировать "tcp", используя bind / connect на сокетах udp с SO_REUSEPORT? - PullRequest
0 голосов
/ 20 июня 2019

Я пытаюсь написать модуль связи на C, который может обрабатывать подключенные или неподключенные сокеты полностью прозрачно.

будучи ленивым, я подумал, что смогу получить bind() / connect() сокетов udp, чтобы получить одинсокет на клиента "с использованием udp и примитивов send() / recv().

схема проста, у меня есть" сокет сервера ", связанный с *: PORT с SO_REUSEPORT, на котором я recvfrom(),оттуда я создаю новый сокет с опцией сокета SO_REUSEPORT и использую информацию параметра from из bind() в *: PORT и подключаюсь к моему новому клиенту.

Я поддерживаюэтот список клиентов (он же сокеты) и может отправить () к ним без проблем.recv (), это другое дело ... Я мог бы предположить, что "разветвление" udp будет учитывать подключенный конечный бит сокета, чтобы найти, в какой сокет распространять пакет.в большинстве случаев это работает, и «правильный» сокет получает данные.но время от времени, неподключенный «серверный» сокет получает данные.

Я вижу в коде ядра:

(на момент публикации не удалось найти работающий lxr, извините за ссылки, не доступные для просмотра).

Я немного растерялся из-за этого reciprocal_scale()когда нет программы BPF ...

первый вопрос: я в заблуждение, когда говорю, что reuseport_select_sock() выбирает сокет на основе хэша 4-х кортежей (локальный адрес / порт + внешний адрес / порт)передается в качестве параметра, но полностью игнорирует 4 хеша подключенных сокетов?кажется, что идея последовательно выбирать сокет на основе его 4-х кортежей, не обязательно совпадающих с подключенной конечной точкой?

второй вопрос: я пытаюсь обернуться вокруг написания программы eBPF, чтобы «исправить»такое поведение (потому что это сделало бы мой код пользователя намного проще).Кажется, есть две вспомогательные функции, которые могут приблизить меня к тому, чего я хочу достичь.

  • int bpf_sk_select_reuseport()
  • struct bpf_sock *bpf_sk_lookup_udp()

bpf_sk_lookup_udp() отлично работает для того, что я хочу сделать, он находит подходящий сокет на основе 4-х кортежей в списке носителей для повторного порта.Проблема в том, что bpf_sk_select_reuseport() хочет, чтобы индекс в массиве reuse->socks[] обновлял поле selected_sk ... проблема здесь в том, что поиск вернет мне указатель на bpf_sock ... которого у меня нетИдея, как извлечь индекс массива в массиве reuse->socks[].

Извините, если он был слишком длинным.даже если я не могу заставить вещи работать так, как я хочу, надеюсь, это может быть информативным для других.

edit Итак, после нескольких дней игры с eBPF я пришел косознание того, что во многом то, что я хотел сделать, - это очень много.

для объявления BPF_MAP_TYPE_REUSEPORT_ARRAY требуется CAP_SYS_ADMIN, что как бы убивает идею ...

даже с CAP_SYS_ADMIN, этоневозможно искать элементы в REUSEPORT_ARRAY ...

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

решение с использованием eBPF состояло бы в том, чтобы использовать хеш-таблицу, связывая 5-тиклет пакета, с индексом которого он хранится в REUSEPORT_ARRAY.

, но так как у нас нет способа итерировать REUSEPORT_ARRAY, нам придется полагаться на «угадывание» этого индекса путем повторной реализации алгоритма, используемого в ядре. Сокеты

хранятся в порядкесоздания в REUSEPORT_ARRAY, пронумерованные от 0 до N-1.

когда сокет A закрыт, сокет N (последний сокет в массиве) занимает свое место в массиве, а массив сокращается на один элемент.

программе eBPF придется только искать хеш-таблицу и возвращать связанный индекс ... но это кажется слишком хрупким, поскольку это зависит от того, что ядро ​​всегда будет придерживаться одного и того же алгоритма упорядочения ... что маловероятно.1072 *

...