Подтвердить что ты не робот

Linux: как я могу использовать определенный сетевой интерфейс?

Это можно было бы считать продолжением этого более раннего SO вопроса.

В идеале, я хотел бы заключить в тюрьму процесс только с использованием определенного интерфейса, несмотря ни на что. Он будет создавать TCP-соединения, отправлять UDP-дейтаграммы и прослушивать широковещательные передачи UDP. В настоящее время я делаю следующее:

  • Определите IP-адрес используемого интерфейса.
  • Создайте правило политики IP для маршрутизации всех пакетов, поступающих из интерфейса, на этот IP
  • Создать другое правило политики IP для маршрутизации всех пакетов, поступающих с этого IP-адреса на этот интерфейс
  • Настройка таблицы маршрутизации по умолчанию для каждого правила

Теперь это работает, в основном, но клиентский процесс также должен быть готов к игре. То есть, он должен привязываться к конкретному IP-интерфейсу, который он хочет использовать, и я думаю, что мне нужно также установить SO_BINDTODEVICE. (Тем не менее, я продолжаю читать противоречивую информацию о том, работает ли SO_BINDTODEVICE при использовании TCP или UDP.) К счастью, клиентское приложение - это Python, и я могу расширить класс сокета, чтобы сделать все это прозрачно. Но я не уверен, что это полное решение, особенно в отношении приема трансляций.

Мои вопросы:

  • Делает ли SO_BINDTODEVICE то, что я хочу здесь? Или он эффективен только для сырых сокетов? Кто-то отметил, что "SO_BINDTODEVICE в сокете не гарантирует, что сокет будет принимать только пакеты, которые поступают на этот провод/антенну физического интерфейса". Если это действительно так, то что делает SO_BINDTODEVICE?

  • Есть ли способ сделать это так, чтобы локальный IP-адрес не был уникальным? Это не было бы проблемой, кроме того, что сервер DHCP на одном интерфейсе может выделять ему IP-адрес, который используется другим интерфейсом, что путает таблицу маршрутизации.

  • Как получать трансляции только с определенного интерфейса? Привязка к определенному IP-видимому, делает его игнорировать трансляции, что имеет смысл, но не совсем то, что я ищу.

Я работаю на Ubuntu 8.04 w/Linux kernel 2.6.26. Возможность одновременного доступа к одной и той же подсети в двух разных сетях через два разных интерфейса является неоспоримым требованием, что делает его (в основном) невосприимчивым к "не делайте этого".:)

4b9b3361

Ответ 1

После тяжелого уик-энда я рад представить решение, в котором рассматривается большинство из того, что я обсуждал ранее, с почти нулевыми проблемами.

Существует sysctl, называемый net.ipv4.conf.all.rp_filter, который может быть установлен на 0, чтобы отключить проверку источника:

    rp_filter - INTEGER
         2 - do source validation by reversed path, as specified in RFC1812
             Recommended option for single homed hosts and stub network
             routers. Could cause troubles for complicated (not loop free)
             networks running a slow unreliable protocol (sort of RIP),
             or using static routes.

         1 - (DEFAULT) Weaker form of RP filtering: drop all the packets
             that look as sourced at a directly connected interface, but
             were input from another interface.

         0 - No source validation.

Это также можно установить для каждого интерфейса, используя /proc/sys/net/ipv 4/conf/ <interface>/rp_filter.

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

Трансляции по-прежнему проблематичны по причинам, которые я не понимаю, но я, наконец, доволен этой проблемой, и я надеюсь, что это поможет другим.

Ответ 2

Что касается моего общего вопроса, для этого есть несколько способов сделать это:

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

  • Напишите пользовательскую модель ядра, которая полностью игнорирует таблицу маршрутизации, если установлен SO_BINDTODEVICE. Однако клиентский процесс по-прежнему требуется для вызова setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev). Этот вариант определенно не для слабонервных.

  • Виртуализировать процесс. Это, вероятно, не подходит для многих людей, и это принесет собственный набор головных болей, в основном с настройкой. Но стоит упомянуть.

Варианты 1 и 2 требуют, чтобы процессы для отказа выполнялись так, как нам бы хотелось. Это можно частично устранить, создав динамическую библиотеку, которая захватывает вызов socket() для создания сокета, а затем сразу же привязывает его к устройству перед возвратом дескриптора. Это описано более подробно здесь.

После нескольких исследований и большого количества Googling я могу сделать несколько выводов о том, как ведет себя ядро ​​Linux 2.6.26. Обратите внимание, что это, вероятно, все поведение, специфичное для реализации, и, возможно, даже специфичное для ядра. Проверьте свою собственную платформу, прежде чем принимать решение о внедрении функций, основанных на моей единственной точке данных.

  • SO_BINDTODEVICE действительно делает то, что он говорит, по крайней мере для UDP.

  • Уникальные IP-адреса для каждого интерфейса кажутся необходимыми, поскольку мы используем таблицы маршрутизации. Пользовательский модуль ядра может обойти это ограничение.

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

Я протестировал его, сначала создав сокет. Затем я привязал его к определенному интерфейсу с помощью setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev). Наконец, я связал его с широковещательным адресом. С другого компьютера я отправил широковещательную передачу, которая будет получена через несвязанный интерфейс. Соединитель, связанный с устройством, не получил эту трансляцию, что имеет смысл. Удалите вызов setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev), и широковещательная передача будет принята.

Следует также упомянуть, что здесь можно использовать setsockopt(SOL_SOCKET, SO_REUSEADDR, 1). Обратите внимание, что семантика SO_REUSEADDR изменяется с широковещательными адресами. В частности, законно иметь два сокета, связанных с широковещательным адресом и одним и тем же портом на одном компьютере, если оба они имеют SO_REUSEADDR.

UPDATE: SO_BINDTODEVICE с трансляциями, похоже, чревато опасностью, в частности, приемом широковещательных кадров. Я наблюдал за широковещательными кадрами, полученными на одном интерфейсе, но одновременно исчезал на другом. Похоже, что они выполняются локальной таблицей маршрутизации, но не защищены от правил политики IP. Тем не менее, я не уверен на 100% об этом, и я только упоминаю его как отправную точку, если вы хотите продолжить его. Все это сказать: используйте на свой страх и риск. В интересах времени я открыл сырой сокет на интерфейсе и сам разбирал заголовки Ethernet и IP.

Ответ 3

Не прямой ответ на ваш вопрос, а просто FYI. Как вы упомянули выше, это решение может быть слишком большим для того, что вам нужно/нужно делать.

Мне лично нравится идея создания модуля ядра ядра сетевого стека, который позволит мне это сделать. Таким образом, у меня есть полный контроль над многоадресными и модульными кадрами, идущими и выходящими из пользовательского пространства. Вам нужно будет использовать что-то вроде сокеты netlink для отправки/получения данных в приложение драйвера и приложения пользователя и из него, но он работает очень хорошо и очень быстро.

Вы также можете подключиться к любому уровню стека таким образом... Ethernet или IP. Таким образом, имея полный контроль над тем, что вы отправляете/получаете.

Вот пример article, в котором говорится о подключении к стеку netfilter.

Примечание. эта статья перехватывает IP-стек, а также старую. Я знаю, что API изменились, но многие из этих статей по-прежнему применяются практически и теоретически. Если вы хотите подключиться к слою моста, вы должны использовать аналогичный механизм, но укажите

BR_LOCAL_IN instead of NF_IP_LOCAL_IN

Примечание. Это очень похоже на открытие сырого сокета на интерфейсе. Вам придется самостоятельно создавать свои фреймы.

Ответ 4

вы могли бы попытаться ограничить пространство имен процессов в одном интерфейсе. Для выполнения задания вам понадобится сборка ядра с CONFIG_NETNS (большинство ядер современных дистрибутивов) и некоторые script. Пример конфигурации