Используя Linux, как указать, по какому интерфейсу ethernet передаются данные

StackOverflow https://stackoverflow.com/questions/172905

  •  05-07-2019
  •  | 
  •  

Вопрос

Я работаю над серверной системой на базе Linux, в которой есть два сетевых интерфейса, оба в одной подсети (пока давайте просто скажем, что они 172.17.32.10 & 172.17.32.11).Когда я отправляю данные на хост в сети, я хотел бы указать, по какому интерфейсу на моем сервере передаются данные.Мне нужно иметь возможность переключаться с одного интерфейса на другой (или, возможно, даже передавать данные на обоих) программно (статические правила маршрутизации не будут работать для этого приложения).

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

Это было полезно?

Решение

Не хочу вас обидеть, но ответ об использовании bind() совершенно неверен.bind() будет управлять исходным IP-адресом, помещенным в IP-заголовок пакета.Он не определяет, какой интерфейс будет использоваться для отправки пакета:мы обратимся к таблице маршрутизации ядра, чтобы определить, какой интерфейс имеет наименьшие затраты для достижения определенного пункта назначения.(* см. примечание)

Вместо этого вам следует использовать SO_BINDTODEVICE сокопт.Это делает две вещи:

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

Если у вас есть несколько интерфейсов, между которыми вы хотите переключаться, я бы предложил создать по одному сокету для каждого интерфейса.Поскольку вы также будете получать пакеты только на тот интерфейс, к которому вы привязаны, вам нужно будет добавить все эти сокеты в свой select()/poll()/ все, что вы используете.

#include <net/if.h>

struct ifreq ifr;

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, "eth1", sizeof(ifr.ifr_name));
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
            (void *)&ifr, sizeof(ifr)) < 0) {
    perror("SO_BINDTODEVICE failed");
}

(*примечание) Bind() обращение к IP-адресу интерфейса может привести к запутанному, но, тем не менее, правильному поведению.Например, если вы bind() на IP-адрес для eth1, но таблица маршрутизации отправляет пакет из eth0, тогда на проводе eth0 появится пакет, но содержащий исходный IP-адрес интерфейса eth1.Это странно, но разрешено, хотя пакеты, отправленные обратно на IP-адрес eth1, будут перенаправлены обратно на eth1.Вы можете протестировать это, используя систему Linux с двумя IP-интерфейсами.У меня есть один, и я его протестировал, и bind() неэффективен при передаче пакета через физический интерфейс.

Хотя технически это разрешено, в зависимости от топологии это, тем не менее, может не сработать.Чтобы ослабить распределенные атаки типа "отказ в обслуживании", при которых злоумышленники используют поддельные исходные IP-адреса, многие маршрутизаторы теперь выполняют проверки обратной переадресации путей (RPF).Пакеты с исходным IP-адресом по "неправильному" пути могут быть отброшены.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top