TCP Принудительная маршрутизация через назначенный Интерфейс Ethernet не работает в ubuntu 18.04

Я бы хотел установить исходящий путь TCP через любой назначенный интерфейс Ethernet, даже если указанный интерфейс Ethernet не настроен как шлюз по умолчанию.

У моего устройства два интерфейса Ethernet ( eth0, eth1).

Я хотел бы принудительно установить TCP-соединение для eth0 (192.168.73.x), даже если шлюз по умолчанию установлен на eth1 (192.168.83.1) вместо eth0, как показано ниже в таблице маршрутизации.


Destination     Gateway               Genmask             Flags   Metric   Ref    Use   Iface
0.0.0.0         192.168.83.1          0.0.0.0             UG      0        0      0     eth1
192.168.73.0    0.0.0.0               255.255.255.0       U       0        0      0     eth0
192.168.83.0    0.0.0.0               255.255.255.0       U       0        0      0     eth1

Для этого я создал один сокет и прикрепил свой исходный IP-адрес (= eth0) с помощью bind api.

(Я также пробовал использовать SO_BINDTODEVICE, но это не сработало)

А затем я вызывается API-интерфейс подключения с IP-адресом назначения для подключения к общедоступному адресу внешнего назначения

Фрагмент кода, как показано ниже.

1>


sock = socket(AF_INET, SOCK_STREAM, 0);

2>


struct sockaddr_in  my_addr;

memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(0);
my_addr.sin_addr.s_addr = htonl(myIP); // eth0's IP address (192.168.73.238)

bind(sock, (struct ::sockaddr *)&my_addr, sizeof(my_addr) )

3>


struct sockaddr_in  dest_addr;
memset(&dest_addr, 0, sizeof(dest_addr));

dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = inet_addr(destIp);        // Destination IP 
dest_addr.sin_port = htons(destinationPort);             // Destination Port

connect(sock, (struct sockaddr *)(&dest_addr),sizeof(struct sockaddr_in))

Код ошибки, возвращаемый соединением: «Нет route to host »

Но это не удается на моем последнем устройстве с ubuntu linux 18.04 (ядро 4.15.18), тогда как та же логика работает на моем старом устройстве со старым ядром версия похожа на версию 3.10.

Как только я изменю шлюз по умолчанию с eth1 на eth0 и таблица маршрутизации станет такой, как показано ниже, я могу отправлять исходящие пакеты через eth0.


Destination    Gateway         Genmask          Flags  Metric Ref    Use  Iface
0.0.0.0        192.168.83.1    0.0.0.0          UG     0      0      0    eth1
192.168.73.0   0.0.0.0         255.255.255.0    U      0      0      0    eth0
192.168.83.0   0.0.0.0         255.255.255.0    U      0      0      0    eth1

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

Но это не удается в последней версии ubuntu 18.04 (ядро 4.15.18).

Есть ли какой-нибудь параметр sysctl, который я могу попробовать, чтобы заставить его работать?

Или есть что-нибудь, что я могу попробовать при сборке ядра вариант?

Мы будем благодарны за любые советы.

1
задан 18 November 2020 в 05:21

1 ответ

TL;DR: Вам нужно будет определить маршруты/шлюзы, необходимые для каждого желаемого пункта назначения, в зависимости от вашей топологии; возможно, ваша программа должна работать как root.

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

Например, если вы отправляете что-то на 192.168.73.25, применение известных масок 255.255.255.0 ваших интерфейсов приводит к сети 192.168.73.0 (присутствует в eth0), поэтому оно будет отправлено через eth0 (как в вашем первом настроенная таблица маршрутизации). Если ни один интерфейс не имеет нужной сети назначения, пакет отправляется на шлюз по умолчанию, который, как мы уже знаем, находится по адресу 192.168.83.1 и виден через eth1.

Все это можно контролировать с помощью таблиц маршрутизации, чтобы изменить процесс принятия решений в системе. Таким образом, если пункт назначения доступен через eth0, вам необходимо настроить новый маршрут, указывающий на это. Например, если вы хотите отправить трафик на 192.168.12.35 и знаете, что этот адрес доступен с маршрутизатора в сети, подключенной к eth1, скажем, по адресу 192.168.83.67, то вы добавляете новый маршрут следующим образом:

ip route add 192.168.12.65/32 via 192.168.83.67 dev eth1

Обратите внимание, что мы не можем просто сказать «через eth1», потому что IP-адрес назначения не находится в диапазоне сетевых адресов 192.168.83.1-254 eth1 (как указано в маске eth1), и отправка пакета через этот интерфейс не будет найдена автоматически. кто-то, кто знает, куда отправить его повторно (и, возможно, также не знает, как вернуть ответ). Конечно, чтобы сделать изменение постоянным между перезагрузками, мы должны настроить маршрут в конфигурации сетевого плана.

Если у вас есть топология с двумя провайдерами (с одним шлюзом в Интернет у каждого), но только один из них настроен как шлюз по умолчанию, вам необходимо разработать специальные таблицы маршрутизации, которые выбирают пакет по IP-адресу назначения, порту или какому-либо другое условие и отправьте его через нужный шлюз. Для этого я рекомендую прочитать Linux Advanced Routing and Traffic Control HowTo или, возможно, реализовать сценарий Net-ISP-Balance от Lincoln D. Stein, который решает подобные ситуации.

Прошу прощения за длинный текст или предположение, что вы еще не знакомы с механизмами маршрутизации.

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

EDIT: Читая о программировании маршрутизации на C, я нашел интересный код в Linux Journal и в блоге Олега Куткова.

0
ответ дан 18 November 2020 в 08:18

Другие вопросы по тегам:

Похожие вопросы: