Я бы хотел установить исходящий путь 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, который я могу попробовать, чтобы заставить его работать?
Или есть что-нибудь, что я могу попробовать при сборке ядра вариант?
Мы будем благодарны за любые советы.
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 и в блоге Олега Куткова.