Ping до 192.0.2.10 повідомляє лише те, що одна адреса відповіла на ехо ICMP, і більше нічого. У мережі без тунелю він може все одно пройти, бо адреса може бути в локальній підмережі; із здоровим тунелем він може не пройти, бо сервер відкидає ICMP, поки обслуговує HTTPS. Перевірка, потрібна вашому скрипту, — це запит стану платформи плюс тест маршруту до призначення, яке використовує застосунок. Розглядайте їх як два окремі поля, ніколи як одне логічне значення.

Що насправді доводить відповідь на ехо

ICMP — це протокол досяжності, який не знає ні інтерфейсів, ні тунелів. Коли надходить відповідь, ядро повідомляє, що хост відповів; воно не повідомляє, який маршрут ніс запит, а який — відповідь. Фільтрування робить сигнал ще шумнішим, бо брандмауер, що блокує ехо-запити, нічого не каже про порти за ним. А профіль із розділеним тунелем може навмисно виключити тестовану адресу, поки всі інші призначення працюють.

Насправді ви хочете знати, чи доступна конкретна служба на шляху, яким іде застосунок. Виберіть хост і порт, які викликає застосунок, а потім вимагайте відповідь на прикладному рівні.

Досяжність і стан тунелю — різні факти, і один може бути істинним, поки інший хибний. Якщо тримати їх окремо, зрозуміло, чия це несправність. Якщо злити їх в одну перевірку, вийде заявка, яку ніхто не зможе маршрутизувати.

Запитайте Android про транспорт, а не про ping

Android відповідає на питання про стан безпосередньо через ConnectivityManager. Можливості активної мережі несуть прапорець транспорту VPN, який встановлюється, коли трафік спрямовується через інтерфейс VPN. Виклик локальний, дешевий і не залежить від того, чи відповідає якийсь віддалений хост.

Ця версія Java повертає false замість винятку, коли активної мережі немає. Оскільки вона читає активну мережу, вона відображає профіль, який користувач вибрав у Налаштуваннях.

ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkCapabilities caps = cm.getNetworkCapabilities(cm.getActiveNetwork());
boolean tunnel = caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);

Для сповіщень про зміни зареєструйте NetworkCallback замість опитування за таймером. Не плутайте це з VpnService.prepare, який повідомляє лише, чи надав користувач дозвіл на VPN, а не чи течуть пакети. Відкликання надходить через VpnService.onRevoke, але забутий профіль у Налаштуваннях не породжує жодного broadcast.

iOS: назви інтерфейсів — евристика, а не контракт

Багато фрагментів коду для iOS перевіряють налаштування проксі, знаходять словник __SCOPED__ і шукають ключі інтерфейсів на кшталт tap, tun, utun2, ppp або ipsec. Цей підхід — домовленість про назви, а не задокументована гарантія, тому оновлення системи, яке перейменує інтерфейс, може його зламати. Він також схильний повідомляти true для застарілих записів, що переживають тунель.

Коли конфігурацією володіє ваш застосунок, NEVPNManager дає справжній стан. Він описує тунель, який запустив ваш застосунок; із VPN для кожного застосунку інші застосунки можуть бути повністю поза цим тунелем.

Перезавантажте налаштування перед читанням стану, бо значення змінюється, коли тунель піднімається й опускається:

let manager = NEVPNManager.shared()
manager.loadFromPreferences { error in
    let up = manager.connection.status == .connected
    print(up)
}

Прапорець стану платформи все одно не доводить, що трафік доходить до піра. Саме цю прогалину закриває тест маршруту, і тому одного логічного значення недостатньо для підтримки.

Профілі Windows надають справжнє поле стану

Windows відстежує профілі VPN окремо від адаптерів, а Get-VpnConnection надає ConnectionStatus для кожного відомого профілю. Тут з'являються профілі, створені через клієнт Windows або пакунок підготовки. Цей командлет не є універсальним детектором тунелів.

Прочитайте поле для названого профілю, додавши -AllUserConnection, коли профіль належить усім користувачам:

Get-VpnConnection -Name "Fabrikam" | Select-Object Name, ConnectionStatus, ServerAddress

Сторонні клієнти часто залишаються невидимими для цього командлета навіть коли їхній тунель працює, тому порожній результат означає невідомо, а не відключено. Списки адаптерів вводять в оману так само: адаптер постачальника може зберігатися, поки він неактивний, а деякі адаптери існують лише під час сеансу. Для профілю, яким володіє Windows, rasdial повідомляє код виходу, за яким можна розгалужуватися. Усе це описує локальне переконання, а не досяжність піра.

Стан плюс маршрут: перевірка з двох частин

Підключений профіль нічого не каже про те, куди йдуть пакети. Розділений тунель може тримати тунель піднятим, виключаючи потрібне вам призначення. Профілі always-on залишаються підключеними з мертвим сервером, а kill switch навмисно блокує трафік, коли тунель падає.

Перевірте обидва шари: підтвердьте, що транспорт VPN або профіль активний, потім підтвердьте, що призначення розв'язується й відповідає цим шляхом. У Linux запитайте ядро, який інтерфейс воно використало б:

ip route get 192.0.2.10
resolvectl query intranet.example.test

Коли клієнт поза вашим контролем, фіксуйте обидва результати й дозвольте закономірності вказати на шар. Підключено і невдалий тест маршруту вказує на розділений тунель, DNS або політику. Відключено і робочий тест маршруту означає, що трафік несе інший шлях.

Тест, який можна виконати по черзі

Спершу зафіксуйте стан платформи, перш ніж змінювати будь-який стан мережі. Розв'яжіть ім'я хоста й занотуйте родину адрес, бо запис AAAA може піти іншим шляхом, ніж запис A. Відкрийте порт служби й вимагайте відповіді застосунку, а не лише рукостискання. Тримайте кожне спостереження в окремому полі журналу.

На машині з Linux, dig і curl три команди охоплюють розв'язання, маршрут і саму службу. Запустіть їх із піднятим тунелем і знову з опущеним:

dig +short intranet.example.test
ip route get 198.51.100.20
curl -sS -o /dev/null -w "%{http_code}" --max-time 8 https://intranet.example.test/health

Чотири окремі поля кращі за одне логічне значення, бо стан, DNS, маршрут і політика відмовляють незалежно й потрапляють до різних команд. Ping у терміналі залишається доброю ручною перевіркою. Це погане визначення зв'язності для автоматизації, де API платформи й тест маршруту відповідають на справжнє питання.

Схожі статті