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

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

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

Ответы

Ответ 1

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

Вместо этого вы должны использовать sockopt 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-адресом на "неправильном" пути могут быть удалены.