Linux: Как заставить определенный сетевой интерфейс использоваться? - PullRequest
18 голосов
/ 19 февраля 2009

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

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

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

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

Мои вопросы:

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

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

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

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

Ответы [ 4 ]

4 голосов
/ 19 февраля 2009

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

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

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

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

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

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

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

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

  3. Чтобы получать широковещательные рассылки по определенному интерфейсу, сначала выполните привязку к устройству, используя 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.

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

3 голосов
/ 23 февраля 2009

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

Существует 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/ipv4/conf/<interface>/rp_filter.

.

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

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

2 голосов
/ 19 июля 2012

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

2 голосов
/ 20 февраля 2009

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

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

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

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

BR_LOCAL_IN instead of NF_IP_LOCAL_IN

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...