Comment vérifier l'état d'un VPN sans ping sous Android, iOS et Windows
Un ping prouve seulement qu'un hôte a répondu, pas que le tunnel est actif. Interrogez les API d'état d'Android, d'iOS et de Windows, puis vérifiez la route dont votre application a réellement besoin.
Pinger 192.0.2.10 vous dit qu'une adresse a répondu à un écho ICMP, et rien de plus. Sur un réseau sans tunnel, cela peut réussir quand même, car l'adresse peut se trouver sur le sous-réseau local ; avec un tunnel sain, cela peut échouer, car le serveur abandonne ICMP pendant qu'il sert HTTPS. Le contrôle dont votre script a besoin, c'est une requête d'état de la plateforme plus un test de route vers la destination que l'application utilise. Traitez-les comme deux champs distincts, jamais comme un seul booléen.
Ce qu'une réponse d'écho prouve réellement
ICMP est un protocole d'accessibilité qui ignore tout des interfaces et des tunnels. Quand une réponse arrive, le noyau signale qu'un hôte a répondu ; il ne signale pas quelle route a porté la requête ni celle qui a porté la réponse. Le filtrage rend le signal encore plus bruyant, car un pare-feu qui bloque les requêtes d'écho ne dit rien des ports derrière lui. Et un profil à tunnel divisé peut exclure volontairement l'adresse testée alors que toutes les autres destinations fonctionnent.
Ce que vous voulez vraiment savoir, c'est si un service précis est utilisable sur le chemin que prend l'application. Choisissez l'hôte et le port que l'application appelle, puis exigez une réponse au niveau applicatif.
L'accessibilité et l'état du tunnel sont deux faits différents, et l'un peut être vrai tandis que l'autre est faux. Les garder séparés vous dit à qui appartient la panne. Les fusionner en un seul contrôle produit un ticket que personne ne peut router.
Demandez à Android le transport, pas un ping
Android répond directement à la question d'état via ConnectivityManager. Les capacités du réseau actif portent un indicateur de transport VPN, défini lorsque le trafic est routé par une interface VPN. L'appel est local, peu coûteux et indépendant du fait qu'un hôte distant réponde ou non.
Cette version Java renvoie false au lieu de lever une exception quand aucun réseau actif n'existe. Comme elle lit le réseau actif, elle reflète le profil que l'utilisateur a choisi dans les Paramètres.
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkCapabilities caps = cm.getNetworkCapabilities(cm.getActiveNetwork());
boolean tunnel = caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
Pour les notifications de changement, enregistrez un NetworkCallback au lieu d'interroger avec un minuteur. Ne confondez pas cela avec VpnService.prepare, qui indique seulement si l'utilisateur a accordé une autorisation VPN, pas si des paquets circulent. La révocation arrive via VpnService.onRevoke, mais oublier un profil dans les Paramètres ne produit aucune diffusion.
iOS : les noms d'interface sont une heuristique, pas un contrat
Beaucoup d'extraits iOS inspectent les réglages de proxy, trouvent le dictionnaire __SCOPED__ et cherchent des clés d'interface comme tap, tun, utun2, ppp ou ipsec. Cette approche est une convention de nommage, pas une garantie documentée, donc une mise à jour du système qui renomme une interface peut la casser. Elle a aussi tendance à signaler vrai pour des entrées obsolètes qui survivent au tunnel.
Quand votre application possède la configuration, NEVPNManager donne un véritable état. Il décrit le tunnel que votre application a démarré ; avec un VPN par application, d'autres applications peuvent rester entièrement hors de ce tunnel.
Rechargez les préférences avant de lire l'état, car la valeur change à mesure que le tunnel monte et descend :
let manager = NEVPNManager.shared()
manager.loadFromPreferences { error in
let up = manager.connection.status == .connected
print(up)
}
Un indicateur d'état de la plateforme ne prouve toujours pas que le trafic atteint le pair. C'est précisément ce manque que couvre le test de route, et c'est pourquoi un seul booléen ne suffit pas pour le support.
Les profils Windows exposent un vrai champ d'état
Windows suit les profils VPN séparément des adaptateurs, et Get-VpnConnection expose un ConnectionStatus pour chaque profil qu'il connaît. Les profils créés via le client Windows ou un paquet de provisionnement apparaissent ici. L'applet de commande n'est pas un détecteur général de tunnels.
Lisez le champ pour un profil nommé, en ajoutant -AllUserConnection quand le profil appartient à tous les utilisateurs :
Get-VpnConnection -Name "Fabrikam" | Select-Object Name, ConnectionStatus, ServerAddress
Les clients tiers restent souvent invisibles pour cette applet même lorsque leur tunnel est actif, donc un résultat vide signifie inconnu, pas déconnecté. Les listes d'adaptateurs trompent autant : un adaptateur de fabricant peut persister alors qu'il est inactif, et certains adaptateurs n'existent que pendant une session. Pour un profil que Windows possède, rasdial rapporte un code de sortie sur lequel vous pouvez brancher. Tout cela décrit une croyance locale, pas l'accessibilité du pair.
État plus route : le contrôle en deux parties
Un profil connecté ne dit rien de l'endroit où vont les paquets. Le tunnel divisé peut maintenir le tunnel actif tout en excluant la destination qui vous intéresse. Les profils always-on restent connectés avec un serveur mort, et un kill switch bloque le trafic exprès quand le tunnel tombe.
Testez les deux couches : confirmez qu'un transport VPN ou un profil est actif, puis que la destination se résout et répond par ce chemin. Sous Linux, demandez au noyau quelle interface il utiliserait :
ip route get 192.0.2.10
resolvectl query intranet.example.test
Quand le client échappe à votre contrôle, journalisez les deux résultats et laissez le motif désigner la couche. Connecté plus un test de route en échec suggère un tunnel divisé, le DNS ou une politique. Déconnecté plus un test de route réussi signifie qu'un autre chemin porte le trafic.
Un test que vous pouvez exécuter dans l'ordre
Enregistrez d'abord l'état de la plateforme, avant de changer quoi que ce soit au réseau. Résolvez le nom d'hôte et notez la famille d'adresses, car un enregistrement AAAA peut suivre un autre chemin qu'un enregistrement A. Ouvrez le port du service et exigez une réponse applicative plutôt qu'une simple poignée de main. Gardez chaque observation dans son propre champ de journal.
Sur une machine Linux dotée de dig et curl, trois commandes couvrent la résolution, la route et le service lui-même. Exécutez-les avec le tunnel actif puis de nouveau lorsqu'il est coupé :
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
Quatre champs distincts valent mieux qu'un booléen, car l'état, le DNS, la route et la politique échouent indépendamment et atterrissent dans des équipes différentes. Un ping dans un terminal reste un bon contrôle manuel. C'est une mauvaise définition de la connectivité pour l'automatisation, où l'API de la plateforme et un test de route répondent à la vraie question.