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

Почему клиент не может это предложить

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

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

Настоящая маршрутизация по приложениям существует там, где платформа добавила для этого механизм, например вызовы фильтров в Windows или привязка службы VPN в Android. Настольный экземпляр OpenVPN ничего из этого не поставляет, и никакой флаг конфигурации этого не выдумывает.

Linux: пометьте процесс, затем маршрутизируйте метку

Самый чистый подход привязывает решение к процессу, который владеет сокетом. Совпадение owner работает для локально созданного трафика в цепочке OUTPUT, поэтому метка, применённая там, может выбрать отдельную таблицу:

iptables -t mangle -A OUTPUT -m owner --uid-owner 1001 -j MARK --set-mark 0x1
ip rule add fwmark 0x1 table 200
ip route add 192.168.1.0/24 dev eth0 src 192.168.1.0 table 200
ip route add default via 192.168.1.1 dev eth0 table 200

Пакеты от пользователя 1001 затем обращаются к таблице 200 и выходят через обычный шлюз, а основная таблица по-прежнему отправляет всё остальное в туннель. Совпадение owner действует только на локально созданные пакеты, а это именно тот трафик, который здесь важен, и оно не может классифицировать пакеты, пересылаемые другому хосту. Проверяйте инструментами, а не намерением: ss -tnp показывает локальный адрес и процесс-владелец установленных соединений, а ip route get 1.1.1.1 mark 0x1 показывает, какую таблицу выбирает метка.

Сетевое пространство имён — более прочный ответ для самодостаточной программы, потому что каждый созданный внутри него сокет привязан к нему по построению, так что вспомогательные процессы и дочерние соединения наследуют поведение, вообще не классифицируясь. Цена — сантехника: пара veth, адрес на каждом конце и маршрут по умолчанию внутри пространства имён, который указывает обратно на хост.

ip netns add novpn
ip link add veth-host type veth peer name veth-ns
ip link set veth-ns netns novpn
ip addr add 10.200.0.1/24 dev veth-host
ip netns exec novpn ip addr add 10.200.0.2/24 dev veth-ns
ip netns exec novpn ip route add default via 10.200.0.1

Windows: назначение всё ещё решает

Windows выбирает исходящий интерфейс по назначению, поэтому программе, которой нужен другой путь, приходится менять то, что стек спрашивает до этого выбора. ForceBindIP закрывает первую половину: он запускает программу с сокетами, уже привязанными к указанному вами локальному адресу. Само по себе это не меняет интерфейс, через который уходит пакет, потому что маршрут по-прежнему выбирает назначение. Привяжите источник на ноутбуке, маршрут по умолчанию которого ведёт в туннель, и пакет всё равно попадёт в туннель.

Симптом имеет читаемую форму, как только вы с ним знакомы. Он проявляется для адреса LAN, до которого выбранный интерфейс достаёт напрямую, и не работает для всего в интернете, чей маршрут указывает на туннель. Он также выглядит работающим до тех пор, пока программа не передаст работу помощнику, потому что помощнику, открывающему собственный сокет, о привязке никогда не говорили.

Перенесите решение, когда один процесс — неправильная единица

Если настоящее требование — устройство, а не программа, поместите его на собственный SSID или VLAN и позвольте маршрутизатору решать: этот источник идёт обычным путём, а другие источники используют туннель. Выделенная машина-шлюз — другой честный вариант: она держит VPN и получает только те назначения, которые присылает ей маршрутизатор. В VLAN политика охватывает каждый поток, который открывает устройство, включая вспомогательные процессы, сломавшие подход Windows. Обмен — это охват: два программы на этом устройстве больше нельзя обрабатывать по-разному.

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

Похожие статьи